gecko-dev/toolkit/content/widgets/browser.xml

1712 строки
60 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="browserBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="browser" extends="xul:browser" role="outerdoc">
<content clickthrough="never">
<children/>
</content>
<implementation type="application/javascript" implements="nsIObserver, nsIDOMEventListener, nsIMessageListener, nsIBrowser">
<property name="autoscrollEnabled">
<getter>
<![CDATA[
if (this.getAttribute("autoscroll") == "false")
return false;
return this.mPrefs.getBoolPref("general.autoScroll", true);
]]>
</getter>
</property>
<property name="canGoBack"
onget="return this.webNavigation.canGoBack;"
readonly="true"/>
<property name="canGoForward"
onget="return this.webNavigation.canGoForward;"
readonly="true"/>
<method name="_wrapURIChangeCall">
<parameter name="fn"/>
<body>
<![CDATA[
if (!this.isRemoteBrowser) {
this.inLoadURI = true;
try {
fn();
} finally {
this.inLoadURI = false;
}
} else {
fn();
}
]]>
</body>
</method>
<method name="goBack">
<body>
<![CDATA[
var webNavigation = this.webNavigation;
if (webNavigation.canGoBack)
this._wrapURIChangeCall(() => webNavigation.goBack());
]]>
</body>
</method>
<method name="goForward">
<body>
<![CDATA[
var webNavigation = this.webNavigation;
if (webNavigation.canGoForward)
this._wrapURIChangeCall(() => webNavigation.goForward());
]]>
</body>
</method>
<method name="reload">
<body>
<![CDATA[
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
const flags = nsIWebNavigation.LOAD_FLAGS_NONE;
this.reloadWithFlags(flags);
]]>
</body>
</method>
<method name="reloadWithFlags">
<parameter name="aFlags"/>
<body>
<![CDATA[
this.webNavigation.reload(aFlags);
]]>
</body>
</method>
<method name="stop">
<body>
<![CDATA[
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
const flags = nsIWebNavigation.STOP_ALL;
this.webNavigation.stop(flags);
]]>
</body>
</method>
<!-- throws exception for unknown schemes -->
<method name="loadURI">
<parameter name="aURI"/>
<parameter name="aReferrerURI"/>
<parameter name="aCharset"/>
<body>
<![CDATA[
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
const flags = nsIWebNavigation.LOAD_FLAGS_NONE;
this._wrapURIChangeCall(() =>
this.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset));
]]>
</body>
</method>
<!-- throws exception for unknown schemes -->
<method name="loadURIWithFlags">
<parameter name="aURI"/>
<parameter name="aFlags"/>
<parameter name="aReferrerURI"/>
<parameter name="aCharset"/>
<parameter name="aPostData"/>
<body>
<![CDATA[
if (!aURI)
aURI = "about:blank";
var aReferrerPolicy = Components.interfaces.nsIHttpChannel.REFERRER_POLICY_UNSET;
var aTriggeringPrincipal;
// Check for loadURIWithFlags(uri, { ... });
var params = arguments[1];
if (params && typeof(params) == "object") {
aFlags = params.flags;
aReferrerURI = params.referrerURI;
if ("referrerPolicy" in params) {
aReferrerPolicy = params.referrerPolicy;
}
if ("triggeringPrincipal" in params) {
aTriggeringPrincipal = params.triggeringPrincipal;
}
aCharset = params.charset;
aPostData = params.postData;
}
this._wrapURIChangeCall(() =>
this.webNavigation.loadURIWithOptions(
aURI, aFlags, aReferrerURI, aReferrerPolicy,
aPostData, null, null, aTriggeringPrincipal));
]]>
</body>
</method>
<method name="goHome">
<body>
<![CDATA[
try {
this.loadURI(this.homePage);
} catch (e) {
}
]]>
</body>
</method>
<property name="homePage">
<getter>
<![CDATA[
var uri;
if (this.hasAttribute("homepage"))
uri = this.getAttribute("homepage");
else
uri = "http://www.mozilla.org/"; // widget pride
return uri;
]]>
</getter>
<setter>
<![CDATA[
this.setAttribute("homepage", val);
return val;
]]>
</setter>
</property>
<method name="gotoIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(aIndex));
]]>
</body>
</method>
<property name="currentURI" readonly="true">
<getter><![CDATA[
if (this.webNavigation) {
return this.webNavigation.currentURI;
}
return null;
]]>
</getter>
</property>
<!--
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.docShell.setCurrentURI(aURI);
]]></body>
</method>
<property name="documentURI"
onget="return this.contentDocument.documentURIObject;"
readonly="true"/>
<property name="documentContentType"
onget="return this.contentDocument ? this.contentDocument.contentType : null;"
readonly="true"/>
<property name="preferences"
onget="return this.mPrefs.QueryInterface(Components.interfaces.nsIPrefService);"
readonly="true"/>
<!--
Weak reference to an optional frame loader that can be used to influence
process selection for this browser.
See nsIBrowser.sameProcessAsFrameLoader.
-->
<field name="_sameProcessAsFrameLoader">null</field>
<property name="sameProcessAsFrameLoader">
<getter><![CDATA[
return this._sameProcessAsFrameLoader && this._sameProcessAsFrameLoader.get();
]]></getter>
<setter><![CDATA[
this._sameProcessAsFrameLoader = Components.utils.getWeakReference(val);
]]></setter>
</property>
<field name="_docShell">null</field>
<property name="docShell" readonly="true">
<getter><![CDATA[
if (this._docShell)
return this._docShell;
let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
if (!frameLoader)
return null;
this._docShell = frameLoader.docShell;
return this._docShell;
]]></getter>
</property>
<field name="_loadContext">null</field>
<property name="loadContext" readonly="true">
<getter><![CDATA[
if (this._loadContext)
return this._loadContext;
let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
if (!frameLoader)
return null;
this._loadContext = frameLoader.loadContext;
return this._loadContext;
]]></getter>
</property>
<property name="autoCompletePopup"
onget="return document.getElementById(this.getAttribute('autocompletepopup'))"
readonly="true"/>
<property name="dateTimePicker"
onget="return document.getElementById(this.getAttribute('datetimepicker'))"
readonly="true"/>
<property name="docShellIsActive">
<getter>
<![CDATA[
return this.docShell && this.docShell.isActive;
]]>
</getter>
<setter>
<![CDATA[
if (this.docShell)
return this.docShell.isActive = val;
return false;
]]>
</setter>
</property>
<method name="preserveLayers">
<parameter name="preserve"/>
<body>
// Only useful for remote browsers.
</body>
</method>
<property name="imageDocument"
readonly="true">
<getter>
<![CDATA[
var document = this.contentDocument;
if (!document || !(document instanceof Components.interfaces.nsIImageDocument))
return null;
try {
return {width: document.imageRequest.image.width, height: document.imageRequest.image.height };
} catch (e) {}
return null;
]]>
</getter>
</property>
<property name="isRemoteBrowser"
onget="return (this.getAttribute('remote') == 'true');"
readonly="true"/>
<property name="remoteType"
readonly="true">
<getter>
<![CDATA[
if (!this.isRemoteBrowser) {
return null;
}
let remoteType = this.getAttribute("remoteType");
if (remoteType) {
return remoteType;
}
let E10SUtils = Components.utils.import("resource://gre/modules/E10SUtils.jsm", {}).E10SUtils;
return E10SUtils.DEFAULT_REMOTE_TYPE;
]]>
</getter>
</property>
<property name="messageManager"
readonly="true">
<getter>
<![CDATA[
var owner = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
if (!owner.frameLoader) {
return null;
}
return owner.frameLoader.messageManager;
]]>
</getter>
</property>
<field name="_webNavigation">null</field>
<property name="webNavigation"
readonly="true">
<getter>
<![CDATA[
if (!this._webNavigation) {
if (!this.docShell) {
return null;
}
this._webNavigation = this.docShell.QueryInterface(Components.interfaces.nsIWebNavigation);
}
return this._webNavigation;
]]>
</getter>
</property>
<field name="_webBrowserFind">null</field>
<property name="webBrowserFind"
readonly="true">
<getter>
<![CDATA[
if (!this._webBrowserFind)
this._webBrowserFind = this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebBrowserFind);
return this._webBrowserFind;
]]>
</getter>
</property>
<method name="getTabBrowser">
<body>
<![CDATA[
for (let node = this.parentNode; node instanceof Element; node = node.parentNode) {
if (node.localName == "tabbrowser")
return node;
}
return null;
]]>
</body>
</method>
<field name="_finder">null</field>
<property name="finder" readonly="true">
<getter><![CDATA[
if (!this._finder) {
if (!this.docShell)
return null;
let Finder = Components.utils.import("resource://gre/modules/Finder.jsm", {}).Finder;
this._finder = new Finder(this.docShell);
}
return this._finder;
]]></getter>
</property>
<field name="_fastFind">null</field>
<property name="fastFind" readonly="true">
<getter><![CDATA[
if (!this._fastFind) {
if (!("@mozilla.org/typeaheadfind;1" in Components.classes))
return null;
var tabBrowser = this.getTabBrowser();
if (tabBrowser && "fastFind" in tabBrowser)
return this._fastFind = tabBrowser.fastFind;
if (!this.docShell)
return null;
this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
.createInstance(Components.interfaces.nsITypeAheadFind);
this._fastFind.init(this.docShell);
}
return this._fastFind;
]]></getter>
</property>
<property name="outerWindowID" readonly="true">
<getter><![CDATA[
return this.contentWindow
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.outerWindowID;
]]></getter>
</property>
<property name="innerWindowID" readonly="true">
<getter><![CDATA[
try {
return this.contentWindow
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.currentInnerWindowID;
} catch (e) {
if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
throw e;
}
return null;
}
]]></getter>
</property>
<field name="_lastSearchString">null</field>
<property name="webProgress"
readonly="true"
onget="return this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);"/>
<field name="_contentWindow">null</field>
<property name="contentWindow"
readonly="true"
onget="return this._contentWindow || (this._contentWindow = this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow));"/>
<property name="contentWindowAsCPOW"
readonly="true"
onget="return this.contentWindow;"/>
<property name="sessionHistory"
onget="return this.webNavigation.sessionHistory;"
readonly="true"/>
<property name="markupDocumentViewer"
onget="return this.docShell.contentViewer;"
readonly="true"/>
<property name="contentViewerEdit"
onget="return this.docShell.contentViewer.QueryInterface(Components.interfaces.nsIContentViewerEdit);"
readonly="true"/>
<property name="contentViewerFile"
onget="return this.docShell.contentViewer.QueryInterface(Components.interfaces.nsIContentViewerFile);"
readonly="true"/>
<property name="contentDocument"
onget="return this.webNavigation.document;"
readonly="true"/>
<property name="contentDocumentAsCPOW"
onget="return this.contentDocument;"
readonly="true"/>
<property name="contentTitle"
onget="return this.contentDocument.title;"
readonly="true"/>
<property name="characterSet"
onget="return this.docShell.charset;">
<setter><![CDATA[
this.docShell.charset = val;
this.docShell.gatherCharsetMenuTelemetry();
]]></setter>
</property>
<property name="mayEnableCharacterEncodingMenu"
onget="return this.docShell.mayEnableCharacterEncodingMenu;"
readonly="true"/>
<property name="contentPrincipal"
onget="return this.contentDocument.nodePrincipal;"
readonly="true"/>
<property name="showWindowResizer"
onset="if (val) this.setAttribute('showresizer', 'true');
else this.removeAttribute('showresizer');
return val;"
onget="return this.getAttribute('showresizer') == 'true';"/>
<property name="manifestURI"
readonly="true">
<getter><![CDATA[
return this.contentDocument.documentElement &&
this.contentDocument.documentElement.getAttribute("manifest");
]]></getter>
</property>
<property name="fullZoom">
<getter><![CDATA[
return this.markupDocumentViewer.fullZoom;
]]></getter>
<setter><![CDATA[
this.markupDocumentViewer.fullZoom = val;
]]></setter>
</property>
<property name="textZoom">
<getter><![CDATA[
return this.markupDocumentViewer.textZoom;
]]></getter>
<setter><![CDATA[
this.markupDocumentViewer.textZoom = val;
]]></setter>
</property>
<property name="effectiveTextZoom"
readonly="true">
<getter><![CDATA[
return this.markupDocumentViewer.effectiveTextZoom;
]]></getter>
</property>
<property name="isSyntheticDocument">
<getter><![CDATA[
return this.contentDocument.mozSyntheticDocument;
]]></getter>
</property>
<property name="hasContentOpener">
<getter><![CDATA[
return !!this.contentWindow.opener;
]]></getter>
</property>
<field name="mPrefs" readonly="true">
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
</field>
<field name="_mStrBundle">null</field>
<property name="mStrBundle">
<getter>
<![CDATA[
if (!this._mStrBundle) {
// need to create string bundle manually instead of using <xul:stringbundle/>
// see bug 63370 for details
this._mStrBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle("chrome://global/locale/browser.properties");
}
return this._mStrBundle;
]]></getter>
</property>
<method name="addProgressListener">
<parameter name="aListener"/>
<parameter name="aNotifyMask"/>
<body>
<![CDATA[
if (!aNotifyMask) {
aNotifyMask = Components.interfaces.nsIWebProgress.NOTIFY_ALL;
}
this.webProgress.addProgressListener(aListener, aNotifyMask);
]]>
</body>
</method>
<method name="removeProgressListener">
<parameter name="aListener"/>
<body>
<![CDATA[
this.webProgress.removeProgressListener(aListener);
]]>
</body>
</method>
<method name="findChildShell">
<parameter name="aDocShell"/>
<parameter name="aSoughtURI"/>
<body>
<![CDATA[
if (aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation)
.currentURI.spec == aSoughtURI.spec)
return aDocShell;
var node = aDocShell.QueryInterface(
Components.interfaces.nsIDocShellTreeItem);
for (var i = 0; i < node.childCount; ++i) {
var docShell = node.getChildAt(i);
docShell = this.findChildShell(docShell, aSoughtURI);
if (docShell)
return docShell;
}
return null;
]]>
</body>
</method>
<method name="onPageHide">
<parameter name="aEvent"/>
<body>
<![CDATA[
// Delete the feeds cache if we're hiding the topmost page
// (as opposed to one of its iframes).
if (this.feeds && aEvent.target == this.contentDocument)
this.feeds = null;
if (!this.docShell || !this.fastFind)
return;
var tabBrowser = this.getTabBrowser();
if (!tabBrowser || !("fastFind" in tabBrowser) ||
tabBrowser.selectedBrowser == this)
this.fastFind.setDocShell(this.docShell);
]]>
</body>
</method>
<method name="updateBlockedPopups">
<body>
<![CDATA[
let event = document.createEvent("Events");
event.initEvent("DOMUpdatePageReport", true, true);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="retrieveListOfBlockedPopups">
<body>
<![CDATA[
this.messageManager.sendAsyncMessage("PopupBlocking:GetBlockedPopupList", null);
return new Promise(resolve => {
let self = this;
this.messageManager.addMessageListener("PopupBlocking:ReplyGetBlockedPopupList",
function replyReceived(msg) {
self.messageManager.removeMessageListener("PopupBlocking:ReplyGetBlockedPopupList",
replyReceived);
resolve(msg.data.popupData);
}
);
});
]]>
</body>
</method>
<method name="unblockPopup">
<parameter name="aPopupIndex"/>
<body><![CDATA[
this.messageManager.sendAsyncMessage("PopupBlocking:UnblockPopup",
{index: aPopupIndex});
]]></body>
</method>
<field name="blockedPopups">null</field>
<!-- Obsolete name for blockedPopups. Used by android. -->
<property name="pageReport"
onget="return this.blockedPopups;"
readonly="true"/>
<method name="audioPlaybackStarted">
<body>
<![CDATA[
if (this._audioMuted) {
return;
}
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackStarted", true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="audioPlaybackStopped">
<body>
<![CDATA[
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackStopped", true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
<!--
When the pref "media.block-autoplay-until-in-foreground" is on, all
windows would be blocked by default in gecko. The "block" means the
autoplay media can't be started in that tab unless the tab has been
visited or resumed by tab's play tab icon. Since the window is blocked
by default, there's no method to signal entering that state.
(1) If the window is resumed, no matter it has autoplay media or not
- will call mediaBlockStopped()
(2) If the window has blocked any autoplay media
- will call activeMediaBlockStarted()
(3) If the window has resumed any autoplay media
- will call activeMediaBlockStopped()
-->
<method name="activeMediaBlockStarted">
<body>
<![CDATA[
this._hasAnyPlayingMediaBeenBlocked = true;
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackBlockStarted", true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="activeMediaBlockStopped">
<body>
<![CDATA[
if (!this._hasAnyPlayingMediaBeenBlocked) {
return;
}
this._hasAnyPlayingMediaBeenBlocked = false;
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="mediaBlockStopped">
<body>
<![CDATA[
this._mediaBlocked = false;
]]>
</body>
</method>
<field name="_audioMuted">false</field>
<property name="audioMuted"
onget="return this._audioMuted;"
readonly="true"/>
<field name="_mediaBlocked">true</field>
<property name="mediaBlocked" readonly="true">
<getter>
<![CDATA[
if (this.mPrefs.getBoolPref("media.block-autoplay-until-in-foreground", true)) {
return this._mediaBlocked;
}
return false;
]]>
</getter>
</property>
<field name="_hasAnyPlayingMediaBeenBlocked">false</field>
<method name="mute">
<parameter name="transientState"/>
<body>
<![CDATA[
if (!transientState) {
this._audioMuted = true;
}
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "mute"});
]]>
</body>
</method>
<method name="unmute">
<body>
<![CDATA[
this._audioMuted = false;
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "unmute"});
]]>
</body>
</method>
<method name="pauseMedia">
<parameter name="disposable"/>
<body>
<![CDATA[
let suspendedReason;
if (disposable) {
suspendedReason = "mediaControlPaused";
} else {
suspendedReason = "lostAudioFocusTransiently";
}
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: suspendedReason});
]]>
</body>
</method>
<method name="stopMedia">
<body>
<![CDATA[
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "mediaControlStopped"});
]]>
</body>
</method>
<method name="resumeMedia">
<body>
<![CDATA[
this._mediaBlocked = false;
this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "resumeMedia"});
if (this._hasAnyPlayingMediaBeenBlocked) {
this._hasAnyPlayingMediaBeenBlocked = false;
let event = document.createEvent("Events");
event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
this.dispatchEvent(event);
}
]]>
</body>
</method>
<!--
Only send the message "Browser:UnselectedTabHover" when someone requests
for the message, which can reduce non-necessary communication.
-->
<field name="_shouldSendUnselectedTabHover">false</field>
<field name="_unselectedTabHoverMessageListenerCount">0</field>
<property name="shouldHandleUnselectedTabHover"
onget="return this._shouldSendUnselectedTabHover;"
readonly="true"/>
<method name="unselectedTabHover">
<parameter name="hovered"/>
<body>
<![CDATA[
if (!this._shouldSendUnselectedTabHover) {
return;
}
this.messageManager.sendAsyncMessage("Browser:UnselectedTabHover",
{ hovered });
]]>
</body>
</method>
<property name="securityUI">
<getter>
<![CDATA[
if (!this.docShell.securityUI) {
const SECUREBROWSERUI_CONTRACTID = "@mozilla.org/secure_browser_ui;1";
if (!this.hasAttribute("disablesecurity") &&
SECUREBROWSERUI_CONTRACTID in Components.classes) {
var securityUI = Components.classes[SECUREBROWSERUI_CONTRACTID]
.createInstance(Components.interfaces.nsISecureBrowserUI);
securityUI.init(this.contentWindow);
}
}
return this.docShell.securityUI;
]]>
</getter>
<setter>
<![CDATA[
this.docShell.securityUI = val;
]]>
</setter>
</property>
<field name="urlbarChangeTracker">
({
_startedLoadSinceLastUserTyping: false,
startedLoad() {
this._startedLoadSinceLastUserTyping = true;
},
finishedLoad() {
this._startedLoadSinceLastUserTyping = false;
},
userTyped() {
this._startedLoadSinceLastUserTyping = false;
},
})
</field>
<method name="didStartLoadSinceLastUserTyping">
<body><![CDATA[
return !this.inLoadURI &&
this.urlbarChangeTracker._startedLoadSinceLastUserTyping;
]]></body>
</method>
<field name="_userTypedValue">
null
</field>
<property name="userTypedValue"
onget="return this._userTypedValue;">
<setter><![CDATA[
this.urlbarChangeTracker.userTyped();
this._userTypedValue = val;
return val;
]]></setter>
</property>
<field name="mFormFillAttached">
false
</field>
<field name="isShowingMessage">
false
</field>
<field name="droppedLinkHandler">
null
</field>
<field name="mIconURL">null</field>
<!-- This is managed by the tabbrowser -->
<field name="lastURI">null</field>
<field name="mDestroyed">false</field>
<constructor>
<![CDATA[
try {
// |webNavigation.sessionHistory| will have been set by the frame
// loader when creating the docShell as long as this xul:browser
// doesn't have the 'disablehistory' attribute set.
if (this.docShell && this.webNavigation.sessionHistory) {
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this, "browser:purge-session-history", true);
// enable global history if we weren't told otherwise
if (!this.hasAttribute("disableglobalhistory") && !this.isRemoteBrowser) {
try {
this.docShell.useGlobalHistory = true;
} catch (ex) {
// This can occur if the Places database is locked
Components.utils.reportError("Error enabling browser global history: " + ex);
}
}
}
} catch (e) {
Components.utils.reportError(e);
}
try {
// Ensures the securityUI is initialized.
var securityUI = this.securityUI; // eslint-disable-line no-unused-vars
} catch (e) {
}
// tabbrowser.xml sets "sameProcessAsFrameLoader" as a direct property
// on some browsers before they are put into a DOM (and get a
// binding). This hack makes sure that we hold a weak reference to
// the other browser (and go through the proper getter and setter).
if (this.hasOwnProperty("sameProcessAsFrameLoader")) {
var sameProcessAsFrameLoader = this.sameProcessAsFrameLoader;
delete this.sameProcessAsFrameLoader;
this.sameProcessAsFrameLoader = sameProcessAsFrameLoader;
}
if (!this.isRemoteBrowser) {
this.addEventListener("pagehide", this.onPageHide, true);
}
if (this.messageManager) {
this.messageManager.addMessageListener("PopupBlocking:UpdateBlockedPopups", this);
this.messageManager.addMessageListener("Autoscroll:Start", this);
this.messageManager.addMessageListener("Autoscroll:Cancel", this);
this.messageManager.addMessageListener("AudioPlayback:Start", this);
this.messageManager.addMessageListener("AudioPlayback:Stop", this);
this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStart", this);
this.messageManager.addMessageListener("AudioPlayback:ActiveMediaBlockStop", this);
this.messageManager.addMessageListener("AudioPlayback:MediaBlockStop", this);
this.messageManager.addMessageListener("UnselectedTabHover:Toggle", this);
if (this.hasAttribute("selectmenulist")) {
this.messageManager.addMessageListener("Forms:ShowDropDown", this);
this.messageManager.addMessageListener("Forms:HideDropDown", this);
}
}
]]>
</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 function is overriden in remote-browser.xml, so any clean-up that
also applies to browser.isRemoteBrowser = true must be duplicated there. -->
<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;
if (this.docShell && this.webNavigation.sessionHistory) {
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
try {
os.removeObserver(this, "browser:purge-session-history");
} catch (ex) {
// It's not clear why this sometimes throws an exception.
}
}
this._fastFind = null;
this._webBrowserFind = null;
// The feeds cache can keep the document inside this browser alive.
this.feeds = null;
this.lastURI = null;
if (!this.isRemoteBrowser) {
this.removeEventListener("pagehide", this.onPageHide, true);
}
if (this._autoScrollNeedsCleanup) {
// we polluted the global scope, so clean it up
this._autoScrollPopup.remove();
}
]]>
</body>
</method>
<!--
We call this _receiveMessage (and alias receiveMessage to it) so that
bindings that inherit from this one can delegate to it.
-->
<method name="_receiveMessage">
<parameter name="aMessage"/>
<body><![CDATA[
let data = aMessage.data;
switch (aMessage.name) {
case "PopupBlocking:UpdateBlockedPopups": {
this.blockedPopups = {
length: data.count,
reported: !data.freshPopup,
};
this.updateBlockedPopups();
break;
}
case "Autoscroll:Start": {
if (!this.autoscrollEnabled) {
return false;
}
this.startScroll(data.scrolldir, data.screenX, data.screenY);
if (this.isRemoteBrowser && data.scrollId != null &&
this.mPrefs.getBoolPref("apz.autoscroll.enabled", false)) {
let { tabParent } = this.frameLoader;
if (tabParent) {
// If APZ is handling the autoscroll, it may decide to cancel
// it of its own accord, so register an observer to allow it
// to notify us of that.
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this, "apz:cancel-autoscroll", true);
tabParent.startApzAutoscroll(data.screenX, data.screenY,
data.scrollId, data.presShellId);
}
// Save the IDs for later
this._autoScrollScrollId = data.scrollId;
this._autoScrollPresShellId = data.presShellId;
}
return true;
}
case "Autoscroll:Cancel":
this._autoScrollPopup.hidePopup();
break;
case "AudioPlayback:Start":
this.audioPlaybackStarted();
break;
case "AudioPlayback:Stop":
this.audioPlaybackStopped();
break;
case "AudioPlayback:ActiveMediaBlockStart":
this.activeMediaBlockStarted();
break;
case "AudioPlayback:ActiveMediaBlockStop":
this.activeMediaBlockStopped();
break;
case "AudioPlayback:MediaBlockStop":
this.mediaBlockStopped();
break;
case "UnselectedTabHover:Toggle":
this._shouldSendUnselectedTabHover = data.enable ?
++this._unselectedTabHoverMessageListenerCount > 0 :
--this._unselectedTabHoverMessageListenerCount == 0;
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;
this._selectParentHelper.populate(menulist, data.options, data.selectedIndex, this._fullZoom,
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 "Forms:HideDropDown": {
if (this._selectParentHelper) {
let menulist = document.getElementById(this.getAttribute("selectmenulist"));
this._selectParentHelper.hide(menulist, this);
}
break;
}
}
return undefined;
]]></body>
</method>
<method name="receiveMessage">
<parameter name="aMessage"/>
<body><![CDATA[
return this._receiveMessage(aMessage);
]]></body>
</method>
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aState"/>
<body>
<![CDATA[
if (aTopic == "browser:purge-session-history") {
this.purgeSessionHistory();
} else if (aTopic == "apz:cancel-autoscroll") {
if (aState == this._autoScrollScrollId) {
this._autoScrollPopup.hidePopup();
// Set this._autoScrollScrollId to null, so in stopScroll() we
// don't call stopApzAutoscroll() (since it's APZ that
// initiated the stopping).
this._autoScrollScrollId = null;
this._autoScrollPresShellId = null;
}
}
]]>
</body>
</method>
<method name="purgeSessionHistory">
<body>
<![CDATA[
this.messageManager.sendAsyncMessage("Browser:PurgeSessionHistory");
]]>
</body>
</method>
<method name="createAboutBlankContentViewer">
<parameter name="aPrincipal"/>
<body>
<![CDATA[
let principal = BrowserUtils.principalWithMatchingOA(aPrincipal, this.contentPrincipal);
this.docShell.createAboutBlankContentViewer(principal);
]]>
</body>
</method>
<field name="_AUTOSCROLL_SNAP">10</field>
<field name="_scrolling">false</field>
<field name="_startX">null</field>
<field name="_startY">null</field>
<field name="_autoScrollPopup">null</field>
<field name="_autoScrollNeedsCleanup">false</field>
<!-- These IDs identify the scroll frame being autoscrolled. -->
<field name="_autoScrollScrollId">null</field>
<field name="_autoScrollPresShellId">null</field>
<method name="stopScroll">
<body>
<![CDATA[
if (this._scrolling) {
this._scrolling = false;
window.removeEventListener("mousemove", this, true);
window.removeEventListener("mousedown", this, true);
window.removeEventListener("mouseup", this, true);
window.removeEventListener("DOMMouseScroll", this, true);
window.removeEventListener("contextmenu", this, true);
window.removeEventListener("keydown", this, true);
window.removeEventListener("keypress", this, true);
window.removeEventListener("keyup", this, true);
this.messageManager.sendAsyncMessage("Autoscroll:Stop");
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
try {
os.removeObserver(this, "apz:cancel-autoscroll");
} catch (ex) {
// It's not clear why this sometimes throws an exception
}
if (this.isRemoteBrowser && this._autoScrollScrollId != null) {
let { tabParent } = this.frameLoader;
if (tabParent) {
tabParent.stopApzAutoscroll(this._autoScrollScrollId,
this._autoScrollPresShellId);
}
this._autoScrollScrollId = null;
this._autoScrollPresShellId = null;
}
}
]]>
</body>
</method>
<method name="_createAutoScrollPopup">
<body>
<![CDATA[
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var popup = document.createElementNS(XUL_NS, "panel");
popup.className = "autoscroller";
// We set this attribute on the element so that mousemove
// events can be handled by browser-content.js.
popup.setAttribute("mousethrough", "always");
popup.setAttribute("rolluponmousewheel", "true");
popup.setAttribute("hidden", "true");
return popup;
]]>
</body>
</method>
<method name="startScroll">
<parameter name="scrolldir"/>
<parameter name="screenX"/>
<parameter name="screenY"/>
<body><![CDATA[
if (!this._autoScrollPopup) {
if (this.hasAttribute("autoscrollpopup")) {
// our creator provided a popup to share
this._autoScrollPopup = document.getElementById(this.getAttribute("autoscrollpopup"));
} else {
// we weren't provided a popup; we have to use the global scope
this._autoScrollPopup = this._createAutoScrollPopup();
document.documentElement.appendChild(this._autoScrollPopup);
this._autoScrollNeedsCleanup = true;
}
}
// we need these attributes so themers don't need to create per-platform packages
if (screen.colorDepth > 8) { // need high color for transparency
// Exclude second-rate platforms
this._autoScrollPopup.setAttribute("transparent", !/BeOS|OS\/2/.test(navigator.appVersion));
// Enable translucency on Windows and Mac
this._autoScrollPopup.setAttribute("translucent", /Win|Mac/.test(navigator.platform));
}
this._autoScrollPopup.removeAttribute("hidden");
this._autoScrollPopup.setAttribute("noautofocus", "true");
this._autoScrollPopup.setAttribute("scrolldir", scrolldir);
this._autoScrollPopup.addEventListener("popuphidden", this, true);
this._autoScrollPopup.showPopup(document.documentElement,
screenX,
screenY,
"popup", null, null);
this._ignoreMouseEvents = true;
this._scrolling = true;
this._startX = screenX;
this._startY = screenY;
window.addEventListener("mousemove", this, true);
window.addEventListener("mousedown", this, true);
window.addEventListener("mouseup", this, true);
window.addEventListener("DOMMouseScroll", this, true);
window.addEventListener("contextmenu", this, true);
window.addEventListener("keydown", this, true);
window.addEventListener("keypress", this, true);
window.addEventListener("keyup", this, true);
]]>
</body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (this._scrolling) {
switch (aEvent.type) {
case "mousemove": {
var x = aEvent.screenX - this._startX;
var y = aEvent.screenY - this._startY;
if ((x > this._AUTOSCROLL_SNAP || x < -this._AUTOSCROLL_SNAP) ||
(y > this._AUTOSCROLL_SNAP || y < -this._AUTOSCROLL_SNAP))
this._ignoreMouseEvents = false;
break;
}
case "mouseup":
case "mousedown":
case "contextmenu": {
if (!this._ignoreMouseEvents) {
// Use a timeout to prevent the mousedown from opening the popup again.
// Ideally, we could use preventDefault here, but contenteditable
// and middlemouse paste don't interact well. See bug 1188536.
setTimeout(() => this._autoScrollPopup.hidePopup(), 0);
}
this._ignoreMouseEvents = false;
break;
}
case "DOMMouseScroll": {
this._autoScrollPopup.hidePopup();
aEvent.preventDefault();
break;
}
case "popuphidden": {
this._autoScrollPopup.removeEventListener("popuphidden", this, true);
this.stopScroll();
break;
}
case "keydown": {
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
// the escape key will be processed by
// nsXULPopupManager::KeyDown and the panel will be closed.
// So, don't consume the key event here.
break;
}
// don't break here. we need to eat keydown events.
}
case "keypress":
case "keyup": {
// All keyevents should be eaten here during autoscrolling.
aEvent.stopPropagation();
aEvent.preventDefault();
break;
}
}
}
]]>
</body>
</method>
<method name="closeBrowser">
<body>
<![CDATA[
// The request comes from a XPCOM component, we'd want to redirect
// the request to tabbrowser.
let tabbrowser = this.getTabBrowser();
if (tabbrowser) {
let tab = tabbrowser.getTabForBrowser(this);
if (tab) {
tabbrowser.removeTab(tab);
return;
}
}
throw new Error("Closing a browser which was not attached to a tabbrowser is unsupported.");
]]>
</body>
</method>
<method name="swapBrowsers">
<parameter name="aOtherBrowser"/>
<parameter name="aFlags"/>
<body>
<![CDATA[
// The request comes from a XPCOM component, we'd want to redirect
// the request to tabbrowser so tabbrowser will be setup correctly,
// and it will eventually call swapDocShells.
let ourTabBrowser = this.getTabBrowser();
let otherTabBrowser = aOtherBrowser.getTabBrowser();
if (ourTabBrowser && otherTabBrowser) {
let ourTab = ourTabBrowser.getTabForBrowser(this);
let otherTab = otherTabBrowser.getTabForBrowser(aOtherBrowser);
ourTabBrowser.swapBrowsers(ourTab, otherTab, aFlags);
return;
}
// One of us is not connected to a tabbrowser, so just swap.
this.swapDocShells(aOtherBrowser);
]]>
</body>
</method>
<method name="swapDocShells">
<parameter name="aOtherBrowser"/>
<body>
<![CDATA[
if (this.isRemoteBrowser != aOtherBrowser.isRemoteBrowser)
throw new Error("Can only swap docshells between browsers in the same process.");
// Give others a chance to swap state.
// IMPORTANT: Since a swapDocShells call does not swap the messageManager
// instances attached to a browser to aOtherBrowser, others
// will need to add the message listeners to the new
// messageManager.
// This is not a bug in swapDocShells or the FrameLoader,
// merely a design decision: If message managers were swapped,
// so that no new listeners were needed, the new
// aOtherBrowser.messageManager would have listeners pointing
// to the JS global of the current browser, which would rather
// easily create leaks while swapping.
// IMPORTANT2: When the current browser element is removed from DOM,
// which is quite common after a swpDocShells call, its
// frame loader is destroyed, and that destroys the relevant
// message manager, which will remove the listeners.
let event = new CustomEvent("SwapDocShells", {"detail": aOtherBrowser});
this.dispatchEvent(event);
event = new CustomEvent("SwapDocShells", {"detail": this});
aOtherBrowser.dispatchEvent(event);
// We need to swap fields that are tied to our docshell or related to
// the loaded page
// Fields which are built as a result of notifactions (pageshow/hide,
// DOMLinkAdded/Removed, onStateChange) should not be swapped here,
// because these notifications are dispatched again once the docshells
// are swapped.
var fieldsToSwap = [
"_docShell",
"_webBrowserFind",
"_contentWindow",
"_webNavigation"
];
if (this.isRemoteBrowser) {
fieldsToSwap.push(...[
"_remoteWebNavigation",
"_remoteWebNavigationImpl",
"_remoteWebProgressManager",
"_remoteWebProgress",
"_remoteFinder",
"_securityUI",
"_documentURI",
"_documentContentType",
"_contentTitle",
"_characterSet",
"_mayEnableCharacterEncodingMenu",
"_contentPrincipal",
"_imageDocument",
"_fullZoom",
"_textZoom",
"_isSyntheticDocument",
"_innerWindowID",
"_manifestURI",
]);
}
var ourFieldValues = {};
var otherFieldValues = {};
for (let field of fieldsToSwap) {
ourFieldValues[field] = this[field];
otherFieldValues[field] = aOtherBrowser[field];
}
if (window.PopupNotifications)
PopupNotifications._swapBrowserNotifications(aOtherBrowser, this);
try {
this.swapFrameLoaders(aOtherBrowser);
} catch (ex) {
// This may not be implemented for browser elements that are not
// attached to a BrowserDOMWindow.
}
for (let field of fieldsToSwap) {
this[field] = otherFieldValues[field];
aOtherBrowser[field] = ourFieldValues[field];
}
if (!this.isRemoteBrowser) {
// Null the current nsITypeAheadFind instances so that they're
// lazily re-created on access. We need to do this because they
// might have attached the wrong docShell.
this._fastFind = aOtherBrowser._fastFind = null;
} else {
// Rewire the remote listeners
this._remoteWebNavigationImpl.swapBrowser(this);
aOtherBrowser._remoteWebNavigationImpl.swapBrowser(aOtherBrowser);
if (this._remoteWebProgressManager && aOtherBrowser._remoteWebProgressManager) {
this._remoteWebProgressManager.swapBrowser(this);
aOtherBrowser._remoteWebProgressManager.swapBrowser(aOtherBrowser);
}
if (this._remoteFinder)
this._remoteFinder.swapBrowser(this);
if (aOtherBrowser._remoteFinder)
aOtherBrowser._remoteFinder.swapBrowser(aOtherBrowser);
}
event = new CustomEvent("EndSwapDocShells", {"detail": aOtherBrowser});
this.dispatchEvent(event);
event = new CustomEvent("EndSwapDocShells", {"detail": this});
aOtherBrowser.dispatchEvent(event);
]]>
</body>
</method>
<method name="getInPermitUnload">
<parameter name="aCallback"/>
<body>
<![CDATA[
if (!this.docShell || !this.docShell.contentViewer) {
aCallback(false);
return;
}
aCallback(this.docShell.contentViewer.inPermitUnload);
]]>
</body>
</method>
<method name="permitUnload">
<body>
<![CDATA[
if (!this.docShell || !this.docShell.contentViewer) {
return {permitUnload: true, timedOut: false};
}
return {permitUnload: this.docShell.contentViewer.permitUnload(), timedOut: false};
]]>
</body>
</method>
<method name="print">
<parameter name="aOuterWindowID"/>
<parameter name="aPrintSettings"/>
<parameter name="aPrintProgressListener"/>
<body>
<![CDATA[
var owner = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
if (!owner.frameLoader) {
throw Components.Exception("No frame loader.",
Components.results.NS_ERROR_FAILURE);
}
owner.frameLoader.print(aOuterWindowID, aPrintSettings,
aPrintProgressListener);
]]>
</body>
</method>
<method name="dropLinks">
<parameter name="aLinksCount"/>
<parameter name="aLinks"/>
<parameter name="aTriggeringPrincipal"/>
<body><![CDATA[
if (!this.droppedLinkHandler) {
return false;
}
let links = [];
for (let i = 0; i < aLinksCount; i += 3) {
links.push({
url: aLinks[i],
name: aLinks[i + 1],
type: aLinks[i + 2],
});
}
this.droppedLinkHandler(null, links, aTriggeringPrincipal);
return true;
]]></body>
</method>
</implementation>
<handlers>
<handler event="keypress" keycode="VK_F7" group="system">
<![CDATA[
if (event.defaultPrevented || !event.isTrusted)
return;
const kPrefShortcutEnabled = "accessibility.browsewithcaret_shortcut.enabled";
const kPrefWarnOnEnable = "accessibility.warn_on_browsewithcaret";
const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
var isEnabled = this.mPrefs.getBoolPref(kPrefShortcutEnabled);
if (!isEnabled)
return;
// Toggle browse with caret mode
var browseWithCaretOn = this.mPrefs.getBoolPref(kPrefCaretBrowsingOn, false);
var warn = this.mPrefs.getBoolPref(kPrefWarnOnEnable, true);
if (warn && !browseWithCaretOn) {
var checkValue = {value: false};
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var buttonPressed = promptService.confirmEx(window,
this.mStrBundle.GetStringFromName("browsewithcaret.checkWindowTitle"),
this.mStrBundle.GetStringFromName("browsewithcaret.checkLabel"),
// Make "No" the default:
promptService.STD_YES_NO_BUTTONS | promptService.BUTTON_POS_1_DEFAULT,
null, null, null, this.mStrBundle.GetStringFromName("browsewithcaret.checkMsg"),
checkValue);
if (buttonPressed != 0) {
if (checkValue.value) {
try {
this.mPrefs.setBoolPref(kPrefShortcutEnabled, false);
} catch (ex) {
}
}
return;
}
if (checkValue.value) {
try {
this.mPrefs.setBoolPref(kPrefWarnOnEnable, false);
} catch (ex) {
}
}
}
// Toggle the pref
try {
this.mPrefs.setBoolPref(kPrefCaretBrowsingOn, !browseWithCaretOn);
} catch (ex) {
}
]]>
</handler>
<handler event="dragover" group="system">
<![CDATA[
if (!this.droppedLinkHandler || event.defaultPrevented)
return;
// For drags that appear to be internal text (for example, tab drags),
// set the dropEffect to 'none'. This prevents the drop even if some
// other listener cancelled the event.
var types = event.dataTransfer.types;
if (types.includes("text/x-moz-text-internal") && !types.includes("text/plain")) {
event.dataTransfer.dropEffect = "none";
event.stopPropagation();
event.preventDefault();
}
// No need to handle "dragover" in e10s, since nsDocShellTreeOwner.cpp in the child process
// handles that case using "@mozilla.org/content/dropped-link-handler;1" service.
if (this.isRemoteBrowser)
return;
let linkHandler = Components.classes["@mozilla.org/content/dropped-link-handler;1"].
getService(Components.interfaces.nsIDroppedLinkHandler);
if (linkHandler.canDropLink(event, false))
event.preventDefault();
]]>
</handler>
<handler event="drop" group="system">
<![CDATA[
// No need to handle "drop" in e10s, since nsDocShellTreeOwner.cpp in the child process
// handles that case using "@mozilla.org/content/dropped-link-handler;1" service.
if (!this.droppedLinkHandler || event.defaultPrevented || this.isRemoteBrowser)
return;
let linkHandler = Components.classes["@mozilla.org/content/dropped-link-handler;1"].
getService(Components.interfaces.nsIDroppedLinkHandler);
try {
// Pass true to prevent the dropping of javascript:/data: URIs
var links = linkHandler.dropLinks(event, true);
} catch (ex) {
return;
}
if (links.length) {
let triggeringPrincipal = linkHandler.getTriggeringPrincipal(event);
this.droppedLinkHandler(event, links, triggeringPrincipal);
}
]]>
</handler>
</handlers>
</binding>
</bindings>