Bug 1613863 [wpt PR 21653] - [css-pseudo] Avoid empty line in legacy list-item when...
[gecko.git] / testing / marionette / modal.js
blob66792d27c02ad141e599b8ef0e15342667366c63
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 "use strict";
7 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
8 const { XPCOMUtils } = ChromeUtils.import(
9   "resource://gre/modules/XPCOMUtils.jsm"
12 const { Log } = ChromeUtils.import("chrome://marionette/content/log.js");
14 XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
16 this.EXPORTED_SYMBOLS = ["modal"];
18 const COMMON_DIALOG = "chrome://global/content/commonDialog.xhtml";
20 const isFirefox = () =>
21   Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
23 /** @namespace */
24 this.modal = {
25   ACTION_CLOSED: "closed",
26   ACTION_OPENED: "opened",
29 /**
30  * Check for already existing modal or tab modal dialogs
31  *
32  * @param {browser.Context} context
33  *     Reference to the browser context to check for existent dialogs.
34  *
35  * @return {modal.Dialog}
36  *     Returns instance of the Dialog class, or `null` if no modal dialog
37  *     is present.
38  */
39 modal.findModalDialogs = function(context) {
40   // First check if there is a modal dialog already present for the
41   // current browser window.
42   for (let win of Services.wm.getEnumerator(null)) {
43     // TODO: Use BrowserWindowTracker.getTopWindow for modal dialogs without
44     // an opener.
45     if (
46       win.document.documentURI === COMMON_DIALOG &&
47       win.opener &&
48       win.opener === context.window
49     ) {
50       return new modal.Dialog(() => context, Cu.getWeakReference(win));
51     }
52   }
54   // If no modal dialog has been found, also check if there is an open
55   // tab modal dialog present for the current tab.
56   // TODO: Find an adequate implementation for Fennec.
57   if (context.tab && context.tabBrowser.getTabModalPromptBox) {
58     let contentBrowser = context.contentBrowser;
59     let promptManager = context.tabBrowser.getTabModalPromptBox(contentBrowser);
60     let prompts = promptManager.listPrompts();
62     if (prompts.length) {
63       return new modal.Dialog(() => context, null);
64     }
65   }
67   return null;
70 /**
71  * Observer for modal and tab modal dialogs.
72  *
73  * @return {modal.DialogObserver}
74  *     Returns instance of the DialogObserver class.
75  */
76 modal.DialogObserver = class {
77   constructor() {
78     this.callbacks = new Set();
79     this.register();
80   }
82   register() {
83     Services.obs.addObserver(this, "common-dialog-loaded");
84     Services.obs.addObserver(this, "tabmodal-dialog-loaded");
85     Services.obs.addObserver(this, "toplevel-window-ready");
87     // Register event listener for all already open windows
88     for (let win of Services.wm.getEnumerator(null)) {
89       win.addEventListener("DOMModalDialogClosed", this);
90     }
91   }
93   unregister() {
94     Services.obs.removeObserver(this, "common-dialog-loaded");
95     Services.obs.removeObserver(this, "tabmodal-dialog-loaded");
96     Services.obs.removeObserver(this, "toplevel-window-ready");
98     // Unregister event listener for all open windows
99     for (let win of Services.wm.getEnumerator(null)) {
100       win.removeEventListener("DOMModalDialogClosed", this);
101     }
102   }
104   cleanup() {
105     this.callbacks.clear();
106     this.unregister();
107   }
109   handleEvent(event) {
110     logger.trace(`Received event ${event.type}`);
112     let chromeWin = event.target.opener
113       ? event.target.opener.ownerGlobal
114       : event.target.ownerGlobal;
116     let targetRef = Cu.getWeakReference(event.target);
118     this.callbacks.forEach(callback => {
119       callback(modal.ACTION_CLOSED, targetRef, chromeWin);
120     });
121   }
123   observe(subject, topic) {
124     logger.trace(`Received observer notification ${topic}`);
126     switch (topic) {
127       case "common-dialog-loaded":
128       case "tabmodal-dialog-loaded":
129         let chromeWin = subject.opener
130           ? subject.opener.ownerGlobal
131           : subject.ownerGlobal;
133         // Always keep a weak reference to the current dialog
134         let targetRef = Cu.getWeakReference(subject);
136         this.callbacks.forEach(callback => {
137           callback(modal.ACTION_OPENED, targetRef, chromeWin);
138         });
139         break;
141       case "toplevel-window-ready":
142         subject.addEventListener("DOMModalDialogClosed", this);
143         break;
144     }
145   }
147   /**
148    * Add dialog handler by function reference.
149    *
150    * @param {function} callback
151    *     The handler to be added.
152    */
153   add(callback) {
154     if (this.callbacks.has(callback)) {
155       return;
156     }
157     this.callbacks.add(callback);
158   }
160   /**
161    * Remove dialog handler by function reference.
162    *
163    * @param {function} callback
164    *     The handler to be removed.
165    */
166   remove(callback) {
167     if (!this.callbacks.has(callback)) {
168       return;
169     }
170     this.callbacks.delete(callback);
171   }
175  * Represents a modal dialog.
177  * @param {function(): browser.Context} curBrowserFn
178  *     Function that returns the current |browser.Context|.
179  * @param {nsIWeakReference=} winRef
180  *     A weak reference to the current |ChromeWindow|.
181  */
182 modal.Dialog = class {
183   constructor(curBrowserFn, winRef = undefined) {
184     this.curBrowserFn_ = curBrowserFn;
185     this.win_ = winRef;
186   }
188   get curBrowser_() {
189     return this.curBrowserFn_();
190   }
192   /**
193    * Returns the ChromeWindow associated with an open dialog window if
194    * it is currently attached to the DOM.
195    */
196   get window() {
197     if (this.win_) {
198       let win = this.win_.get();
199       if (win && win.parent) {
200         return win;
201       }
202     }
203     return null;
204   }
206   get tabModal() {
207     let win = this.window;
208     if (win) {
209       return win.Dialog;
210     }
211     return this.curBrowser_.getTabModal();
212   }
214   get args() {
215     let tm = this.tabModal;
216     return tm ? tm.args : null;
217   }
219   get ui() {
220     let tm = this.tabModal;
221     return tm ? tm.ui : null;
222   }