Bug 1301312 - Part 5: Handle input element's attribute change explicitly. r=smaug
authorJessica Jong <jjong@mozilla.com>
Wed, 15 Mar 2017 11:39:02 +0800
changeset 398213 6d303d1651cc0adeee4b787f568b95c6f3c653ce
parent 398212 8fda74ef8da6c270e983e38a03f616330741168a
child 398214 f7605a47342d5019159e97d6814b6a8fe127342a
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1301312
milestone55.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 1301312 - Part 5: Handle input element's attribute change explicitly. r=smaug MozReview-Commit-ID: AswWoeGasXZ
dom/html/nsIDateTimeInputArea.idl
layout/forms/nsDateTimeControlFrame.cpp
toolkit/content/widgets/datetimebox.xml
--- a/dom/html/nsIDateTimeInputArea.idl
+++ b/dom/html/nsIDateTimeInputArea.idl
@@ -28,9 +28,21 @@ interface nsIDateTimeInputArea : nsISupp
    * Called from DOM/Layout to blur inner text box.
    */
   void blurInnerTextBox();
 
   /**
    * Set the current state of the picker, true if it's opened, false otherwise.
    */
   void setPickerState(in boolean isOpen);
+
+  /**
+   * Set the attribute of the inner text boxes. Only "tabindex", "readonly",
+   * and "disabled" are allowed.
+   */
+  void setEditAttribute(in DOMString name, in DOMString value);
+
+  /**
+   * Remove the attribute of the inner text boxes. Only "tabindex", "readonly",
+   * and "disabled" are allowed.
+   */
+  void removeEditAttribute(in DOMString name);
 };
--- a/layout/forms/nsDateTimeControlFrame.cpp
+++ b/layout/forms/nsDateTimeControlFrame.cpp
@@ -311,53 +311,64 @@ nsDateTimeControlFrame::CreateAnonymousC
   RefPtr<NodeInfo> nodeInfo =
     nodeInfoManager->GetNodeInfo(nsGkAtoms::datetimebox, nullptr,
                                  kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
   NS_TrustedNewXULElement(getter_AddRefs(mInputAreaContent), nodeInfo.forget());
   aElements.AppendElement(mInputAreaContent);
 
-  // Propogate our tabindex.
-  nsAutoString tabIndexStr;
-  if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr)) {
-    mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex,
-                               tabIndexStr, false);
-  }
+  nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
+    do_QueryInterface(mInputAreaContent);
+  if (inputAreaContent) {
+    // Propogate our tabindex.
+    nsAutoString tabIndexStr;
+    if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr)) {
+      inputAreaContent->SetEditAttribute(NS_LITERAL_STRING("tabindex"),
+                                         tabIndexStr);
+    }
 
-  // Propagate our readonly state.
-  nsAutoString readonly;
-  if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
-    mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly,
-                               false);
+    // Propagate our readonly state.
+    nsAutoString readonly;
+    if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
+      inputAreaContent->SetEditAttribute(NS_LITERAL_STRING("readonly"),
+                                         readonly);
+    }
+
+    SyncDisabledState();
   }
 
-  SyncDisabledState();
-
   return NS_OK;
 }
 
 void
 nsDateTimeControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
                                                  uint32_t aFilter)
 {
   if (mInputAreaContent) {
     aElements.AppendElement(mInputAreaContent);
   }
 }
 
 void
 nsDateTimeControlFrame::SyncDisabledState()
 {
+  NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
+  nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
+    do_QueryInterface(mInputAreaContent);
+  if (!inputAreaContent) {
+    return;
+  }
+
   EventStates eventStates = mContent->AsElement()->State();
   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
-    mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
-                               EmptyString(), true);
+    inputAreaContent->SetEditAttribute(NS_LITERAL_STRING("disabled"),
+                                       EmptyString());
   } else {
-    mInputAreaContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
+    inputAreaContent->RemoveEditAttribute(NS_LITERAL_STRING("disabled"));
   }
 }
 
 nsresult
 nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
                                          nsIAtom* aAttribute,
                                          int32_t aModType)
 {
@@ -369,32 +380,38 @@ nsDateTimeControlFrame::AttributeChanged
         aAttribute == nsGkAtoms::readonly ||
         aAttribute == nsGkAtoms::tabindex) {
       MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast");
       auto contentAsInputElem = static_cast<dom::HTMLInputElement*>(mContent);
       // If script changed the <input>'s type before setting these attributes
       // then we don't need to do anything since we are going to be reframed.
       if (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME ||
           contentAsInputElem->GetType() == NS_FORM_INPUT_DATE) {
+        nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
+          do_QueryInterface(mInputAreaContent);
         if (aAttribute == nsGkAtoms::value) {
-          nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
-            do_QueryInterface(mInputAreaContent);
           if (inputAreaContent) {
             nsContentUtils::AddScriptRunner(NewRunnableMethod(inputAreaContent,
               &nsIDateTimeInputArea::NotifyInputElementValueChanged));
           }
         } else {
           if (aModType == nsIDOMMutationEvent::REMOVAL) {
-            mInputAreaContent->UnsetAttr(aNameSpaceID, aAttribute, true);
+            if (inputAreaContent) {
+              nsAtomString name(aAttribute);
+              inputAreaContent->RemoveEditAttribute(name);
+            }
           } else {
             MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
                        aModType == nsIDOMMutationEvent::MODIFICATION);
-            nsAutoString value;
-            mContent->GetAttr(aNameSpaceID, aAttribute, value);
-            mInputAreaContent->SetAttr(aNameSpaceID, aAttribute, value, true);
+            if (inputAreaContent) {
+              nsAtomString name(aAttribute);
+              nsAutoString value;
+              mContent->GetAttr(aNameSpaceID, aAttribute, value);
+              inputAreaContent->SetEditAttribute(name, value);
+            }
           }
         }
       }
     }
   }
 
   return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                             aModType);
--- a/toolkit/content/widgets/datetimebox.xml
+++ b/toolkit/content/widgets/datetimebox.xml
@@ -1275,16 +1275,63 @@
         <body>
         <![CDATA[
           this.log("picker is now " + (aIsOpen ? "opened" : "closed"));
           this.mIsPickerOpen = aIsOpen;
         ]]>
         </body>
       </method>
 
+      <method name="setEditAttribute">
+        <parameter name="aName"/>
+        <parameter name="aValue"/>
+        <body>
+        <![CDATA[
+          this.log("setAttribute: " + aName + "=" + aValue);
+
+          if (aName != "tabindex" && aName != "disabled" &&
+              aName != "readonly") {
+            return;
+          }
+
+          let editRoot =
+            document.getAnonymousElementByAttribute(this, "anonid", "edit-wrapper");
+
+          for (let child = editRoot.firstChild; child; child = child.nextSibling) {
+            if (child instanceof HTMLInputElement) {
+              child.setAttribute(aName, aValue);
+            }
+          }
+        ]]>
+        </body>
+      </method>
+
+      <method name="removeEditAttribute">
+        <parameter name="aName"/>
+        <body>
+        <![CDATA[
+          this.log("removeAttribute: " + aName);
+
+          if (aName != "tabindex" && aName != "disabled" &&
+              aName != "readonly") {
+            return;
+          }
+
+          let editRoot =
+            document.getAnonymousElementByAttribute(this, "anonid", "edit-wrapper");
+
+          for (let child = editRoot.firstChild; child; child = child.nextSibling) {
+            if (child instanceof HTMLInputElement) {
+              child.removeAttribute(aName);
+            }
+          }
+        ]]>
+        </body>
+      </method>
+
       <method name="isEmpty">
         <parameter name="aValue"/>
         <body>
           return (aValue == undefined || 0 === aValue.length);
         </body>
       </method>
 
       <method name="stripDirFormattingChars">
@@ -1357,25 +1404,25 @@
           }
         ]]>
         </body>
       </method>
 
       <method name="isDisabled">
         <body>
         <![CDATA[
-          return this.hasAttribute("disabled");
+          return this.mInputElement.hasAttribute("disabled");
         ]]>
         </body>
       </method>
 
       <method name="isReadonly">
         <body>
         <![CDATA[
-          return this.hasAttribute("readonly");
+          return this.mInputElement.hasAttribute("readonly");
         ]]>
         </body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body>
         <![CDATA[