зеркало из https://github.com/mozilla/gecko-dev.git
606 строки
20 KiB
XML
606 строки
20 KiB
XML
<?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">
|
|
|
|
<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 = ChromeUtils.import(jsm, {}).RemoteSecurityUI;
|
|
this._securityUI = new RemoteSecurityUI();
|
|
}
|
|
|
|
// We want to double-wrap the JS implemented interface, so that QI and instanceof works.
|
|
var ptr = Cc["@mozilla.org/supports-interface-pointer;1"]
|
|
.createInstance(Ci.nsISupportsInterfacePointer);
|
|
ptr.data = this._securityUI;
|
|
return ptr.data.QueryInterface(Ci.nsISecureBrowserUI);
|
|
]]></getter>
|
|
</property>
|
|
|
|
<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"
|
|
onget="return this._remoteWebProgress;"
|
|
readonly="true"/>
|
|
|
|
<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/FinderParent.jsm";
|
|
let { FinderParent } = ChromeUtils.import(jsm, {});
|
|
this._remoteFinder = new FinderParent(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"/>
|
|
|
|
<field name="_contentRequestContextID">null</field>
|
|
|
|
<property name="contentRequestContextID"
|
|
onget="return this._contentRequestContextID"
|
|
readonly="true"/>
|
|
|
|
<field name="_contentDocument">null</field>
|
|
|
|
<property name="contentDocumentAsCPOW"
|
|
onget="return this._contentDocument"
|
|
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);
|
|
|
|
if (changed) {
|
|
this._fullZoom = val;
|
|
try {
|
|
this.messageManager.sendAsyncMessage("FullZoom", {value: val});
|
|
} catch (ex) {}
|
|
|
|
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);
|
|
|
|
if (changed) {
|
|
this._textZoom = val;
|
|
try {
|
|
this.messageManager.sendAsyncMessage("TextZoom", {value: val});
|
|
} catch (ex) {}
|
|
|
|
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[
|
|
return this.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[
|
|
return this.frameLoader.tabParent.docShellIsActive;
|
|
]]>
|
|
</getter>
|
|
<setter>
|
|
<![CDATA[
|
|
this.frameLoader.tabParent.docShellIsActive = val;
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="preserveLayers">
|
|
<parameter name="preserve"/>
|
|
<body><![CDATA[
|
|
let {frameLoader} = this;
|
|
if (frameLoader.tabParent) {
|
|
frameLoader.tabParent.preserveLayers(preserve);
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<property name="renderLayers">
|
|
<getter>
|
|
<![CDATA[
|
|
let {frameLoader} = this;
|
|
if (frameLoader && frameLoader.tabParent) {
|
|
return frameLoader.tabParent.renderLayers;
|
|
}
|
|
return false;
|
|
]]>
|
|
</getter>
|
|
<setter>
|
|
<![CDATA[
|
|
let {frameLoader} = this;
|
|
if (frameLoader && frameLoader.tabParent) {
|
|
return frameLoader.tabParent.renderLayers = val;
|
|
}
|
|
return false;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="forceRepaint">
|
|
<body>
|
|
<![CDATA[
|
|
let {frameLoader} = this;
|
|
if (frameLoader && frameLoader.tabParent) {
|
|
frameLoader.tabParent.forceRepaint();
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<property name="hasLayers" readonly="true">
|
|
<getter><![CDATA[
|
|
let {frameLoader} = this;
|
|
if (frameLoader.tabParent) {
|
|
return frameLoader.tabParent.hasLayers;
|
|
}
|
|
return false;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<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">
|
|
<parameter name="aPermitUnloadFlags"/>
|
|
<body>
|
|
<![CDATA[
|
|
let {tabParent} = this.frameLoader;
|
|
|
|
if (!tabParent.hasBeforeUnload) {
|
|
return { permitUnload: true, timedOut: false };
|
|
}
|
|
|
|
const kTimeout = 1000;
|
|
|
|
let finished = false;
|
|
let responded = false;
|
|
let permitUnload;
|
|
let id = this._permitUnloadId++;
|
|
let mm = this.messageManager;
|
|
let Services = ChromeUtils.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, aPermitUnloadFlags});
|
|
mm.addMessageListener("PermitUnload", msgListener);
|
|
Services.obs.addObserver(observer, "message-manager-close");
|
|
|
|
let timedOut = false;
|
|
function timeout() {
|
|
if (!responded) {
|
|
timedOut = true;
|
|
}
|
|
|
|
// Dispatch something to ensure that the main thread wakes up.
|
|
Services.tm.dispatchToMainThread(function() {});
|
|
}
|
|
|
|
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.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 = Cc["@mozilla.org/remote-web-navigation;1"]
|
|
.createInstance(Ci.nsIWebNavigation);
|
|
this._remoteWebNavigationImpl = this._remoteWebNavigation.wrappedJSObject;
|
|
this._remoteWebNavigationImpl.swapBrowser(this);
|
|
|
|
// Initialize contentPrincipal to the about:blank principal for this loadcontext
|
|
let {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
|
|
let aboutBlank = Services.io.newURI("about:blank");
|
|
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("MozApplicationManifest", this);
|
|
|
|
// browser-child messages, such as Content:LocationChange, are handled in
|
|
// RemoteWebProgress, ensure it is loaded and ready.
|
|
let jsm = "resource://gre/modules/RemoteWebProgress.jsm";
|
|
let { RemoteWebProgressManager } = ChromeUtils.import(jsm, {});
|
|
this._remoteWebProgressManager = new RemoteWebProgressManager(this);
|
|
this._remoteWebProgress = this._remoteWebProgressManager.topLevelWebProgress;
|
|
|
|
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);
|
|
}
|
|
|
|
if (!this.hasAttribute("disablehistory")) {
|
|
Services.obs.addObserver(this, "browser:purge-session-history", true);
|
|
}
|
|
|
|
let rc_js = "resource://gre/modules/RemoteController.js";
|
|
let scope = {};
|
|
Services.scriptloader.loadSubScript(rc_js, scope);
|
|
let RemoteController = scope.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 = ChromeUtils.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 =
|
|
ChromeUtils.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, data.uaBackgroundColor, data.uaColor,
|
|
data.uaSelectBackgroundColor, data.uaSelectColor,
|
|
data.selectBackgroundColor, data.selectColor, data.selectTextShadow);
|
|
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 "MozApplicationManifest":
|
|
this._manifestURI = aMessage.data.manifest;
|
|
break;
|
|
|
|
default:
|
|
// Delegate to browser.xml.
|
|
return this._receiveMessage(aMessage);
|
|
}
|
|
return undefined;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="enableDisableCommandsRemoteOnly">
|
|
<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 != Cr.NS_ERROR_NOT_INITIALIZED) {
|
|
throw ex;
|
|
}
|
|
}
|
|
this._remoteWebNavigationImpl.canGoBack = false;
|
|
this._remoteWebNavigationImpl.canGoForward = false;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="createAboutBlankContentViewer">
|
|
<parameter name="aPrincipal"/>
|
|
<body>
|
|
<![CDATA[
|
|
// Ensure that the content process has the permissions which are
|
|
// needed to create a document with the given principal.
|
|
let permissionPrincipal =
|
|
BrowserUtils.principalWithMatchingOA(aPrincipal, this.contentPrincipal);
|
|
this.frameLoader.tabParent.transmitPermissionsForPrincipal(permissionPrincipal);
|
|
|
|
// Create the about blank content viewer in the content process
|
|
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>
|