Bug 625114 - Port parts of |Bug 402147 - Clean up and refactor some event handling code in tabbrowser.xml| to SeaMonkey. r=frg
authorIan Neal <iann_cvs@blueyonder.co.uk>
Sun, 12 Jan 2020 13:33:43 +0100
changeset 37483 b1e99d195a4791f383149f540ab901df0ae24b4b
parent 37482 98fe11c81b149b29acf1fb441b7dfc769e3be065
child 37484 be2e2e5d1813c00f62db6a41f976ba661ca7003d
push id2566
push userclokep@gmail.com
push dateMon, 09 Mar 2020 19:20:31 +0000
treeherdercomm-beta@a352facfa0a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrg
bugs625114, 402147
Bug 625114 - Port parts of |Bug 402147 - Clean up and refactor some event handling code in tabbrowser.xml| to SeaMonkey. r=frg
suite/browser/tabbrowser.xml
--- a/suite/browser/tabbrowser.xml
+++ b/suite/browser/tabbrowser.xml
@@ -91,17 +91,17 @@
           <xul:notificationbox class="browser-notificationbox" xbl:inherits="popupnotification">
             <xul:browser flex="1" type="content" primary="true"
                          xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
-    <implementation implements="nsIObserver">
+    <implementation implements="nsIDOMEventListener, nsIObserver">
       <field name="mSessionStore" readonly="true">
         Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.nsISessionStore);
       </field>
       <field name="mURIFixup" readonly="true">
         Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
       </field>
       <field name="mTabBox" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
@@ -149,19 +149,25 @@
         window.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIWebNavigation)
               .QueryInterface(Ci.nsILoadContext)
               .usePrivateBrowsing;
       </field>
       <field name="mContextTab">
         null
       </field>
-      <field name="_keyEventHandler" readonly="true">
-      <![CDATA[({
-        handleEvent: function handleEvent(aEvent) {
+
+      <method name="_handleKeyEvent">
+        <parameter name="aEvent"/>
+        <body><![CDATA[
+          if (!aEvent.isTrusted) {
+            // Don't let untrusted events mess with tabs.
+            return;
+          }
+
           if (aEvent.altKey)
             return;
 
           if (AppConstants.platform == "macosx") {
             if (!aEvent.metaKey)
               return;
 
             var offset = 1;
@@ -169,29 +175,58 @@
               case '}'.charCodeAt(0):
                 offset = -1;
               case '{'.charCodeAt(0):
                 if (window.getComputedStyle(this, null).direction == "ltr")
                   offset *= -1;
                 this.tabContainer.advanceSelectedTab(offset, true);
                 aEvent.stopPropagation();
                 aEvent.preventDefault();
+                return;
             }
           } else {
             if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
                 aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
                 this.getStripVisibility()) {
               this.removeCurrentTab();
               aEvent.stopPropagation();
               aEvent.preventDefault();
+              return;
+            }
+
+            if (aEvent.target == this) {
+              switch (aEvent.keyCode) {
+                case KeyEvent.DOM_VK_UP:
+                  this.moveTabBackward();
+                  break;
+                case KeyEvent.DOM_VK_DOWN:
+                  this.moveTabForward();
+                  break;
+                case KeyEvent.DOM_VK_RIGHT:
+                case KeyEvent.DOM_VK_LEFT:
+                  this.moveTabOver(aEvent);
+                  break;
+                case KeyEvent.DOM_VK_HOME:
+                  this.moveTabToStart();
+                  break;
+                case KeyEvent.DOM_VK_END:
+                  this.moveTabToEnd();
+                  break;
+                default:
+                  // Stop the keypress event for the above keyboard
+                  // shortcuts only.
+                  return;
+              }
+              aEvent.stopPropagation();
+              aEvent.preventDefault();
             }
           }
-        }.bind(this)
-      })]]>
-      </field>
+        ]]></body>
+      </method>
+
       <field name="arrowKeysShouldWrap">
         null
       </field>
       <field name="nextTabNumber">
         0
       </field>
       <field name="_browsers">
         null
@@ -1334,33 +1369,16 @@
         <body>
           <![CDATA[
             aTab.label = this.mStringBundle.getString("tabs.loading");
             aTab.crop = "end";
             this._tabAttrModified(aTab, ["label", "crop"]);
           ]]>
         </body>
       </method>
-      <method name="onTitleChanged">
-        <parameter name="evt"/>
-        <body>
-          <![CDATA[
-            if (evt.target != this.contentDocument)
-              return;
-
-            var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;
-            var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id);
-
-            tabBrowser.setTabTitle(tab);
-
-            if (tab == tabBrowser.mCurrentTab)
-              tabBrowser.updateTitlebar();
-          ]]>
-        </body>
-      </method>
 
       <method name="setTabTitle">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             var browser = aTab.linkedBrowser;
             var title = browser.contentTitle;
             var crop = "end";
@@ -1606,18 +1624,16 @@
 
             // NB: this appendChild call causes us to run constructors for the
             // browser element, which fires off a bunch of notifications. Some
             // of those notifications can cause code to run that inspects our
             // state, so it is important that the tab element is fully
             // initialized by this point.
             this.mPanelContainer.appendChild(n);
 
-            b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
             // We start our browsers out as inactive.
             b.docShellIsActive = false;
 
             this.mStrip.collapsed = false;
 
             Services.prefs.setBoolPref("browser.tabs.forceHide", false);
 
             // wire up a progress listener for the new browser object.
@@ -1804,19 +1820,16 @@
 
             // navigate back to the proper page from the light page
             b.stop();
             b.webNavigation.gotoIndex(0);
 
             // reattach the old history
             b.webNavigation.sessionHistory = hist;
 
-            // Hook up the title change listener again
-            b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
             // add back the filters, security first (bug 313335)
             const nsIWebProgress = Ci.nsIWebProgress;
             var secFlags = nsIWebProgress.NOTIFY_STATE_ALL | nsIWebProgress.NOTIFY_LOCATION | nsIWebProgress.NOTIFY_SECURITY;
             b.webProgress.addProgressListener(b.securityUI, secFlags);
 
             var position = this.tabs.length - 1;
             var tabListener = this.mTabProgressListener(t, b, false);
             const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
@@ -1903,19 +1916,16 @@
 
             // Remove the tab's filter and progress listener.
             const filter = this.mTabFilters[index];
             oldBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(this.mTabListeners[index]);
             this.mTabFilters.splice(index, 1);
             this.mTabListeners.splice(index, 1);
 
-            // Remove our title change listener
-            oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
             // We are no longer the primary content area
             oldBrowser.removeAttribute("primary");
 
             // Now select the new tab before nuking the old one.
             var currentIndex = this.tabContainer.selectedIndex;
 
             var newIndex = -1;
             if (currentIndex > index)
@@ -2535,16 +2545,18 @@
                 return i;
             }
 
             return -1;
           ]]>
         </body>
       </method>
 
+      <!-- moveTabLeft and moveTabRight methods have been kept for backwards
+           compatibility for extensions. Internally moveTabOver is used. -->
       <method name="moveTabLeft">
         <body>
           <![CDATA[
             if (window.getComputedStyle(this, null).direction == "ltr")
               this.moveTabBackward();
             else
               this.moveTabForward();
           ]]>
@@ -2605,16 +2617,31 @@
             var tabPos = this.tabContainer.selectedIndex;
             if (tabPos < this.browsers.length - 1) {
               this.moveTabTo(tabPos, this.browsers.length - 1);
             }
           ]]>
         </body>
       </method>
 
+      <method name="moveTabOver">
+        <parameter name="aEvent"/>
+        <body>
+          <![CDATA[
+            var direction = window.getComputedStyle(this, null).direction;
+            var keyCode = aEvent.keyCode;
+            if ((direction == "ltr" && keyCode == KeyEvent.DOM_VK_RIGHT) ||
+                (direction == "rtl" && keyCode == KeyEvent.DOM_VK_LEFT))
+              this.moveTabForward();
+            else
+              this.moveTabBackward();
+          ]]>
+        </body>
+      </method>
+
       <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
            MAKE SURE TO ADD IT HERE AS WELL. -->
       <property name="canGoBack"
                 onget="return this.mCurrentBrowser.canGoBack;"
                 readonly="true"/>
 
       <property name="canGoForward"
                 onget="return this.mCurrentBrowser.canGoForward;"
@@ -2937,35 +2964,43 @@
           return this._fastFind;
         ]]>
         </getter>
       </property>
 
       <field name="_lastSearchString">null</field>
       <field name="_lastSearchHighlight">false</field>
 
+      <method name="handleEvent">
+        <parameter name="aEvent"/>
+        <body><![CDATA[
+          switch (aEvent.type) {
+            case "keypress":
+              this._handleKeyEvent(aEvent);
+              break;
+          }
+        ]]></body>
+      </method>
+
       <constructor>
         <![CDATA[
-          document.addEventListener("keypress", this._keyEventHandler);
+          document.addEventListener("keypress", this);
           this.arrowKeysShouldWrap = AppConstants.platform == "macosx";
           // Bail out early if we are in tabmail. See Bug 521803.
           if (!this.mPanelContainer)
             return;
 
           this.mCurrentBrowser = this.mPanelContainer.firstChild.firstChild;
           this.mCurrentTab = this.tabContainer.firstChild;
 
           var uniqueId = "panel" + this.nextTabNumber++;
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.tabs[0].linkedPanel = uniqueId;
           this.tabs[0].linkedBrowser = this.mCurrentBrowser;
 
-          // Wire up the first title change listener.
-          this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
           // Ensure the browser's session history and security UI are wired up
           // note that toolkit browser automatically inits its security UI
           // when you get it but for xpfe you need to init it explicitly
           if (!this.mCurrentBrowser.securityUI)
             this.mCurrentBrowser.init();
 
           // Wire up the tab's progress listener and filter.
           const nsIWebProgress = Ci.nsIWebProgress;
@@ -3003,28 +3038,26 @@
           var onclick = this.getAttribute("oncontentclick");
           if (onclick)
             this.onContentClick = new Function("event", onclick);
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
-          document.removeEventListener("keypress", this._keyEventHandler);
+          document.removeEventListener("keypress", this);
           // Bail out early if we are in tabmail. See Bug 521803.
           if (!this.mPanelContainer)
             return;
 
           for (var i = 0; i < this.mTabListeners.length; ++i) {
             this.browsers[i].webProgress.removeProgressListener(this.mTabFilters[i]);
             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
             this.mTabFilters[i] = null;
             this.mTabListeners[i] = null;
-            // eventListeners are removed from the browsers in display order of the browsers
-            this.browsers[i].removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
           }
           Services.obs.removeObserver(this, "browser:purge-session-history");
           Services.prefs.removeObserver("browser.tabs.max_tabs_undo", this);
           this.savedBrowsers.forEach(function(aTabData) {
             delete aTabData.browserData;
           });
         ]]>
       </destructor>
@@ -3073,22 +3106,34 @@
             return;
 
           // We're about to open a modal dialog, make sure the opening
           // tab is brought to the front.
           this.selectedTab = this._getTabForContentWindow(event.target.top);
         ]]>
       </handler>
 
-      <handler event="keypress" keycode="VK_LEFT" modifiers="accel" action="if (event.target == this) { this.moveTabLeft(); event.preventDefault(); }"/>
-      <handler event="keypress" keycode="VK_RIGHT" modifiers="accel" action="if (event.target == this) { this.moveTabRight(); event.preventDefault(); }"/>
-      <handler event="keypress" keycode="VK_UP" modifiers="accel" action="if (event.target == this) { this.moveTabBackward(); event.preventDefault(); }"/>
-      <handler event="keypress" keycode="VK_DOWN" modifiers="accel" action="if (event.target == this) { this.moveTabForward(); event.preventDefault(); }"/>
-      <handler event="keypress" keycode="VK_HOME" modifiers="accel" action="if (event.target == this) { this.moveTabToStart(); event.preventDefault(); }"/>
-      <handler event="keypress" keycode="VK_END" modifiers="accel" action="if (event.target == this) { this.moveTabToEnd(); event.preventDefault(); }"/>
+      <handler event="DOMTitleChanged">
+        <![CDATA[
+          if (!event.isTrusted)
+            return;
+
+          var contentWin = event.target.defaultView;
+          if (contentWin != contentWin.top)
+            return;
+
+          var tab = this._getTabForContentWindow(contentWin);
+          if (!tab)
+            return;
+
+          this.setTabTitle(tab);
+          if (tab == this.mCurrentTab)
+            this.updateTitlebar();
+        ]]>
+      </handler>
 
       <handler event="click" phase="capturing" group="system">
         <![CDATA[
           if (this.onContentClick)
             this.onContentClick(event);
         ]]>
       </handler>