Bug 948549 - Make <input type=number> behave and look disabled when the 'disabled' attribute is set or it's inside a disabled fieldset. r=smaug
authorJonathan Watt <jwatt@jwatt.org>
Wed, 11 Dec 2013 02:13:06 +0000
changeset 160182 acadd89682ec3086ba24ecb566b488dc73854649
parent 160181 fb0fa2c213924b02ce3f48d76bea8ffdf68ac557
child 160183 2da1013d5182dcac11c9a062f7f104e9895424bd
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssmaug
bugs948549
milestone29.0a1
Bug 948549 - Make <input type=number> behave and look disabled when the 'disabled' attribute is set or it's inside a disabled fieldset. r=smaug
layout/forms/nsNumberControlFrame.cpp
layout/forms/nsNumberControlFrame.h
layout/reftests/bugs/557087-1.html
layout/reftests/bugs/557087-2.html
layout/reftests/bugs/557087-ref.html
layout/reftests/forms/input/number/number-disabled-ref.html
layout/reftests/forms/input/number/number-disabled.html
layout/reftests/forms/input/number/reftest.list
widget/xpwidgets/nsNativeTheme.cpp
--- a/layout/forms/nsNumberControlFrame.cpp
+++ b/layout/forms/nsNumberControlFrame.cpp
@@ -145,21 +145,34 @@ nsNumberControlFrame::
   NS_ENSURE_SUCCESS(rv, rv);
   MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
              "We gave our child unconstrained height, so it should be complete");
   return FinishReflowChild(aOuterWrapperFrame, aPresContext,
                            &wrapperReflowState, aWrappersDesiredSize,
                            xoffset, yoffset, 0);
 }
 
+void
+nsNumberControlFrame::SyncDisabledState()
+{
+  nsEventStates eventStates = mContent->AsElement()->State();
+  if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
+    mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
+                        true);
+  } else {
+    mTextField->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
+  }
+}
+
 NS_IMETHODIMP
 nsNumberControlFrame::AttributeChanged(int32_t  aNameSpaceID,
                                        nsIAtom* aAttribute,
                                        int32_t  aModType)
 {
+  // nsGkAtoms::disabled is handled by SyncDisabledState
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::placeholder ||
         aAttribute == nsGkAtoms::readonly ||
         aAttribute == nsGkAtoms::tabindex) {
       if (aModType == nsIDOMMutationEvent::REMOVAL) {
         mTextField->UnsetAttr(aNameSpaceID, aAttribute, true);
       } else {
         MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
@@ -170,16 +183,24 @@ nsNumberControlFrame::AttributeChanged(i
       }
     }
   }
 
   return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                             aModType);
 }
 
+void
+nsNumberControlFrame::ContentStatesChanged(nsEventStates aStates)
+{
+  if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
+    nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
+  }
+}
+
 nsresult
 nsNumberControlFrame::MakeAnonymousElement(Element** aResult,
                                            nsTArray<ContentInfo>& aElements,
                                            nsIAtom* aTagName,
                                            nsCSSPseudoElements::Type aPseudoType,
                                            nsStyleContext* aParentContext)
 {
   // Get the NodeInfoManager and tag necessary to create the anonymous divs.
@@ -305,16 +326,19 @@ nsNumberControlFrame::CreateAnonymousCon
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create the ::-moz-number-spin-down pseudo-element:
   rv = MakeAnonymousElement(getter_AddRefs(mSpinDown),
                             spinBoxCI.mChildren,
                             nsGkAtoms::div,
                             nsCSSPseudoElements::ePseudo_mozNumberSpinDown,
                             spinBoxCI.mStyleContext);
+
+  SyncDisabledState();
+
   return rv;
 }
 
 nsIAtom*
 nsNumberControlFrame::GetType() const
 {
   return nsGkAtoms::numberControlFrame;
 }
--- a/layout/forms/nsNumberControlFrame.h
+++ b/layout/forms/nsNumberControlFrame.h
@@ -38,17 +38,17 @@ class nsNumberControlFrame MOZ_FINAL : p
   nsNumberControlFrame(nsStyleContext* aContext);
 
 public:
   NS_DECL_QUERYFRAME_TARGET(nsNumberControlFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
-
+  virtual void ContentStatesChanged(nsEventStates aStates);
   virtual bool IsLeaf() const MOZ_OVERRIDE { return false; }
 
   NS_IMETHOD Reflow(nsPresContext*           aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
   NS_IMETHOD AttributeChanged(int32_t  aNameSpaceID,
@@ -137,16 +137,44 @@ private:
                                 nsCSSPseudoElements::Type aPseudoType,
                                 nsStyleContext* aParentContext);
 
   nsresult ReflowAnonymousContent(nsPresContext* aPresContext,
                                   nsHTMLReflowMetrics& aWrappersDesiredSize,
                                   const nsHTMLReflowState& aReflowState,
                                   nsIFrame* aOuterWrapperFrame);
 
+  class SyncDisabledStateEvent;
+  friend class SyncDisabledStateEvent;
+  class SyncDisabledStateEvent : public nsRunnable
+  {
+  public:
+    SyncDisabledStateEvent(nsNumberControlFrame* aFrame)
+    : mFrame(aFrame)
+    {}
+
+    NS_IMETHOD Run() MOZ_OVERRIDE
+    {
+      nsNumberControlFrame* frame =
+        static_cast<nsNumberControlFrame*>(mFrame.GetFrame());
+      NS_ENSURE_STATE(frame);
+
+      frame->SyncDisabledState();
+      return NS_OK;
+    }
+
+  private:
+    nsWeakFrame mFrame;
+  };
+
+  /**
+   * Sync the disabled state of the anonymous children up with our content's.
+   */
+  void SyncDisabledState();
+
   /**
    * The text field used to edit and show the number.
    * @see nsNumberControlFrame::CreateAnonymousContent.
    */
   nsCOMPtr<Element> mOuterWrapper;
   nsCOMPtr<Element> mTextField;
   nsCOMPtr<Element> mSpinBox;
   nsCOMPtr<Element> mSpinUp;
--- a/layout/reftests/bugs/557087-1.html
+++ b/layout/reftests/bugs/557087-1.html
@@ -1,15 +1,16 @@
 <!DOCTYPE html>
 <html>
   <body>
     <fieldset disabled>
       <input type='file'>
       <input type='checkbox'>
       <input type='radio'>
+      <input type='number'>
       <input>
       <button>foo</button>
       <textarea></textarea>
       <select><option>foo</option></select>
       <fieldset></fieldset>
     </fieldset>
   </body>
 </html>
--- a/layout/reftests/bugs/557087-2.html
+++ b/layout/reftests/bugs/557087-2.html
@@ -1,16 +1,17 @@
 <!DOCTYPE html>
 <html class='reftest-wait'>
   <body onload="document.getElementsByTagName('fieldset')[0].disabled = true;
                 document.documentElement.className='';">
     <fieldset>
       <input type='file'>
       <input type='checkbox'>
       <input type='radio'>
+      <input type='number'>
       <input>
       <button>foo</button>
       <textarea></textarea>
       <select><option>foo</option></select>
       <fieldset></fieldset>
     </fieldset>
   </body>
 </html>
--- a/layout/reftests/bugs/557087-ref.html
+++ b/layout/reftests/bugs/557087-ref.html
@@ -1,15 +1,16 @@
 <!DOCTYPE html>
 <html>
   <body>
     <fieldset disabled>
       <input type='file' disabled>
       <input type='checkbox' disabled>
       <input type='radio' disabled>
+      <input type='number' disabled>
       <input disabled>
       <button disabled>foo</button>
       <textarea disabled></textarea>
       <select disabled><option>foo</option></select>
       <fieldset disabled></fieldset>
     </fieldset>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-disabled-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <input type="text" disabled>
+    <!-- div to cover spin box area -->
+    <div style="display:block; position:absolute; background-color:black; width:200px; height:100px; top:0px; left:100px;">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-disabled.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <input type="number" disabled>
+    <!-- div to cover spin box area -->
+    <div style="display:block; position:absolute; background-color:black; width:200px; height:100px; top:0px; left:100px;">
+  </body>
+</html>
--- a/layout/reftests/forms/input/number/reftest.list
+++ b/layout/reftests/forms/input/number/reftest.list
@@ -12,14 +12,17 @@ skip-if(!Android&&!B2G) == number-same-a
 
 # dynamic type changes:
 fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),64,4) fuzzy-if(cocoaWidget,63,4) == to-number-from-other-type-unthemed-1.html to-number-from-other-type-unthemed-1-ref.html
 == from-number-to-other-type-unthemed-1.html from-number-to-other-type-unthemed-1-ref.html
 
 # dynamic value changes:
 == show-value.html show-value-ref.html
 
+# disabled
+== number-disabled.html number-disabled-ref.html
+
 # focus
 needs-focus == focus-handling.html focus-handling-ref.html
 
 # pseudo-elements not usable from content:
 == number-pseudo-elements.html number-pseudo-elements-ref.html
 
--- a/widget/xpwidgets/nsNativeTheme.cpp
+++ b/widget/xpwidgets/nsNativeTheme.cpp
@@ -80,16 +80,24 @@ nsNativeTheme::GetContentState(nsIFrame*
     // anonymous <input type=text> takes focus for it.
     if (aWidgetType == NS_THEME_NUMBER_INPUT &&
         frameContent->IsHTML(nsGkAtoms::input)) {
       nsNumberControlFrame *numberControlFrame = do_QueryFrame(aFrame);
       if (numberControlFrame && numberControlFrame->IsFocused()) {
         flags |= NS_EVENT_STATE_FOCUS;
       }
     }
+
+    nsNumberControlFrame* numberControlFrame =
+      nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
+    if (numberControlFrame &&
+        numberControlFrame->GetContent()->AsElement()->State().
+          HasState(NS_EVENT_STATE_DISABLED)) {
+      flags |= NS_EVENT_STATE_DISABLED;
+    }
   }
   
   if (isXULCheckboxRadio && aWidgetType == NS_THEME_RADIO) {
     if (IsFocused(aFrame))
       flags |= NS_EVENT_STATE_FOCUS;
   }
 
   // On Windows and Mac, only draw focus rings if they should be shown. This