summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/remote-browser.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/remote-browser.xml')
-rw-r--r--toolkit/content/widgets/remote-browser.xml591
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 000000000..b78179944
--- /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>