diff options
Diffstat (limited to 'toolkit/content/widgets/remote-browser.xml')
-rw-r--r-- | toolkit/content/widgets/remote-browser.xml | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/toolkit/content/widgets/remote-browser.xml b/toolkit/content/widgets/remote-browser.xml new file mode 100644 index 0000000000..b78179944d --- /dev/null +++ b/toolkit/content/widgets/remote-browser.xml @@ -0,0 +1,591 @@ +<?xml version="1.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/. --> + +<bindings id="firefoxBrowserBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <binding id="remote-browser" extends="chrome://global/content/bindings/browser.xml#browser"> + + <implementation type="application/javascript" + implements="nsIObserver, nsIDOMEventListener, nsIMessageListener, nsIRemoteBrowser"> + + <field name="_securityUI">null</field> + + <property name="securityUI" + readonly="true"> + <getter><![CDATA[ + if (!this._securityUI) { + // Don't attempt to create the remote web progress if the + // messageManager has already gone away + if (!this.messageManager) + return null; + + let jsm = "resource://gre/modules/RemoteSecurityUI.jsm"; + let RemoteSecurityUI = Components.utils.import(jsm, {}).RemoteSecurityUI; + this._securityUI = new RemoteSecurityUI(); + } + + // We want to double-wrap the JS implemented interface, so that QI and instanceof works. + var ptr = Components.classes["@mozilla.org/supports-interface-pointer;1"] + .createInstance(Components.interfaces.nsISupportsInterfacePointer); + ptr.data = this._securityUI; + return ptr.data.QueryInterface(Components.interfaces.nsISecureBrowserUI); + ]]></getter> + </property> + + <!-- increases or decreases the browser's network priority --> + <method name="adjustPriority"> + <parameter name="adjustment"/> + <body><![CDATA[ + this.messageManager.sendAsyncMessage("NetworkPrioritizer:AdjustPriority", + {adjustment}); + ]]></body> + </method> + + <!-- sets the browser's network priority to a discrete value --> + <method name="setPriority"> + <parameter name="priority"/> + <body><![CDATA[ + this.messageManager.sendAsyncMessage("NetworkPrioritizer:SetPriority", + {priority}); + ]]></body> + </method> + + <field name="_controller">null</field> + + <field name="_selectParentHelper">null</field> + + <field name="_remoteWebNavigation">null</field> + + <property name="webNavigation" + onget="return this._remoteWebNavigation;" + readonly="true"/> + + <field name="_remoteWebProgress">null</field> + + <property name="webProgress" readonly="true"> + <getter> + <![CDATA[ + if (!this._remoteWebProgress) { + // Don't attempt to create the remote web progress if the + // messageManager has already gone away + if (!this.messageManager) + return null; + + let jsm = "resource://gre/modules/RemoteWebProgress.jsm"; + let { RemoteWebProgressManager } = Components.utils.import(jsm, {}); + this._remoteWebProgressManager = new RemoteWebProgressManager(this); + this._remoteWebProgress = this._remoteWebProgressManager.topLevelWebProgress; + } + return this._remoteWebProgress; + ]]> + </getter> + </property> + + <field name="_remoteFinder">null</field> + + <property name="finder" readonly="true"> + <getter><![CDATA[ + if (!this._remoteFinder) { + // Don't attempt to create the remote finder if the + // messageManager has already gone away + if (!this.messageManager) + return null; + + let jsm = "resource://gre/modules/RemoteFinder.jsm"; + let { RemoteFinder } = Components.utils.import(jsm, {}); + this._remoteFinder = new RemoteFinder(this); + } + return this._remoteFinder; + ]]></getter> + </property> + + <field name="_documentURI">null</field> + + <field name="_documentContentType">null</field> + + <!-- + Used by session restore to ensure that currentURI is set so + that switch-to-tab works before the tab is fully + restored. This function also invokes onLocationChanged + listeners in tabbrowser.xml. + --> + <method name="_setCurrentURI"> + <parameter name="aURI"/> + <body><![CDATA[ + this._remoteWebProgressManager.setCurrentURI(aURI); + ]]></body> + </method> + + <property name="documentURI" + onget="return this._documentURI;" + readonly="true"/> + + <property name="documentContentType" + onget="return this._documentContentType;" + readonly="true"/> + + <field name="_contentTitle">""</field> + + <property name="contentTitle" + onget="return this._contentTitle" + readonly="true"/> + + <field name="_characterSet">""</field> + + <property name="characterSet" + onget="return this._characterSet"> + <setter><![CDATA[ + this.messageManager.sendAsyncMessage("UpdateCharacterSet", {value: val}); + this._characterSet = val; + ]]></setter> + </property> + + <field name="_mayEnableCharacterEncodingMenu">null</field> + + <property name="mayEnableCharacterEncodingMenu" + onget="return this._mayEnableCharacterEncodingMenu;" + readonly="true"/> + + <field name="_contentWindow">null</field> + + <property name="contentWindow" + onget="return null" + readonly="true"/> + + <property name="contentWindowAsCPOW" + onget="return this._contentWindow" + readonly="true"/> + + <property name="contentDocument" + onget="return null" + readonly="true"/> + + <field name="_contentPrincipal">null</field> + + <property name="contentPrincipal" + onget="return this._contentPrincipal" + readonly="true"/> + + <property name="contentDocumentAsCPOW" + onget="return this.contentWindowAsCPOW ? this.contentWindowAsCPOW.document : null" + readonly="true"/> + + <field name="_imageDocument">null</field> + + <property name="imageDocument" + onget="return this._imageDocument" + readonly="true"/> + + <field name="_fullZoom">1</field> + <property name="fullZoom"> + <getter><![CDATA[ + return this._fullZoom; + ]]></getter> + <setter><![CDATA[ + let changed = val.toFixed(2) != this._fullZoom.toFixed(2); + + this._fullZoom = val; + this.messageManager.sendAsyncMessage("FullZoom", {value: val}); + + if (changed) { + let event = new Event("FullZoomChange", {bubbles: true}); + this.dispatchEvent(event); + } + ]]></setter> + </property> + + <field name="_textZoom">1</field> + <property name="textZoom"> + <getter><![CDATA[ + return this._textZoom; + ]]></getter> + <setter><![CDATA[ + let changed = val.toFixed(2) != this._textZoom.toFixed(2); + + this._textZoom = val; + this.messageManager.sendAsyncMessage("TextZoom", {value: val}); + + if (changed) { + let event = new Event("TextZoomChange", {bubbles: true}); + this.dispatchEvent(event); + } + ]]></setter> + </property> + + <field name="_isSyntheticDocument">false</field> + <property name="isSyntheticDocument"> + <getter><![CDATA[ + return this._isSyntheticDocument; + ]]></getter> + </property> + + <property name="hasContentOpener"> + <getter><![CDATA[ + let {frameLoader} = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner); + return frameLoader.tabParent.hasContentOpener; + ]]></getter> + </property> + + <field name="_outerWindowID">null</field> + <property name="outerWindowID" + onget="return this._outerWindowID" + readonly="true"/> + + <field name="_innerWindowID">null</field> + <property name="innerWindowID"> + <getter><![CDATA[ + return this._innerWindowID; + ]]></getter> + </property> + + <property name="docShellIsActive"> + <getter> + <![CDATA[ + let {frameLoader} = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner); + return frameLoader.tabParent.docShellIsActive; + ]]> + </getter> + <setter> + <![CDATA[ + let {frameLoader} = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner); + frameLoader.tabParent.docShellIsActive = val; + return val; + ]]> + </setter> + </property> + + <method name="preserveLayers"> + <parameter name="preserve"/> + <body><![CDATA[ + let {frameLoader} = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner); + if (frameLoader.tabParent) { + frameLoader.tabParent.preserveLayers(preserve); + } + ]]></body> + </method> + + <field name="_manifestURI"/> + <property name="manifestURI" + onget="return this._manifestURI" + readonly="true"/> + + <field name="mDestroyed">false</field> + + <field name="_permitUnloadId">0</field> + + <method name="getInPermitUnload"> + <parameter name="aCallback"/> + <body> + <![CDATA[ + let id = this._permitUnloadId++; + let mm = this.messageManager; + mm.sendAsyncMessage("InPermitUnload", {id}); + mm.addMessageListener("InPermitUnload", function listener(msg) { + if (msg.data.id != id) { + return; + } + aCallback(msg.data.inPermitUnload); + }); + ]]> + </body> + </method> + + <method name="permitUnload"> + <body> + <![CDATA[ + const kTimeout = 5000; + + let finished = false; + let responded = false; + let permitUnload; + let id = this._permitUnloadId++; + let mm = this.messageManager; + let Services = Components.utils.import("resource://gre/modules/Services.jsm", {}).Services; + + let msgListener = msg => { + if (msg.data.id != id) { + return; + } + if (msg.data.kind == "start") { + responded = true; + return; + } + done(msg.data.permitUnload); + }; + + let observer = subject => { + if (subject == mm) { + done(true); + } + }; + + function done(result) { + finished = true; + permitUnload = result; + mm.removeMessageListener("PermitUnload", msgListener); + Services.obs.removeObserver(observer, "message-manager-close"); + } + + mm.sendAsyncMessage("PermitUnload", {id}); + mm.addMessageListener("PermitUnload", msgListener); + Services.obs.addObserver(observer, "message-manager-close", false); + + let timedOut = false; + function timeout() { + if (!responded) { + timedOut = true; + } + + // Dispatch something to ensure that the main thread wakes up. + Services.tm.mainThread.dispatch(function() {}, Components.interfaces.nsIThread.DISPATCH_NORMAL); + } + + let timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); + timer.initWithCallback(timeout, kTimeout, timer.TYPE_ONE_SHOT); + + while (!finished && !timedOut) { + Services.tm.currentThread.processNextEvent(true); + } + + return {permitUnload, timedOut}; + ]]> + </body> + </method> + + <constructor> + <![CDATA[ + /* + * Don't try to send messages from this function. The message manager for + * the <browser> element may not be initialized yet. + */ + + this._remoteWebNavigation = Components.classes["@mozilla.org/remote-web-navigation;1"] + .createInstance(Components.interfaces.nsIWebNavigation); + this._remoteWebNavigationImpl = this._remoteWebNavigation.wrappedJSObject; + this._remoteWebNavigationImpl.swapBrowser(this); + + // Initialize contentPrincipal to the about:blank principal for this loadcontext + let {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {}); + let aboutBlank = Services.io.newURI("about:blank", null, null); + let ssm = Services.scriptSecurityManager; + this._contentPrincipal = ssm.getLoadContextCodebasePrincipal(aboutBlank, this.loadContext); + + this.messageManager.addMessageListener("Browser:Init", this); + this.messageManager.addMessageListener("DOMTitleChanged", this); + this.messageManager.addMessageListener("ImageDocumentLoaded", this); + this.messageManager.addMessageListener("FullZoomChange", this); + this.messageManager.addMessageListener("TextZoomChange", this); + this.messageManager.addMessageListener("ZoomChangeUsingMouseWheel", this); + this.messageManager.addMessageListener("DOMFullscreen:RequestExit", this); + this.messageManager.addMessageListener("DOMFullscreen:RequestRollback", this); + this.messageManager.addMessageListener("MozApplicationManifest", this); + this.messageManager.loadFrameScript("chrome://global/content/browser-child.js", true); + + if (this.hasAttribute("selectmenulist")) { + this.messageManager.addMessageListener("Forms:ShowDropDown", this); + this.messageManager.addMessageListener("Forms:HideDropDown", this); + this.messageManager.loadFrameScript("chrome://global/content/select-child.js", true); + } + + if (!this.hasAttribute("disablehistory")) { + Services.obs.addObserver(this, "browser:purge-session-history", true); + } + + let jsm = "resource://gre/modules/RemoteController.jsm"; + let RemoteController = Components.utils.import(jsm, {}).RemoteController; + this._controller = new RemoteController(this); + this.controllers.appendController(this._controller); + ]]> + </constructor> + + <destructor> + <![CDATA[ + this.destroy(); + ]]> + </destructor> + + <!-- This is necessary because the destructor doesn't always get called when + we are removed from a tabbrowser. This will be explicitly called by tabbrowser. + + Note: This overrides the destroy() method from browser.xml. --> + <method name="destroy"> + <body><![CDATA[ + // Make sure that any open select is closed. + if (this._selectParentHelper) { + let menulist = document.getElementById(this.getAttribute("selectmenulist")); + this._selectParentHelper.hide(menulist, this); + } + + if (this.mDestroyed) + return; + this.mDestroyed = true; + + try { + this.controllers.removeController(this._controller); + } catch (ex) { + // This can fail when this browser element is not attached to a + // BrowserDOMWindow. + } + + if (!this.hasAttribute("disablehistory")) { + let Services = Components.utils.import("resource://gre/modules/Services.jsm", {}).Services; + try { + Services.obs.removeObserver(this, "browser:purge-session-history"); + } catch (ex) { + // It's not clear why this sometimes throws an exception. + } + } + ]]></body> + </method> + + <method name="receiveMessage"> + <parameter name="aMessage"/> + <body><![CDATA[ + let data = aMessage.data; + switch (aMessage.name) { + case "Browser:Init": + this._outerWindowID = data.outerWindowID; + break; + case "DOMTitleChanged": + this._contentTitle = data.title; + break; + case "ImageDocumentLoaded": + this._imageDocument = { + width: data.width, + height: data.height + }; + break; + + case "Forms:ShowDropDown": { + if (!this._selectParentHelper) { + this._selectParentHelper = + Cu.import("resource://gre/modules/SelectParentHelper.jsm", {}).SelectParentHelper; + } + + let menulist = document.getElementById(this.getAttribute("selectmenulist")); + menulist.menupopup.style.direction = data.direction; + + let zoom = Services.prefs.getBoolPref("browser.zoom.full") || + this.isSyntheticDocument ? this._fullZoom : this._textZoom; + this._selectParentHelper.populate(menulist, data.options, data.selectedIndex, zoom); + this._selectParentHelper.open(this, menulist, data.rect, data.isOpenedViaTouch); + break; + } + + case "FullZoomChange": { + this._fullZoom = data.value; + let event = document.createEvent("Events"); + event.initEvent("FullZoomChange", true, false); + this.dispatchEvent(event); + break; + } + + case "TextZoomChange": { + this._textZoom = data.value; + let event = document.createEvent("Events"); + event.initEvent("TextZoomChange", true, false); + this.dispatchEvent(event); + break; + } + + case "ZoomChangeUsingMouseWheel": { + let event = document.createEvent("Events"); + event.initEvent("ZoomChangeUsingMouseWheel", true, false); + this.dispatchEvent(event); + break; + } + + case "Forms:HideDropDown": { + if (this._selectParentHelper) { + let menulist = document.getElementById(this.getAttribute("selectmenulist")); + this._selectParentHelper.hide(menulist, this); + } + break; + } + + case "DOMFullscreen:RequestExit": { + let windowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils); + windowUtils.exitFullscreen(); + break; + } + + case "DOMFullscreen:RequestRollback": { + let windowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils); + windowUtils.remoteFrameFullscreenReverted(); + break; + } + + case "MozApplicationManifest": + this._manifestURI = aMessage.data.manifest; + break; + + default: + // Delegate to browser.xml. + return this._receiveMessage(aMessage); + } + return undefined; + ]]></body> + </method> + + <method name="enableDisableCommands"> + <parameter name="aAction"/> + <parameter name="aEnabledLength"/> + <parameter name="aEnabledCommands"/> + <parameter name="aDisabledLength"/> + <parameter name="aDisabledCommands"/> + <body> + if (this._controller) { + this._controller.enableDisableCommands(aAction, + aEnabledLength, aEnabledCommands, + aDisabledLength, aDisabledCommands); + } + </body> + </method> + + <method name="purgeSessionHistory"> + <body> + <![CDATA[ + try { + this.messageManager.sendAsyncMessage("Browser:PurgeSessionHistory"); + } catch (ex) { + // This can throw if the browser has started to go away. + if (ex.result != Components.results.NS_ERROR_NOT_INITIALIZED) { + throw ex; + } + } + this._remoteWebNavigationImpl.canGoBack = false; + this._remoteWebNavigationImpl.canGoForward = false; + ]]> + </body> + </method> + + <method name="createAboutBlankContentViewer"> + <parameter name="aPrincipal"/> + <body> + <![CDATA[ + this.messageManager.sendAsyncMessage("Browser:CreateAboutBlank", aPrincipal); + ]]> + </body> + </method> + </implementation> + <handlers> + <handler event="dragstart"> + <![CDATA[ + // If we're a remote browser dealing with a dragstart, stop it + // from propagating up, since our content process should be dealing + // with the mouse movement. + event.stopPropagation(); + ]]> + </handler> + </handlers> + + </binding> + +</bindings> |