Bug 1059146 - Switch menulist.xml to use MutationObserver instead of Mutation Events. r=gijs
authorIan Moody <moz-ian@perix.co.uk>
Tue, 14 Jun 2016 21:13:29 +0100
changeset 301942 7b2c25149bc18bc0e15a79779c812ad8143185dc
parent 301941 6f305ccfd7761c4215f024de0abe264f4e994437
child 301943 1e387aca0b246edecd61140014e3e53746f83659
push id78510
push usercbook@mozilla.com
push dateThu, 16 Jun 2016 07:39:45 +0000
treeherdermozilla-inbound@40a413832349 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs
bugs1059146
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1059146 - Switch menulist.xml to use MutationObserver instead of Mutation Events. r=gijs MozReview-Commit-ID: KeQg9AZxisg
toolkit/content/widgets/menulist.xml
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -61,20 +61,21 @@
               this.menuBoxObject.activeChild.doCommand();
               event.preventDefault();
             }
           }
         ]]>
       </handler>
     </handlers>
 
-    <implementation implements="nsIDOMXULMenuListElement, nsIDOMEventListener">
+    <implementation implements="nsIDOMXULMenuListElement">
       <constructor>
         this.mInputField = null;
         this.mSelectedInternal = null;
+        this.mAttributeObserver = null;
         this.menuBoxObject = this.boxObject;
         this.setInitialSelection();
       </constructor>
 
       <method name="setInitialSelection">
         <body>
           <![CDATA[
             var popup = this.menupopup;
@@ -212,18 +213,19 @@
             if (oldval) {
               oldval.removeAttribute('selected');
               if (document instanceof Components.interfaces.nsIDOMXULDocument) {
                 document.removeBroadcastListenerFor(oldval, this, "value");
                 document.removeBroadcastListenerFor(oldval, this, "label");
                 document.removeBroadcastListenerFor(oldval, this, "image");
                 document.removeBroadcastListenerFor(oldval, this, "description");
               }
-              else
-                oldval.removeEventListener("DOMAttrModified", this, false);
+              else {
+                this.mAttributeObserver.disconnect();
+              }
             }
 
             this.mSelectedInternal = val;
             if (val) {
               val.setAttribute('selected', 'true');
               this.setAttribute('value', val.getAttribute('value'));
               this.setAttribute('image', val.getAttribute('image'));
               this.setAttribute('label', val.getAttribute('label'));
@@ -231,18 +233,21 @@
               // DOMAttrModified listeners slow down setAttribute calls within
               // the document, see bug 395496
               if (document instanceof Components.interfaces.nsIDOMXULDocument) {
                 document.addBroadcastListenerFor(val, this, "value");
                 document.addBroadcastListenerFor(val, this, "label");
                 document.addBroadcastListenerFor(val, this, "image");
                 document.addBroadcastListenerFor(val, this, "description");
               }
-              else
-                val.addEventListener("DOMAttrModified", this, false);
+              else {
+                let attributeFilter = ["value", "label", "image", "description"];
+                this.mAttributeObserver = new MutationObserver(this.handleMutation.bind(this));
+                this.mAttributeObserver.observe(val, { attributeFilter });
+              }
             }
             else {
               this.removeAttribute('value');
               this.removeAttribute('image');
               this.removeAttribute('label');
               this.removeAttribute('description');
             }
 
@@ -254,29 +259,36 @@
             event.initEvent("ValueChange", true, true);
             this.dispatchEvent(event);
 
             return val;
           ]]>
         </setter>
       </property>
 
-      <method name="handleEvent">
-        <parameter name="aEvent"/>
+      <method name="handleMutation">
+        <parameter name="aRecords"/>
         <body>
           <![CDATA[
-            if (aEvent.type == "DOMAttrModified" &&
-                aEvent.target == this.mSelectedInternal) {
-              var attrName = aEvent.attrName;
-              switch (attrName) {
-                case "value":
-                case "label":
-                case "image":
-                case "description":
-                  this.setAttribute(attrName, aEvent.newValue);
+            for (let record of aRecords) {
+              let t = record.target;
+              if (t == this.mSelectedInternal) {
+                let attrName = record.attributeName;
+                switch (attrName) {
+                  case "value":
+                  case "label":
+                  case "image":
+                  case "description":
+                    if (t.hasAttribute(attrName)) {
+                      this.setAttribute(attrName, t.getAttribute(attrName));
+                    }
+                    else {
+                      this.removeAttribute(attrName);
+                    }
+                }
               }
             }
           ]]>
         </body>
       </method>
 
       <method name="getIndexOfItem">
         <parameter name="item"/>
@@ -386,18 +398,19 @@
         <![CDATA[
           if (this.mSelectedInternal) {
             if (document instanceof Components.interfaces.nsIDOMXULDocument) {
               document.removeBroadcastListenerFor(this.mSelectedInternal, this, "value");
               document.removeBroadcastListenerFor(this.mSelectedInternal, this, "label");
               document.removeBroadcastListenerFor(this.mSelectedInternal, this, "image");
               document.removeBroadcastListenerFor(this.mSelectedInternal, this, "description");
             }
-            else
-              this.mSelectedInternal.removeEventListener("DOMAttrModified", this, false);
+            else {
+              this.mAttributeObserver.disconnect();
+            }
           }
         ]]>
       </destructor>
     </implementation>
   </binding>
 
   <binding id="menulist-editable" extends="chrome://global/content/bindings/menulist.xml#menulist">
     <content sizetopopup="pref">