зеркало из https://github.com/mozilla/gecko-dev.git
224 строки
5.7 KiB
JavaScript
224 строки
5.7 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
);
|
|
|
|
const { Log } = ChromeUtils.import("chrome://marionette/content/log.js");
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
|
|
|
|
this.EXPORTED_SYMBOLS = ["modal"];
|
|
|
|
const COMMON_DIALOG = "chrome://global/content/commonDialog.xhtml";
|
|
|
|
const isFirefox = () =>
|
|
Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
|
|
|
|
/** @namespace */
|
|
this.modal = {
|
|
ACTION_CLOSED: "closed",
|
|
ACTION_OPENED: "opened",
|
|
};
|
|
|
|
/**
|
|
* Check for already existing modal or tab modal dialogs
|
|
*
|
|
* @param {browser.Context} context
|
|
* Reference to the browser context to check for existent dialogs.
|
|
*
|
|
* @return {modal.Dialog}
|
|
* Returns instance of the Dialog class, or `null` if no modal dialog
|
|
* is present.
|
|
*/
|
|
modal.findModalDialogs = function(context) {
|
|
// First check if there is a modal dialog already present for the
|
|
// current browser window.
|
|
for (let win of Services.wm.getEnumerator(null)) {
|
|
// TODO: Use BrowserWindowTracker.getTopWindow for modal dialogs without
|
|
// an opener.
|
|
if (
|
|
win.document.documentURI === COMMON_DIALOG &&
|
|
win.opener &&
|
|
win.opener === context.window
|
|
) {
|
|
return new modal.Dialog(() => context, Cu.getWeakReference(win));
|
|
}
|
|
}
|
|
|
|
// If no modal dialog has been found, also check if there is an open
|
|
// tab modal dialog present for the current tab.
|
|
// TODO: Find an adequate implementation for Fennec.
|
|
if (context.tab && context.tabBrowser.getTabModalPromptBox) {
|
|
let contentBrowser = context.contentBrowser;
|
|
let promptManager = context.tabBrowser.getTabModalPromptBox(contentBrowser);
|
|
let prompts = promptManager.listPrompts();
|
|
|
|
if (prompts.length) {
|
|
return new modal.Dialog(() => context, null);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Observer for modal and tab modal dialogs.
|
|
*
|
|
* @return {modal.DialogObserver}
|
|
* Returns instance of the DialogObserver class.
|
|
*/
|
|
modal.DialogObserver = class {
|
|
constructor() {
|
|
this.callbacks = new Set();
|
|
this.register();
|
|
}
|
|
|
|
register() {
|
|
Services.obs.addObserver(this, "common-dialog-loaded");
|
|
Services.obs.addObserver(this, "tabmodal-dialog-loaded");
|
|
Services.obs.addObserver(this, "toplevel-window-ready");
|
|
|
|
// Register event listener for all already open windows
|
|
for (let win of Services.wm.getEnumerator(null)) {
|
|
win.addEventListener("DOMModalDialogClosed", this);
|
|
}
|
|
}
|
|
|
|
unregister() {
|
|
Services.obs.removeObserver(this, "common-dialog-loaded");
|
|
Services.obs.removeObserver(this, "tabmodal-dialog-loaded");
|
|
Services.obs.removeObserver(this, "toplevel-window-ready");
|
|
|
|
// Unregister event listener for all open windows
|
|
for (let win of Services.wm.getEnumerator(null)) {
|
|
win.removeEventListener("DOMModalDialogClosed", this);
|
|
}
|
|
}
|
|
|
|
cleanup() {
|
|
this.callbacks.clear();
|
|
this.unregister();
|
|
}
|
|
|
|
handleEvent(event) {
|
|
logger.trace(`Received event ${event.type}`);
|
|
|
|
let chromeWin = event.target.opener
|
|
? event.target.opener.ownerGlobal
|
|
: event.target.ownerGlobal;
|
|
|
|
let targetRef = Cu.getWeakReference(event.target);
|
|
|
|
this.callbacks.forEach(callback => {
|
|
callback(modal.ACTION_CLOSED, targetRef, chromeWin);
|
|
});
|
|
}
|
|
|
|
observe(subject, topic) {
|
|
logger.trace(`Received observer notification ${topic}`);
|
|
|
|
switch (topic) {
|
|
case "common-dialog-loaded":
|
|
case "tabmodal-dialog-loaded":
|
|
let chromeWin = subject.opener
|
|
? subject.opener.ownerGlobal
|
|
: subject.ownerGlobal;
|
|
|
|
// Always keep a weak reference to the current dialog
|
|
let targetRef = Cu.getWeakReference(subject);
|
|
|
|
this.callbacks.forEach(callback => {
|
|
callback(modal.ACTION_OPENED, targetRef, chromeWin);
|
|
});
|
|
break;
|
|
|
|
case "toplevel-window-ready":
|
|
subject.addEventListener("DOMModalDialogClosed", this);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add dialog handler by function reference.
|
|
*
|
|
* @param {function} callback
|
|
* The handler to be added.
|
|
*/
|
|
add(callback) {
|
|
if (this.callbacks.has(callback)) {
|
|
return;
|
|
}
|
|
this.callbacks.add(callback);
|
|
}
|
|
|
|
/**
|
|
* Remove dialog handler by function reference.
|
|
*
|
|
* @param {function} callback
|
|
* The handler to be removed.
|
|
*/
|
|
remove(callback) {
|
|
if (!this.callbacks.has(callback)) {
|
|
return;
|
|
}
|
|
this.callbacks.delete(callback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Represents a modal dialog.
|
|
*
|
|
* @param {function(): browser.Context} curBrowserFn
|
|
* Function that returns the current |browser.Context|.
|
|
* @param {nsIWeakReference=} winRef
|
|
* A weak reference to the current |ChromeWindow|.
|
|
*/
|
|
modal.Dialog = class {
|
|
constructor(curBrowserFn, winRef = undefined) {
|
|
this.curBrowserFn_ = curBrowserFn;
|
|
this.win_ = winRef;
|
|
}
|
|
|
|
get curBrowser_() {
|
|
return this.curBrowserFn_();
|
|
}
|
|
|
|
/**
|
|
* Returns the ChromeWindow associated with an open dialog window if
|
|
* it is currently attached to the DOM.
|
|
*/
|
|
get window() {
|
|
if (this.win_) {
|
|
let win = this.win_.get();
|
|
if (win && win.parent) {
|
|
return win;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
get tabModal() {
|
|
let win = this.window;
|
|
if (win) {
|
|
return win.Dialog;
|
|
}
|
|
return this.curBrowser_.getTabModal();
|
|
}
|
|
|
|
get args() {
|
|
let tm = this.tabModal;
|
|
return tm ? tm.args : null;
|
|
}
|
|
|
|
get ui() {
|
|
let tm = this.tabModal;
|
|
return tm ? tm.ui : null;
|
|
}
|
|
};
|