diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /dom/browser-element | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-adbda400be353e676059e335c3c0aaf99e719475.tar.xz firefox-adbda400be353e676059e335c3c0aaf99e719475.zip |
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/browser-element')
-rw-r--r-- | dom/browser-element/BrowserElementChild.js | 42 | ||||
-rw-r--r-- | dom/browser-element/BrowserElementChildPreload.js | 290 | ||||
-rw-r--r-- | dom/browser-element/BrowserElementParent.jsm | 276 | ||||
-rw-r--r-- | dom/browser-element/BrowserElementPromptService.jsm | 720 | ||||
-rw-r--r-- | dom/browser-element/components.conf | 14 | ||||
-rw-r--r-- | dom/browser-element/moz.build | 37 | ||||
-rw-r--r-- | dom/browser-element/nsIBrowserElementAPI.idl | 44 |
7 files changed, 0 insertions, 1423 deletions
diff --git a/dom/browser-element/BrowserElementChild.js b/dom/browser-element/BrowserElementChild.js deleted file mode 100644 index 762957bcd4..0000000000 --- a/dom/browser-element/BrowserElementChild.js +++ /dev/null @@ -1,42 +0,0 @@ -/* 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/. */ - -/* eslint-env mozilla/frame-script */ -/* global api, CopyPasteAssistent */ - -"use strict"; - -function debug(msg) { - // dump("BrowserElementChild - " + msg + "\n"); -} - -var BrowserElementIsReady; - -debug(`Might load BE scripts: BEIR: ${BrowserElementIsReady}`); -if (!BrowserElementIsReady) { - debug("Loading BE scripts"); - if (!("BrowserElementIsPreloaded" in this)) { - Services.scriptloader.loadSubScript( - "chrome://global/content/BrowserElementChildPreload.js", - this - ); - } - - function onDestroy() { - removeMessageListener("browser-element-api:destroy", onDestroy); - - if (api) { - api.destroy(); - } - - BrowserElementIsReady = false; - } - addMessageListener("browser-element-api:destroy", onDestroy); - - BrowserElementIsReady = true; -} else { - debug("BE already loaded, abort"); -} - -sendAsyncMessage("browser-element-api:call", { msg_name: "hello" }); diff --git a/dom/browser-element/BrowserElementChildPreload.js b/dom/browser-element/BrowserElementChildPreload.js deleted file mode 100644 index 1bbcf9ff05..0000000000 --- a/dom/browser-element/BrowserElementChildPreload.js +++ /dev/null @@ -1,290 +0,0 @@ -/* 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"; - -/* eslint-env mozilla/frame-script */ - -function debug(msg) { - // dump("BrowserElementChildPreload - " + msg + "\n"); -} - -debug("loaded"); - -var BrowserElementIsReady; - -var { BrowserElementPromptService } = ChromeUtils.import( - "resource://gre/modules/BrowserElementPromptService.jsm" -); - -function sendAsyncMsg(msg, data) { - // Ensure that we don't send any messages before BrowserElementChild.js - // finishes loading. - if (!BrowserElementIsReady) { - return; - } - - if (!data) { - data = {}; - } - - data.msg_name = msg; - sendAsyncMessage("browser-element-api:call", data); -} - -var LISTENED_EVENTS = [ - // This listens to unload events from our message manager, but /not/ from - // the |content| window. That's because the window's unload event doesn't - // bubble, and we're not using a capturing listener. If we'd used - // useCapture == true, we /would/ hear unload events from the window, which - // is not what we want! - { type: "unload", useCapture: false, wantsUntrusted: false }, -]; - -/** - * The BrowserElementChild implements one half of <iframe mozbrowser>. - * (The other half is, unsurprisingly, BrowserElementParent.) - * - * This script is injected into an <iframe mozbrowser> via - * nsIMessageManager::LoadFrameScript(). - * - * Our job here is to listen for events within this frame and bubble them up to - * the parent process. - */ - -var global = this; - -function BrowserElementChild() { - // Maps outer window id --> weak ref to window. Used by modal dialog code. - this._windowIDDict = {}; - - this._init(); -} - -BrowserElementChild.prototype = { - _init() { - debug("Starting up."); - - BrowserElementPromptService.mapWindowToBrowserElementChild(content, this); - - this._shuttingDown = false; - - LISTENED_EVENTS.forEach(event => { - addEventListener( - event.type, - this, - event.useCapture, - event.wantsUntrusted - ); - }); - - addMessageListener("browser-element-api:call", this); - }, - - /** - * Shut down the frame's side of the browser API. This is called when: - * - our BrowserChildGlobal starts to die - * - the content is moved to frame without the browser API - * This is not called when the page inside |content| unloads. - */ - destroy() { - debug("Destroying"); - this._shuttingDown = true; - - BrowserElementPromptService.unmapWindowToBrowserElementChild(content); - - LISTENED_EVENTS.forEach(event => { - removeEventListener( - event.type, - this, - event.useCapture, - event.wantsUntrusted - ); - }); - - removeMessageListener("browser-element-api:call", this); - }, - - handleEvent(event) { - switch (event.type) { - case "unload": - this.destroy(event); - break; - } - }, - - receiveMessage(message) { - let self = this; - - let mmCalls = { - "unblock-modal-prompt": this._recvStopWaiting, - }; - - if (message.data.msg_name in mmCalls) { - return mmCalls[message.data.msg_name].apply(self, arguments); - } - return undefined; - }, - - get _windowUtils() { - return content.document.defaultView.windowUtils; - }, - - _tryGetInnerWindowID(win) { - try { - return win.windowGlobalChild.innerWindowId; - } catch (e) { - return null; - } - }, - - /** - * Show a modal prompt. Called by BrowserElementPromptService. - */ - showModalPrompt(win, args) { - args.windowID = { - outer: win.docShell.outerWindowID, - inner: this._tryGetInnerWindowID(win), - }; - sendAsyncMsg("showmodalprompt", args); - - let returnValue = this._waitForResult(win); - - if ( - args.promptType == "prompt" || - args.promptType == "confirm" || - args.promptType == "custom-prompt" - ) { - return returnValue; - } - return undefined; - }, - - /** - * Spin in a nested event loop until we receive a unblock-modal-prompt message for - * this window. - */ - _waitForResult(win) { - debug("_waitForResult(" + win + ")"); - let utils = win.windowUtils; - - let outerWindowID = win.docShell.outerWindowID; - let innerWindowID = this._tryGetInnerWindowID(win); - if (innerWindowID === null) { - // I have no idea what waiting for a result means when there's no inner - // window, so let's just bail. - debug("_waitForResult: No inner window. Bailing."); - return undefined; - } - - this._windowIDDict[outerWindowID] = Cu.getWeakReference(win); - - debug( - "Entering modal state (outerWindowID=" + - outerWindowID + - ", " + - "innerWindowID=" + - innerWindowID + - ")" - ); - - utils.enterModalState(); - - // We'll decrement win.modalDepth when we receive a unblock-modal-prompt message - // for the window. - if (!win.modalDepth) { - win.modalDepth = 0; - } - win.modalDepth++; - let origModalDepth = win.modalDepth; - - debug("Nested event loop - begin"); - Services.tm.spinEventLoopUntil( - "BrowserElementChildPreload.js:_waitForResult", - () => { - // Bail out of the loop if the inner window changed; that means the - // window navigated. Bail out when we're shutting down because otherwise - // we'll leak our window. - if (this._tryGetInnerWindowID(win) !== innerWindowID) { - debug( - "_waitForResult: Inner window ID changed " + - "while in nested event loop." - ); - return true; - } - - return win.modalDepth !== origModalDepth || this._shuttingDown; - } - ); - debug("Nested event loop - finish"); - - if (win.modalDepth == 0) { - delete this._windowIDDict[outerWindowID]; - } - - // If we exited the loop because the inner window changed, then bail on the - // modal prompt. - if (innerWindowID !== this._tryGetInnerWindowID(win)) { - throw Components.Exception( - "Modal state aborted by navigation", - Cr.NS_ERROR_NOT_AVAILABLE - ); - } - - let returnValue = win.modalReturnValue; - delete win.modalReturnValue; - - if (!this._shuttingDown) { - utils.leaveModalState(); - } - - debug( - "Leaving modal state (outerID=" + - outerWindowID + - ", " + - "innerID=" + - innerWindowID + - ")" - ); - return returnValue; - }, - - _recvStopWaiting(msg) { - let outerID = msg.json.windowID.outer; - let innerID = msg.json.windowID.inner; - let returnValue = msg.json.returnValue; - debug( - "recvStopWaiting(outer=" + - outerID + - ", inner=" + - innerID + - ", returnValue=" + - returnValue + - ")" - ); - - if (!this._windowIDDict[outerID]) { - debug("recvStopWaiting: No record of outer window ID " + outerID); - return; - } - - let win = this._windowIDDict[outerID].get(); - - if (!win) { - debug("recvStopWaiting, but window is gone\n"); - return; - } - - if (innerID !== this._tryGetInnerWindowID(win)) { - debug("recvStopWaiting, but inner ID has changed\n"); - return; - } - - debug("recvStopWaiting " + win); - win.modalReturnValue = returnValue; - win.modalDepth--; - }, -}; - -var api = new BrowserElementChild(); diff --git a/dom/browser-element/BrowserElementParent.jsm b/dom/browser-element/BrowserElementParent.jsm deleted file mode 100644 index ec342dace2..0000000000 --- a/dom/browser-element/BrowserElementParent.jsm +++ /dev/null @@ -1,276 +0,0 @@ -/* 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"; - -/* BrowserElementParent injects script to listen for certain events in the - * child. We then listen to messages from the child script and take - * appropriate action here in the parent. - */ - -const { BrowserElementPromptService } = ChromeUtils.import( - "resource://gre/modules/BrowserElementPromptService.jsm" -); - -function debug(msg) { - // dump("BrowserElementParent - " + msg + "\n"); -} - -function handleWindowEvent(e) { - if (this._browserElementParents) { - let beps = ChromeUtils.nondeterministicGetWeakMapKeys( - this._browserElementParents - ); - beps.forEach(bep => bep._handleOwnerEvent(e)); - } -} - -function BrowserElementParent() { - debug("Creating new BrowserElementParent object"); -} - -BrowserElementParent.prototype = { - classDescription: "BrowserElementAPI implementation", - classID: Components.ID("{9f171ac4-0939-4ef8-b360-3408aedc3060}"), - contractID: "@mozilla.org/dom/browser-element-api;1", - QueryInterface: ChromeUtils.generateQI([ - "nsIBrowserElementAPI", - "nsISupportsWeakReference", - ]), - - setFrameLoader(frameLoader) { - debug("Setting frameLoader"); - this._frameLoader = frameLoader; - this._frameElement = frameLoader.ownerElement; - if (!this._frameElement) { - debug("No frame element?"); - return; - } - // Listen to visibilitychange on the iframe's owner window, and forward - // changes down to the child. We want to do this while registering as few - // visibilitychange listeners on _window as possible, because such a listener - // may live longer than this BrowserElementParent object. - // - // To accomplish this, we register just one listener on the window, and have - // it reference a WeakMap whose keys are all the BrowserElementParent objects - // on the window. Then when the listener fires, we iterate over the - // WeakMap's keys (which we can do, because we're chrome) to notify the - // BrowserElementParents. - if (!this._window._browserElementParents) { - this._window._browserElementParents = new WeakMap(); - let handler = handleWindowEvent.bind(this._window); - let windowEvents = ["visibilitychange"]; - for (let event of windowEvents) { - Services.els.addSystemEventListener( - this._window, - event, - handler, - /* useCapture = */ true - ); - } - } - - this._window._browserElementParents.set(this, null); - - // Insert ourself into the prompt service. - BrowserElementPromptService.mapFrameToBrowserElementParent( - this._frameElement, - this - ); - this._setupMessageListener(); - }, - - destroyFrameScripts() { - debug("Destroying frame scripts"); - this._mm.sendAsyncMessage("browser-element-api:destroy"); - }, - - _setupMessageListener() { - this._mm = this._frameLoader.messageManager; - this._mm.addMessageListener("browser-element-api:call", this); - }, - - receiveMessage(aMsg) { - if (!this._isAlive()) { - return undefined; - } - - // Messages we receive are handed to functions which take a (data) argument, - // where |data| is the message manager's data object. - // We use a single message and dispatch to various function based - // on data.msg_name - let mmCalls = { - hello: this._recvHello, - }; - - let mmSecuritySensitiveCalls = { - showmodalprompt: this._handleShowModalPrompt, - }; - - if (aMsg.data.msg_name in mmCalls) { - return mmCalls[aMsg.data.msg_name].apply(this, arguments); - } else if (aMsg.data.msg_name in mmSecuritySensitiveCalls) { - return mmSecuritySensitiveCalls[aMsg.data.msg_name].apply( - this, - arguments - ); - } - return undefined; - }, - - _removeMessageListener() { - this._mm.removeMessageListener("browser-element-api:call", this); - }, - - /** - * You shouldn't touch this._frameElement or this._window if _isAlive is - * false. (You'll likely get an exception if you do.) - */ - _isAlive() { - return ( - !Cu.isDeadWrapper(this._frameElement) && - !Cu.isDeadWrapper(this._frameElement.ownerDocument) && - !Cu.isDeadWrapper(this._frameElement.ownerGlobal) - ); - }, - - get _window() { - return this._frameElement.ownerGlobal; - }, - - _sendAsyncMsg(msg, data) { - try { - if (!data) { - data = {}; - } - - data.msg_name = msg; - this._mm.sendAsyncMessage("browser-element-api:call", data); - } catch (e) { - return false; - } - return true; - }, - - _recvHello() { - debug("recvHello"); - - // Inform our child if our owner element's document is invisible. Note - // that we must do so here, rather than in the BrowserElementParent - // constructor, because the BrowserElementChild may not be initialized when - // we run our constructor. - if (this._window.document.hidden) { - this._ownerVisibilityChange(); - } - }, - - /** - * Fire either a vanilla or a custom event, depending on the contents of - * |data|. - */ - _fireEventFromMsg(data) { - let detail = data.json; - let name = detail.msg_name; - - // For events that send a "_payload_" property, we just want to transmit - // this in the event. - if ("_payload_" in detail) { - detail = detail._payload_; - } - - debug("fireEventFromMsg: " + name + ", " + JSON.stringify(detail)); - let evt = this._createEvent(name, detail, /* cancelable = */ false); - this._frameElement.dispatchEvent(evt); - }, - - _handleShowModalPrompt(data) { - // Fire a showmodalprmopt event on the iframe. When this method is called, - // the child is spinning in a nested event loop waiting for an - // unblock-modal-prompt message. - // - // If the embedder calls preventDefault() on the showmodalprompt event, - // we'll block the child until event.detail.unblock() is called. - // - // Otherwise, if preventDefault() is not called, we'll send the - // unblock-modal-prompt message to the child as soon as the event is done - // dispatching. - - let detail = data.json; - debug("handleShowPrompt " + JSON.stringify(detail)); - - // Strip off the windowID property from the object we send along in the - // event. - let windowID = detail.windowID; - delete detail.windowID; - debug("Event will have detail: " + JSON.stringify(detail)); - let evt = this._createEvent( - "showmodalprompt", - detail, - /* cancelable = */ true - ); - - let self = this; - let unblockMsgSent = false; - function sendUnblockMsg() { - if (unblockMsgSent) { - return; - } - unblockMsgSent = true; - - // We don't need to sanitize evt.detail.returnValue (e.g. converting the - // return value of confirm() to a boolean); Gecko does that for us. - - let data = { windowID, returnValue: evt.detail.returnValue }; - self._sendAsyncMsg("unblock-modal-prompt", data); - } - - Cu.exportFunction(sendUnblockMsg, evt.detail, { defineAs: "unblock" }); - - this._frameElement.dispatchEvent(evt); - - if (!evt.defaultPrevented) { - // Unblock the inner frame immediately. Otherwise we'll unblock upon - // evt.detail.unblock(). - sendUnblockMsg(); - } - }, - - _createEvent(evtName, detail, cancelable) { - // This will have to change if we ever want to send a CustomEvent with null - // detail. For now, it's OK. - if (detail !== undefined && detail !== null) { - detail = Cu.cloneInto(detail, this._window); - return new this._window.CustomEvent("mozbrowser" + evtName, { - bubbles: true, - cancelable, - detail, - }); - } - - return new this._window.Event("mozbrowser" + evtName, { - bubbles: true, - cancelable, - }); - }, - - _handleOwnerEvent(evt) { - switch (evt.type) { - case "visibilitychange": - this._ownerVisibilityChange(); - break; - } - }, - - /** - * Called when the visibility of the window which owns this iframe changes. - */ - _ownerVisibilityChange() { - let bc = this._frameLoader?.browsingContext; - if (bc) { - bc.isActive = !this._window.document.hidden; - } - }, -}; - -var EXPORTED_SYMBOLS = ["BrowserElementParent"]; diff --git a/dom/browser-element/BrowserElementPromptService.jsm b/dom/browser-element/BrowserElementPromptService.jsm deleted file mode 100644 index dd73004959..0000000000 --- a/dom/browser-element/BrowserElementPromptService.jsm +++ /dev/null @@ -1,720 +0,0 @@ -/* 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/. */ -/* vim: set ft=javascript : */ - -"use strict"; - -var Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - -var EXPORTED_SYMBOLS = ["BrowserElementPromptService"]; - -function debug(msg) { - // dump("BrowserElementPromptService - " + msg + "\n"); -} - -function BrowserElementPrompt(win, browserElementChild) { - this._win = win; - this._browserElementChild = browserElementChild; -} - -BrowserElementPrompt.prototype = { - QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]), - - alert(title, text) { - this._browserElementChild.showModalPrompt(this._win, { - promptType: "alert", - title, - message: text, - returnValue: undefined, - }); - }, - - alertCheck(title, text, checkMsg, checkState) { - // Treat this like a normal alert() call, ignoring the checkState. The - // front-end can do its own suppression of the alert() if it wants. - this.alert(title, text); - }, - - confirm(title, text) { - return this._browserElementChild.showModalPrompt(this._win, { - promptType: "confirm", - title, - message: text, - returnValue: undefined, - }); - }, - - confirmCheck(title, text, checkMsg, checkState) { - return this.confirm(title, text); - }, - - // Each button is described by an object with the following schema - // { - // string messageType, // 'builtin' or 'custom' - // string message, // 'ok', 'cancel', 'yes', 'no', 'save', 'dontsave', - // // 'revert' or a string from caller if messageType was 'custom'. - // } - // - // Expected result from embedder: - // { - // int button, // Index of the button that user pressed. - // boolean checked, // True if the check box is checked. - // } - confirmEx( - title, - text, - buttonFlags, - button0Title, - button1Title, - button2Title, - checkMsg, - checkState - ) { - let buttonProperties = this._buildConfirmExButtonProperties( - buttonFlags, - button0Title, - button1Title, - button2Title - ); - let defaultReturnValue = { selectedButton: buttonProperties.defaultButton }; - if (checkMsg) { - defaultReturnValue.checked = checkState.value; - } - let ret = this._browserElementChild.showModalPrompt(this._win, { - promptType: "custom-prompt", - title, - message: text, - defaultButton: buttonProperties.defaultButton, - buttons: buttonProperties.buttons, - showCheckbox: !!checkMsg, - checkboxMessage: checkMsg, - checkboxCheckedByDefault: !!checkState.value, - returnValue: defaultReturnValue, - }); - if (checkMsg) { - checkState.value = ret.checked; - } - return buttonProperties.indexToButtonNumberMap[ret.selectedButton]; - }, - - prompt(title, text, value, checkMsg, checkState) { - let rv = this._browserElementChild.showModalPrompt(this._win, { - promptType: "prompt", - title, - message: text, - initialValue: value.value, - returnValue: null, - }); - - value.value = rv; - - // nsIPrompt::Prompt returns true if the user pressed "OK" at the prompt, - // and false if the user pressed "Cancel". - // - // BrowserElementChild returns null for "Cancel" and returns the string the - // user entered otherwise. - return rv !== null; - }, - - promptUsernameAndPassword(title, text, username, password) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, - - promptPassword(title, text, password) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, - - select(title, text, aSelectList, aOutSelection) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, - - _buildConfirmExButtonProperties( - buttonFlags, - button0Title, - button1Title, - button2Title - ) { - let r = { - defaultButton: -1, - buttons: [], - // This map is for translating array index to the button number that - // is recognized by Gecko. This shouldn't be exposed to embedder. - indexToButtonNumberMap: [], - }; - - let defaultButton = 0; // Default to Button 0. - if (buttonFlags & Ci.nsIPrompt.BUTTON_POS_1_DEFAULT) { - defaultButton = 1; - } else if (buttonFlags & Ci.nsIPrompt.BUTTON_POS_2_DEFAULT) { - defaultButton = 2; - } - - // Properties of each button. - let buttonPositions = [ - Ci.nsIPrompt.BUTTON_POS_0, - Ci.nsIPrompt.BUTTON_POS_1, - Ci.nsIPrompt.BUTTON_POS_2, - ]; - - function buildButton(buttonTitle, buttonNumber) { - let ret = {}; - let buttonPosition = buttonPositions[buttonNumber]; - let mask = 0xff * buttonPosition; // 8 bit mask - let titleType = (buttonFlags & mask) / buttonPosition; - - ret.messageType = "builtin"; - switch (titleType) { - case Ci.nsIPrompt.BUTTON_TITLE_OK: - ret.message = "ok"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_CANCEL: - ret.message = "cancel"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_YES: - ret.message = "yes"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_NO: - ret.message = "no"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_SAVE: - ret.message = "save"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_DONT_SAVE: - ret.message = "dontsave"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_REVERT: - ret.message = "revert"; - break; - case Ci.nsIPrompt.BUTTON_TITLE_IS_STRING: - ret.message = buttonTitle; - ret.messageType = "custom"; - break; - default: - // This button is not shown. - return; - } - - // If this is the default button, set r.defaultButton to - // the index of this button in the array. This value is going to be - // exposed to the embedder. - if (defaultButton === buttonNumber) { - r.defaultButton = r.buttons.length; - } - r.buttons.push(ret); - r.indexToButtonNumberMap.push(buttonNumber); - } - - buildButton(button0Title, 0); - buildButton(button1Title, 1); - buildButton(button2Title, 2); - - // If defaultButton is still -1 here, it means the default button won't - // be shown. - if (r.defaultButton === -1) { - throw new Components.Exception( - "Default button won't be shown", - Cr.NS_ERROR_FAILURE - ); - } - - return r; - }, -}; - -function BrowserElementAuthPrompt() {} - -BrowserElementAuthPrompt.prototype = { - QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), - - promptAuth: function promptAuth(channel, level, authInfo) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, - - asyncPromptAuth: function asyncPromptAuth( - channel, - callback, - context, - level, - authInfo - ) { - debug("asyncPromptAuth"); - - // The cases that we don't support now. - if ( - authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY && - authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD - ) { - throw Components.Exception("", Cr.NS_ERROR_FAILURE); - } - - let frame = this._getFrameFromChannel(channel); - if (!frame) { - debug("Cannot get frame, asyncPromptAuth fail"); - throw Components.Exception("", Cr.NS_ERROR_FAILURE); - } - - let browserElementParent = - BrowserElementPromptService.getBrowserElementParentForFrame(frame); - - if (!browserElementParent) { - debug("Failed to load browser element parent."); - throw Components.Exception("", Cr.NS_ERROR_FAILURE); - } - - let consumer = { - QueryInterface: ChromeUtils.generateQI(["nsICancelable"]), - callback, - context, - cancel() { - this.callback.onAuthCancelled(this.context, false); - this.callback = null; - this.context = null; - }, - }; - - let [hostname, httpRealm] = this._getAuthTarget(channel, authInfo); - let hashKey = level + "|" + hostname + "|" + httpRealm; - let asyncPrompt = this._asyncPrompts[hashKey]; - if (asyncPrompt) { - asyncPrompt.consumers.push(consumer); - return consumer; - } - - asyncPrompt = { - consumers: [consumer], - channel, - authInfo, - level, - inProgress: false, - browserElementParent, - }; - - this._asyncPrompts[hashKey] = asyncPrompt; - this._doAsyncPrompt(); - return consumer; - }, - - // Utilities for nsIAuthPrompt2 ---------------- - - _asyncPrompts: {}, - _asyncPromptInProgress: new WeakMap(), - _doAsyncPrompt() { - // Find the key of a prompt whose browser element parent does not have - // async prompt in progress. - let hashKey = null; - for (let key in this._asyncPrompts) { - let prompt = this._asyncPrompts[key]; - if (!this._asyncPromptInProgress.get(prompt.browserElementParent)) { - hashKey = key; - break; - } - } - - // Didn't find an available prompt, so just return. - if (!hashKey) { - return; - } - - let prompt = this._asyncPrompts[hashKey]; - - this._asyncPromptInProgress.set(prompt.browserElementParent, true); - prompt.inProgress = true; - - let self = this; - let callback = function (ok, username, password) { - debug( - "Async auth callback is called, ok = " + ok + ", username = " + username - ); - - // Here we got the username and password provided by embedder, or - // ok = false if the prompt was cancelled by embedder. - delete self._asyncPrompts[hashKey]; - prompt.inProgress = false; - self._asyncPromptInProgress.delete(prompt.browserElementParent); - - // Fill authentication information with username and password provided - // by user. - let flags = prompt.authInfo.flags; - if (username) { - if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { - // Domain is separated from username by a backslash - let idx = username.indexOf("\\"); - if (idx == -1) { - prompt.authInfo.username = username; - } else { - prompt.authInfo.domain = username.substring(0, idx); - prompt.authInfo.username = username.substring(idx + 1); - } - } else { - prompt.authInfo.username = username; - } - } - - if (password) { - prompt.authInfo.password = password; - } - - for (let consumer of prompt.consumers) { - if (!consumer.callback) { - // Not having a callback means that consumer didn't provide it - // or canceled the notification. - continue; - } - - try { - if (ok) { - debug("Ok, calling onAuthAvailable to finish auth"); - consumer.callback.onAuthAvailable( - consumer.context, - prompt.authInfo - ); - } else { - debug("Cancelled, calling onAuthCancelled to finish auth."); - consumer.callback.onAuthCancelled(consumer.context, true); - } - } catch (e) { - /* Throw away exceptions caused by callback */ - } - } - - // Process the next prompt, if one is pending. - self._doAsyncPrompt(); - }; - - let runnable = { - run() { - // Call promptAuth of browserElementParent, to show the prompt. - prompt.browserElementParent.promptAuth( - self._createAuthDetail(prompt.channel, prompt.authInfo), - callback - ); - }, - }; - - Services.tm.dispatchToMainThread(runnable); - }, - - _getFrameFromChannel(channel) { - let loadContext = channel.notificationCallbacks.getInterface( - Ci.nsILoadContext - ); - return loadContext.topFrameElement; - }, - - _createAuthDetail(channel, authInfo) { - let [hostname, httpRealm] = this._getAuthTarget(channel, authInfo); - return { - host: hostname, - path: channel.URI.pathQueryRef, - realm: httpRealm, - username: authInfo.username, - isProxy: !!(authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY), - isOnlyPassword: !!(authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD), - }; - }, - - // The code is taken from nsLoginManagerPrompter.js, with slight - // modification for parameter name consistency here. - _getAuthTarget(channel, authInfo) { - let hostname, realm; - - // If our proxy is demanding authentication, don't use the - // channel's actual destination. - if (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { - if (!(channel instanceof Ci.nsIProxiedChannel)) { - throw new Error("proxy auth needs nsIProxiedChannel"); - } - - let info = channel.proxyInfo; - if (!info) { - throw new Error("proxy auth needs nsIProxyInfo"); - } - - // Proxies don't have a scheme, but we'll use "moz-proxy://" - // so that it's more obvious what the login is for. - var idnService = Cc["@mozilla.org/network/idn-service;1"].getService( - Ci.nsIIDNService - ); - hostname = - "moz-proxy://" + - idnService.convertUTF8toACE(info.host) + - ":" + - info.port; - realm = authInfo.realm; - if (!realm) { - realm = hostname; - } - - return [hostname, realm]; - } - - hostname = this._getFormattedHostname(channel.URI); - - // If a HTTP WWW-Authenticate header specified a realm, that value - // will be available here. If it wasn't set or wasn't HTTP, we'll use - // the formatted hostname instead. - realm = authInfo.realm; - if (!realm) { - realm = hostname; - } - - return [hostname, realm]; - }, - - /** - * Strip out things like userPass and path for display. - */ - _getFormattedHostname(uri) { - return uri.scheme + "://" + uri.hostPort; - }, -}; - -function AuthPromptWrapper(oldImpl, browserElementImpl) { - this._oldImpl = oldImpl; - this._browserElementImpl = browserElementImpl; -} - -AuthPromptWrapper.prototype = { - QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), - promptAuth(channel, level, authInfo) { - if (this._canGetParentElement(channel)) { - return this._browserElementImpl.promptAuth(channel, level, authInfo); - } - return this._oldImpl.promptAuth(channel, level, authInfo); - }, - - asyncPromptAuth(channel, callback, context, level, authInfo) { - if (this._canGetParentElement(channel)) { - return this._browserElementImpl.asyncPromptAuth( - channel, - callback, - context, - level, - authInfo - ); - } - return this._oldImpl.asyncPromptAuth( - channel, - callback, - context, - level, - authInfo - ); - }, - - _canGetParentElement(channel) { - try { - let context = channel.notificationCallbacks.getInterface( - Ci.nsILoadContext - ); - let frame = context.topFrameElement; - if (!frame) { - return false; - } - - if (!BrowserElementPromptService.getBrowserElementParentForFrame(frame)) { - return false; - } - - return true; - } catch (e) { - return false; - } - }, -}; - -function BrowserElementPromptFactory(toWrap) { - this._wrapped = toWrap; -} - -BrowserElementPromptFactory.prototype = { - classID: Components.ID("{24f3d0cf-e417-4b85-9017-c9ecf8bb1299}"), - QueryInterface: ChromeUtils.generateQI(["nsIPromptFactory"]), - - _mayUseNativePrompt() { - try { - return Services.prefs.getBoolPref("browser.prompt.allowNative"); - } catch (e) { - // This properity is default to true. - return true; - } - }, - - _getNativePromptIfAllowed(win, iid, err) { - if (this._mayUseNativePrompt()) { - return this._wrapped.getPrompt(win, iid); - } - - // Not allowed, throw an exception. - throw err; - }, - - getPrompt(win, iid) { - // It is possible for some object to get a prompt without passing - // valid reference of window, like nsNSSComponent. In such case, we - // should just fall back to the native prompt service - if (!win) { - return this._getNativePromptIfAllowed(win, iid, Cr.NS_ERROR_INVALID_ARG); - } - - if ( - iid.number != Ci.nsIPrompt.number && - iid.number != Ci.nsIAuthPrompt2.number - ) { - debug( - "We don't recognize the requested IID (" + - iid + - ", " + - "allowed IID: " + - "nsIPrompt=" + - Ci.nsIPrompt + - ", " + - "nsIAuthPrompt2=" + - Ci.nsIAuthPrompt2 + - ")" - ); - return this._getNativePromptIfAllowed(win, iid, Cr.NS_ERROR_INVALID_ARG); - } - - // Try to find a BrowserElementChild for the window. - let browserElementChild = - BrowserElementPromptService.getBrowserElementChildForWindow(win); - - if (iid.number === Ci.nsIAuthPrompt2.number) { - debug("Caller requests an instance of nsIAuthPrompt2."); - - if (browserElementChild) { - // If we are able to get a BrowserElementChild, it means that - // the auth prompt is for a mozbrowser. Therefore we don't need to - // fall back. - return new BrowserElementAuthPrompt().QueryInterface(iid); - } - - // Because nsIAuthPrompt2 is called in parent process. If caller - // wants nsIAuthPrompt2 and we cannot get BrowserElementchild, - // it doesn't mean that we should fallback. It is possible that we can - // get the BrowserElementParent from nsIChannel that passed to - // functions of nsIAuthPrompt2. - if (this._mayUseNativePrompt()) { - return new AuthPromptWrapper( - this._wrapped.getPrompt(win, iid), - new BrowserElementAuthPrompt().QueryInterface(iid) - ).QueryInterface(iid); - } - // Falling back is not allowed, so we don't need wrap the - // BrowserElementPrompt. - return new BrowserElementAuthPrompt().QueryInterface(iid); - } - - if (!browserElementChild) { - debug( - "We can't find a browserElementChild for " + win + ", " + win.location - ); - return this._getNativePromptIfAllowed(win, iid, Cr.NS_ERROR_FAILURE); - } - - debug("Returning wrapped getPrompt for " + win); - return new BrowserElementPrompt(win, browserElementChild).QueryInterface( - iid - ); - }, -}; - -var BrowserElementPromptService = { - QueryInterface: ChromeUtils.generateQI([ - "nsIObserver", - "nsISupportsWeakReference", - ]), - - _initialized: false, - - _init() { - if (this._initialized) { - return; - } - - this._initialized = true; - this._browserElementParentMap = new WeakMap(); - - Services.obs.addObserver( - this, - "outer-window-destroyed", - /* ownsWeak = */ true - ); - - // Wrap the existing @mozilla.org/prompter;1 implementation. - var contractID = "@mozilla.org/prompter;1"; - var oldCID = Cm.contractIDToCID(contractID); - var newCID = BrowserElementPromptFactory.prototype.classID; - var oldFactory = Cm.getClassObject(Cc[contractID], Ci.nsIFactory); - - if (oldCID == newCID) { - debug("WARNING: Wrapped prompt factory is already installed!"); - return; - } - - var oldInstance = oldFactory.createInstance(null, Ci.nsIPromptFactory); - var newInstance = new BrowserElementPromptFactory(oldInstance); - - var newFactory = { - createInstance(iid) { - return newInstance.QueryInterface(iid); - }, - }; - Cm.registerFactory( - newCID, - "BrowserElementPromptService's prompter;1 wrapper", - contractID, - newFactory - ); - - debug("Done installing new prompt factory."); - }, - - _getOuterWindowID(win) { - return win.docShell.outerWindowID; - }, - - _browserElementChildMap: {}, - mapWindowToBrowserElementChild(win, browserElementChild) { - this._browserElementChildMap[this._getOuterWindowID(win)] = - browserElementChild; - }, - unmapWindowToBrowserElementChild(win) { - delete this._browserElementChildMap[this._getOuterWindowID(win)]; - }, - - getBrowserElementChildForWindow(win) { - // We only have a mapping for <iframe mozbrowser>s, not their inner - // <iframes>, so we look up win.top below. window.top (when called from - // script) respects <iframe mozbrowser> boundaries. - return this._browserElementChildMap[this._getOuterWindowID(win.top)]; - }, - - mapFrameToBrowserElementParent(frame, browserElementParent) { - this._browserElementParentMap.set(frame, browserElementParent); - }, - - getBrowserElementParentForFrame(frame) { - return this._browserElementParentMap.get(frame); - }, - - _observeOuterWindowDestroyed(outerWindowID) { - let id = outerWindowID.QueryInterface(Ci.nsISupportsPRUint64).data; - debug("observeOuterWindowDestroyed " + id); - delete this._browserElementChildMap[outerWindowID.data]; - }, - - observe(subject, topic, data) { - switch (topic) { - case "outer-window-destroyed": - this._observeOuterWindowDestroyed(subject); - break; - default: - debug("Observed unexpected topic " + topic); - } - }, -}; - -BrowserElementPromptService._init(); diff --git a/dom/browser-element/components.conf b/dom/browser-element/components.conf deleted file mode 100644 index a29f7dc66a..0000000000 --- a/dom/browser-element/components.conf +++ /dev/null @@ -1,14 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -Classes = [ - { - 'cid': '{9f171ac4-0939-4ef8-b360-3408aedc3060}', - 'contract_ids': ['@mozilla.org/dom/browser-element-api;1'], - 'jsm': 'resource://gre/modules/BrowserElementParent.jsm', - 'constructor': 'BrowserElementParent', - }, -] diff --git a/dom/browser-element/moz.build b/dom/browser-element/moz.build deleted file mode 100644 index 1f83fbd436..0000000000 --- a/dom/browser-element/moz.build +++ /dev/null @@ -1,37 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -with Files("**"): - BUG_COMPONENT = ("Core", "DOM: Core & HTML") - -XPIDL_SOURCES += [ - "nsIBrowserElementAPI.idl", -] - -XPIDL_MODULE = "browser-element" - -EXTRA_JS_MODULES += [ - "BrowserElementParent.jsm", - "BrowserElementPromptService.jsm", -] - -XPCOM_MANIFESTS += [ - "components.conf", -] - -LOCAL_INCLUDES += [ - "/dom/html", -] - -include("/ipc/chromium/chromium-config.mozbuild") - -FINAL_LIBRARY = "xul" - -LOCAL_INCLUDES += [ - "/dom/", - "/dom/base", - "/dom/ipc", -] diff --git a/dom/browser-element/nsIBrowserElementAPI.idl b/dom/browser-element/nsIBrowserElementAPI.idl deleted file mode 100644 index 4aeba96be8..0000000000 --- a/dom/browser-element/nsIBrowserElementAPI.idl +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* 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/. */ - -#include "nsISupports.idl" - -webidl FrameLoader; - -%{C++ -#define BROWSER_ELEMENT_API_CONTRACTID "@mozilla.org/dom/browser-element-api;1" -#define BROWSER_ELEMENT_API_CID \ - { 0x651db7e3, 0x1734, 0x4536, \ - { 0xb1, 0x5a, 0x5b, 0x3a, 0xe6, 0x44, 0x13, 0x4c } } -%} - -/** - * Interface to the BrowserElementParent implementation. All methods - * but setFrameLoader throw when the remote process is dead. - */ -[scriptable, uuid(57758c10-6036-11e5-a837-0800200c9a66)] -interface nsIBrowserElementAPI : nsISupports -{ - /** - * Notify frame scripts that support the API to destroy. - */ - void destroyFrameScripts(); - - void setFrameLoader(in FrameLoader frameLoader); - - void sendMouseEvent(in AString type, - in uint32_t x, - in uint32_t y, - in uint32_t button, - in uint32_t clickCount, - in uint32_t mifiers); - void goBack(); - void goForward(); - void reload(in boolean hardReload); - void stop(); - Promise getCanGoBack(); - Promise getCanGoForward(); -}; |