diff options
Diffstat (limited to 'toolkit/content/widgets/browser.xml')
-rw-r--r-- | toolkit/content/widgets/browser.xml | 1571 |
1 files changed, 1571 insertions, 0 deletions
diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml new file mode 100644 index 000000000..a5f37b62a --- /dev/null +++ b/toolkit/content/widgets/browser.xml @@ -0,0 +1,1571 @@ +<?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; + + var enabled = true; + try { + enabled = this.mPrefs.getBoolPref("general.autoScroll"); + } + catch (ex) { + } + + return enabled; + ]]> + </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_DEFAULT; + + // 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; + } + aCharset = params.charset; + aPostData = params.postData; + } + + this._wrapURIChangeCall(() => + this.webNavigation.loadURIWithOptions( + aURI, aFlags, aReferrerURI, aReferrerPolicy, + aPostData, null, null)); + ]]> + </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 the related browser (see + nsIBrowser.getRelatedBrowser). + --> + <field name="_relatedBrowser">null</field> + <property name="relatedBrowser"> + <getter><![CDATA[ + return this._relatedBrowser && this._relatedBrowser.get(); + ]]></getter> + <setter><![CDATA[ + this._relatedBrowser = Cu.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> + + <method name="makePrerenderedBrowserActive"> + <body> + <![CDATA[ + let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader; + if (frameLoader) { + frameLoader.makePrerenderedLoaderActive(); + } + ]]> + </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="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[ + var tabBrowser = this.parentNode; + while (tabBrowser && tabBrowser.localName != "tabbrowser") + tabBrowser = tabBrowser.parentNode; + return tabBrowser; + ]]> + </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="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[ + let event = document.createEvent("Events"); + event.initEvent("DOMAudioPlaybackStarted", true, false); + this.dispatchEvent(event); + if (this._audioBlocked) { + this._audioBlocked = false; + event = document.createEvent("Events"); + event.initEvent("DOMAudioPlaybackBlockStopped", 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> + + <method name="audioPlaybackBlocked"> + <body> + <![CDATA[ + this._audioBlocked = true; + let event = document.createEvent("Events"); + event.initEvent("DOMAudioPlaybackBlockStarted", true, false); + this.dispatchEvent(event); + ]]> + </body> + </method> + + <field name="_audioMuted">false</field> + <property name="audioMuted" + onget="return this._audioMuted;" + readonly="true"/> + + + <field name="_audioBlocked">false</field> + <property name="audioBlocked" + onget="return this._audioBlocked;" + readonly="true"/> + + <method name="mute"> + <body> + <![CDATA[ + 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="blockMedia"> + <body> + <![CDATA[ + this._audioBlocked = true; + this.messageManager.sendAsyncMessage("AudioPlayback", + {type: "blockInactivePageMedia"}); + ]]> + </body> + </method> + + <method name="resumeMedia"> + <body> + <![CDATA[ + this._audioBlocked = false; + this.messageManager.sendAsyncMessage("AudioPlayback", + {type: "resumeMedia"}); + let event = document.createEvent("Events"); + event.initEvent("DOMAudioPlaybackBlockStopped", true, false); + this.dispatchEvent(event); + ]]> + </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> + + <!-- increases or decreases the browser's network priority --> + <method name="adjustPriority"> + <parameter name="adjustment"/> + <body><![CDATA[ + let loadGroup = this.webNavigation.QueryInterface(Components.interfaces.nsIDocumentLoader) + .loadGroup.QueryInterface(Components.interfaces.nsISupportsPriority); + loadGroup.adjustPriority(adjustment); + ]]></body> + </method> + + <!-- sets the browser's network priority to a discrete value --> + <method name="setPriority"> + <parameter name="priority"/> + <body><![CDATA[ + let loadGroup = this.webNavigation.QueryInterface(Components.interfaces.nsIDocumentLoader) + .loadGroup.QueryInterface(Components.interfaces.nsISupportsPriority); + loadGroup.priority = priority; + ]]></body> + </method> + + <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 { + var securityUI = this.securityUI; + } + catch (e) { + } + + // XXX tabbrowser.xml sets "relatedBrowser" 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("relatedBrowser")) { + var relatedBrowser = this.relatedBrowser; + delete this.relatedBrowser; + this.relatedBrowser = relatedBrowser; + } + + 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:Block", 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[ + 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.parentNode.removeChild(this._autoScrollPopup); + } + ]]> + </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); + return true; + } + case "Autoscroll:Cancel": + this._autoScrollPopup.hidePopup(); + break; + case "AudioPlayback:Start": + this.audioPlaybackStarted(); + break; + case "AudioPlayback:Stop": + this.audioPlaybackStopped(); + break; + case "AudioPlayback:Block": + this.audioPlaybackBlocked(); + 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") + return; + + this.purgeSessionHistory(); + ]]> + </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> + + <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"); + } + ]]> + </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"); + 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.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(); + event.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"/> + <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 tabbrowser = this.getTabBrowser(); + if (tabbrowser) { + let tab = tabbrowser.getTabForBrowser(this); + if (tab) { + tabbrowser.swapBrowsers(tab, aOtherBrowser); + return; + } + } + + // If we're not attached to a tabbrowser, 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", + "_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"/> + <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); + 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 = false; + var warn = true; + + try { + warn = this.mPrefs.getBoolPref(kPrefWarnOnEnable); + } catch (ex) { + } + + try { + browseWithCaretOn = this.mPrefs.getBoolPref(kPrefCaretBrowsingOn); + } catch (ex) { + } + 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 name = { }; + 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) { + this.droppedLinkHandler(event, links); + } + ]]> + </handler> + </handlers> + + </binding> + +</bindings> |