toolkit/content/widgets/browser.xml
author Neil Deakin <neil@mozilla.com>
Wed, 02 May 2018 10:32:54 -0400
changeset 416715 76855675e353231dd10ec0a1f8ca7c1211b281f1
parent 416128 e872787876a553d1d210a325e31d7e236c4c0857
child 418236 510dfe436e4ecbfcecb5e01d5b476d749094a791
permissions -rw-r--r--
Bug 1457763, regression, autoscroll popup should be opened with openPopupAtScreen, r=paolo

<?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">
    <content clickthrough="never">
      <children/>
    </content>
    <implementation type="application/javascript" implements="nsIObserver, 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 = Ci.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 = Ci.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="aParams"/>
        <body>
          <![CDATA[
            if (!aURI) {
              aURI = "about:blank";
            }

            let {
              flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
              referrerURI,
              referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
              triggeringPrincipal,
              postData,
            } = aParams || {};

            this._wrapURIChangeCall(() =>
              this.webNavigation.loadURIWithOptions(
                  aURI, flags, referrerURI, referrerPolicy,
                  postData, null, null, triggeringPrincipal));
          ]]>
        </body>
      </method>

      <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"/>

      <!--
        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 = 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;
          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;
          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="renderLayers">
        <getter>
          <![CDATA[
            return this.docShellIsActive;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            return this.docShellIsActive = val;
          ]]>
        </setter>
      </property>

      <property name="hasLayers" readonly="true">
        <getter>
          <![CDATA[
            return this.docShellIsActive;
          ]]>
        </getter>
      </property>

      <property name="imageDocument"
                readonly="true">
        <getter>
          <![CDATA[
            var document = this.contentDocument;
            if (!document || !(document instanceof Ci.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 = ChromeUtils.import("resource://gre/modules/E10SUtils.jsm", {}).E10SUtils;
            return E10SUtils.DEFAULT_REMOTE_TYPE;
          ]]>
        </getter>
      </property>

      <property name="messageManager"
                readonly="true">
        <getter>
          <![CDATA[
            if (this.frameLoader) {
              return this.frameLoader.messageManager;
            }
            return null;
          ]]>
        </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(Ci.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(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebBrowserFind);
          return this._webBrowserFind;
        ]]>
        </getter>
      </property>

      <method name="getTabBrowser">
        <body>
          <![CDATA[
            if (this.ownerGlobal.gBrowser &&
                this.ownerGlobal.gBrowser.getTabForBrowser &&
                this.ownerGlobal.gBrowser.getTabForBrowser(this)) {
              return this.ownerGlobal.gBrowser;
            }
            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 = ChromeUtils.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 Cc))
              return null;

            var tabBrowser = this.getTabBrowser();
            if (tabBrowser && "fastFind" in tabBrowser)
              return this._fastFind = tabBrowser.fastFind;

            if (!this.docShell)
              return null;

            this._fastFind = Cc["@mozilla.org/typeaheadfind;1"]
                               .createInstance(Ci.nsITypeAheadFind);
            this._fastFind.init(this.docShell);
          }
          return this._fastFind;
        ]]></getter>
      </property>

      <property name="outerWindowID" readonly="true">
        <getter><![CDATA[
          return this.contentWindow
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils)
                     .outerWindowID;
        ]]></getter>
      </property>

      <property name="innerWindowID" readonly="true">
        <getter><![CDATA[
          try {
            return this.contentWindow
                       .QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.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="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="contentRequestContextID" readonly="true">
        <getter><![CDATA[
          try {
            return this.contentDocument.documentLoadGroup
                       .requestContextID;
          } catch (e) {
            return null;
          }
        ]]></getter>
      </property>

      <property name="showWindowResizer"
                onset="if (val) this.setAttribute('showresizer', 'true');
                       else this.removeAttribute('showresizer');
                       return val;"
                onget="return this.getAttribute('showresizer') == 'true';"/>

      <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">
        Cc["@mozilla.org/preferences-service;1"].getService(Ci.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 = Cc["@mozilla.org/intl/stringbundle;1"]
                                 .getService(Ci.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 = Ci.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="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("DOMUpdateBlockedPopups", 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>

      <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) &&
                this.mPrefs.getBoolPref("media.autoplay.enabled", 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 Cc) {
                var securityUI = Cc[SECUREBROWSERUI_CONTRACTID]
                                   .createInstance(Ci.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="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 = Cc["@mozilla.org/observer-service;1"]
                         .getService(Ci.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
                  Cu.reportError("Error enabling browser global history: " + ex);
                }
              }
            }
          } catch (e) {
            Cu.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 = Cc["@mozilla.org/observer-service;1"]
                       .getService(Ci.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 {autoscrollEnabled: false, usingApz: false};
              }
              this.startScroll(data.scrolldir, data.screenX, data.screenY);
              let usingApz = false;
              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 = Cc["@mozilla.org/observer-service;1"]
                             .getService(Ci.nsIObserverService);
                  os.addObserver(this, "apz:cancel-autoscroll", true);

                  usingApz = tabParent.startApzAutoscroll(
                      data.screenX, data.screenY,
                      data.scrollId, data.presShellId);
                }
                // Save the IDs for later
                this._autoScrollScrollId = data.scrollId;
                this._autoScrollPresShellId = data.presShellId;
              }
              return {autoscrollEnabled: true, usingApz};
            }
            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 =
                  ChromeUtils.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) {
                // 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;

                this._autoScrollPopup.hidePopup();
              }
            }
          ]]>
        </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 = Cc["@mozilla.org/observer-service;1"]
                         .getService(Ci.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("consumeoutsideclicks", "true");
            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[
            const POPUP_SIZE = 32;
            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;
              }
              this._autoScrollPopup.removeAttribute("hidden");
              this._autoScrollPopup.setAttribute("noautofocus", "true");
              this._autoScrollPopup.style.height = POPUP_SIZE + "px";
              this._autoScrollPopup.style.width = POPUP_SIZE + "px";
              this._autoScrollPopup.style.margin = -POPUP_SIZE / 2 + "px";
            }

            let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
              .getService(Ci.nsIScreenManager);
            let screen = screenManager.screenForRect(screenX, screenY, 1, 1);

            // 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("scrolldir", scrolldir);
            this._autoScrollPopup.addEventListener("popuphidden", this, true);

            // Sanitize screenX/screenY for available screen size with half the size
            // of the popup removed. The popup uses negative margins to center on the
            // coordinates we pass. Unfortunately `window.screen.availLeft` can be negative
            // on Windows in multi-monitor setups, so we use nsIScreenManager instead:
            let left = {}, top = {}, width = {}, height = {};
            screen.GetAvailRect(left, top, width, height);

            // We need to get screen CSS-pixel (rather than display-pixel) coordinates.
            // With 175% DPI, the actual ratio of display pixels to CSS pixels is
            // 1.7647 because of rounding inside gecko. Unfortunately defaultCSSScaleFactor
            // returns the original 1.75 dpi factor. While window.devicePixelRatio would
            // get us the correct ratio, if the window is split between 2 screens,
            // window.devicePixelRatio isn't guaranteed to match the screen we're
            // autoscrolling on. So instead we do the same math as Gecko.
            const scaleFactor = 60 / Math.round(60 / screen.defaultCSSScaleFactor);
            let minX = left.value / scaleFactor + 0.5 * POPUP_SIZE;
            let maxX = (left.value + width.value) / scaleFactor - 0.5 * POPUP_SIZE;
            let minY = top.value / scaleFactor + 0.5 * POPUP_SIZE;
            let maxY = (top.value + height.value) / scaleFactor - 0.5 * POPUP_SIZE;
            let popupX = Math.max(minX, Math.min(maxX, screenX));
            let popupY = Math.max(minY, Math.min(maxY, screenY));
            this._autoScrollPopup.openPopupAtScreen(popupX, popupY);
            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>

      <property name="dontPromptAndDontUnload"
                onget="return 1;"
                readonly="true"/>

      <property name="dontPromptAndUnload"
                onget="return 2;"
                readonly="true"/>

      <method name="permitUnload">
        <parameter name="aPermitUnloadFlags"/>
        <body>
        <![CDATA[
          if (!this.docShell || !this.docShell.contentViewer) {
            return {permitUnload: true, timedOut: false};
          }
          return {permitUnload: this.docShell.contentViewer.permitUnload(aPermitUnloadFlags),
                  timedOut: false};
        ]]>
        </body>
      </method>

      <method name="print">
        <parameter name="aOuterWindowID"/>
        <parameter name="aPrintSettings"/>
        <parameter name="aPrintProgressListener"/>
        <body>
          <![CDATA[
            if (!this.frameLoader) {
              throw Components.Exception("No frame loader.",
                                         Cr.NS_ERROR_FAILURE);
            }

            this.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 = Cc["@mozilla.org/embedcomp/prompt-service;1"]
                                  .getService(Ci.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 = Cc["@mozilla.org/content/dropped-link-handler;1"].
                            getService(Ci.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 = Cc["@mozilla.org/content/dropped-link-handler;1"].
                            getService(Ci.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>