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/. */
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}";
25 ACTION_CLOSED: "closed",
26 ACTION_OPENED: "opened",
30 * Check for already existing modal or tab modal dialogs
32 * @param {browser.Context} context
33 * Reference to the browser context to check for existent dialogs.
35 * @return {modal.Dialog}
36 * Returns instance of the Dialog class, or `null` if no modal dialog
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
46 win.document.documentURI === COMMON_DIALOG &&
48 win.opener === context.window
50 return new modal.Dialog(() => context, Cu.getWeakReference(win));
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();
63 return new modal.Dialog(() => context, null);
71 * Observer for modal and tab modal dialogs.
73 * @return {modal.DialogObserver}
74 * Returns instance of the DialogObserver class.
76 modal.DialogObserver = class {
78 this.callbacks = new Set();
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);
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);
105 this.callbacks.clear();
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);
123 observe(subject, topic) {
124 logger.trace(`Received observer notification ${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);
141 case "toplevel-window-ready":
142 subject.addEventListener("DOMModalDialogClosed", this);
148 * Add dialog handler by function reference.
150 * @param {function} callback
151 * The handler to be added.
154 if (this.callbacks.has(callback)) {
157 this.callbacks.add(callback);
161 * Remove dialog handler by function reference.
163 * @param {function} callback
164 * The handler to be removed.
167 if (!this.callbacks.has(callback)) {
170 this.callbacks.delete(callback);
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|.
182 modal.Dialog = class {
183 constructor(curBrowserFn, winRef = undefined) {
184 this.curBrowserFn_ = curBrowserFn;
189 return this.curBrowserFn_();
193 * Returns the ChromeWindow associated with an open dialog window if
194 * it is currently attached to the DOM.
198 let win = this.win_.get();
199 if (win && win.parent) {
207 let win = this.window;
211 return this.curBrowser_.getTabModal();
215 let tm = this.tabModal;
216 return tm ? tm.args : null;
220 let tm = this.tabModal;
221 return tm ? tm.ui : null;