Bug 1301312 - Part 5: Handle input element's attribute change explicitly. r?smaug draft
authorJessica Jong <jjong@mozilla.com>
Wed, 15 Mar 2017 11:39:02 +0800
changeset 498738 6f61c2d02fba1fb7fe8fd1c44d530f0ab3b4f28c
parent 498737 f2bdaa4e6cbb191eaba777e2eeadc804d903cc7d
child 549234 52a23b99ff1292b121baa70b2711ffa1e8c087d5
push id49288
push userjjong@mozilla.com
push dateWed, 15 Mar 2017 07:30:13 +0000
reviewerssmaug
bugs1301312
milestone55.0a1
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[