<?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>