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