Bug 596088 - Make <input type='file'> looks disabled when inside a disabled fieldset by adding a ContentStatesChanged method in nsIFrame. r=bz sr=roc a=blocking-final
authorMounir Lamouri <mounir.lamouri@gmail.com>
Tue, 14 Dec 2010 10:00:57 -0800
changeset 59447 98ee88c41e2377c4464102cf6aaeffe6d216e9e8
parent 59446 14f1f60c6a11cd3850f7635be0e914c2f48ad946
child 59448 5370d662ae51a18c353546e7e8b19e3ad663f4db
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersbz, roc, blocking-final
bugs596088
milestone2.0b9pre
Bug 596088 - Make <input type='file'> looks disabled when inside a disabled fieldset by adding a ContentStatesChanged method in nsIFrame. r=bz sr=roc a=blocking-final
layout/base/nsCSSFrameConstructor.cpp
layout/forms/nsFileControlFrame.cpp
layout/forms/nsFileControlFrame.h
layout/generic/nsIFrame.h
layout/reftests/bugs/557087-1.html
layout/reftests/bugs/557087-2.html
layout/reftests/bugs/557087.html
layout/reftests/bugs/reftest.list
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8139,34 +8139,36 @@ nsCSSFrameConstructor::DoContentStateCha
   // based on content states, so if we already don't have a frame we don't
   // need to force a reframe -- if it's needed, the HasStateDependentStyle
   // call will handle things.
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   if (primaryFrame) {
     // If it's generated content, ignore LOADING/etc state changes on it.
     if (!primaryFrame->IsGeneratedContentFrame() &&
         aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
-                                  NS_EVENT_STATE_USERDISABLED |
-                                  NS_EVENT_STATE_SUPPRESSED |
-                                  NS_EVENT_STATE_LOADING)) {
+                                         NS_EVENT_STATE_USERDISABLED |
+                                         NS_EVENT_STATE_SUPPRESSED |
+                                         NS_EVENT_STATE_LOADING)) {
       hint = nsChangeHint_ReconstructFrame;
     } else {
       PRUint8 app = primaryFrame->GetStyleDisplay()->mAppearance;
       if (app) {
         nsITheme *theme = presContext->GetTheme();
         if (theme && theme->ThemeSupportsWidget(presContext,
                                                 primaryFrame, app)) {
           PRBool repaint = PR_FALSE;
           theme->WidgetStateChanged(primaryFrame, app, nsnull, &repaint);
           if (repaint) {
             NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
           }
         }
       }
     }
+
+    primaryFrame->ContentStatesChanged(aStateMask);
   }
 
   nsRestyleHint rshint = 
     styleSet->HasStateDependentStyle(presContext, aElement, aStateMask);
       
   if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
     ++mHoverGeneration;
   }
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -92,17 +92,16 @@
 #include "nsIFileURL.h"
 #include "nsDOMFile.h"
 #include "nsIEventStateManager.h"
 
 namespace dom = mozilla::dom;
 
 #define SYNC_TEXT 0x1
 #define SYNC_BUTTON 0x2
-#define SYNC_BOTH 0x3
 
 nsIFrame*
 NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsFileControlFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
@@ -339,17 +338,17 @@ nsFileControlFrame::CreateAnonymousConte
   nsCOMPtr<nsIDOM3EventTarget> dom3Browse = do_QueryInterface(mBrowse);
   NS_ENSURE_STATE(dom3Browse);
   // Register as an event listener of the button
   // to open file dialog on mouse click
   dom3Browse->AddGroupedEventListener(click, mMouseListener, PR_FALSE,
                                       systemGroup);
 
   SyncAttr(kNameSpaceID_None, nsGkAtoms::size,     SYNC_TEXT);
-  SyncAttr(kNameSpaceID_None, nsGkAtoms::disabled, SYNC_BOTH);
+  SyncDisabledState();
 
   return NS_OK;
 }
 
 void
 nsFileControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
                                              PRUint32 aFilter)
 {
@@ -584,36 +583,55 @@ nsFileControlFrame::SyncAttr(PRInt32 aNa
       mTextContent->UnsetAttr(aNameSpaceID, aAttribute, PR_TRUE);
     }
     if (aWhichControls & SYNC_BUTTON && mBrowse) {
       mBrowse->UnsetAttr(aNameSpaceID, aAttribute, PR_TRUE);
     }
   }
 }
 
+void
+nsFileControlFrame::SyncDisabledState()
+{
+  nsEventStates eventStates = mContent->IntrinsicState();
+  if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
+    mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
+                          PR_TRUE);
+    mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
+                     PR_TRUE);
+  } else {
+    mTextContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, PR_TRUE);
+    mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, PR_TRUE);
+  }
+}
+
 NS_IMETHODIMP
 nsFileControlFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      PRInt32         aModType)
 {
-  // propagate disabled to text / button inputs
   if (aNameSpaceID == kNameSpaceID_None) {
-    if (aAttribute == nsGkAtoms::disabled) {
-      SyncAttr(aNameSpaceID, aAttribute, SYNC_BOTH);
-    // propagate size to text
-    } else if (aAttribute == nsGkAtoms::size) {
+    if (aAttribute == nsGkAtoms::size) {
       SyncAttr(aNameSpaceID, aAttribute, SYNC_TEXT);
     } else if (aAttribute == nsGkAtoms::tabindex) {
       SyncAttr(aNameSpaceID, aAttribute, SYNC_BUTTON);
     }
   }
 
   return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
 
+void
+nsFileControlFrame::ContentStatesChanged(nsEventStates aStates)
+{
+  if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
+    nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
+  }
+}
+
 PRBool
 nsFileControlFrame::IsLeaf() const
 {
   return PR_TRUE;
 }
 
 #ifdef NS_DEBUG
 NS_IMETHODIMP
--- a/layout/forms/nsFileControlFrame.h
+++ b/layout/forms/nsFileControlFrame.h
@@ -82,16 +82,17 @@ public:
 
 #ifdef NS_DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const;
 #endif
 
   NS_IMETHOD AttributeChanged(PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
+  virtual void ContentStatesChanged(nsEventStates aStates);
   virtual PRBool IsLeaf() const;
 
 
 
   // nsIAnonymousContentCreator
   virtual nsresult CreateAnonymousContent(nsTArray<nsIContent*>& aElements);
   virtual void AppendAnonymousContentTo(nsBaseContentList& aElements,
                                         PRUint32 aFilter);
@@ -129,17 +130,38 @@ protected:
     NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
     NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; }
     NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; }
     NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
 
   protected:
     nsFileControlFrame* mFrame;
   };
-  
+
+  class SyncDisabledStateEvent;
+  friend class SyncDisabledStateEvent;
+  class SyncDisabledStateEvent : public nsRunnable
+  {
+  public:
+    SyncDisabledStateEvent(nsFileControlFrame* aFrame)
+      : mFrame(aFrame)
+    {}
+
+    NS_IMETHOD Run() {
+      nsFileControlFrame* frame = static_cast<nsFileControlFrame*>(mFrame.GetFrame());
+      NS_ENSURE_STATE(frame);
+
+      frame->SyncDisabledState();
+      return NS_OK;
+    }
+
+  private:
+    nsWeakFrame mFrame;
+  };
+
   class CaptureMouseListener: public MouseListener {
   public:
     CaptureMouseListener(nsFileControlFrame* aFrame) : MouseListener(aFrame),
                                                        mMode(0) {};
     NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent);
     PRUint32 mMode;
   };
   
@@ -198,18 +220,22 @@ private:
    */
   nsNewFrame* GetTextControlFrame(nsPresContext* aPresContext,
                                   nsIFrame* aStart);
 
   /**
    * Copy an attribute from file content to text and button content.
    * @param aNameSpaceID namespace of attr
    * @param aAttribute attribute atom
-   * @param aWhichControls which controls to apply to (SYNC_TEXT or SYNC_FILE
-   *        or SYNC_BOTH)
+   * @param aWhichControls which controls to apply to (SYNC_TEXT or SYNC_FILE)
    */
   void SyncAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                 PRInt32 aWhichControls);
+
+  /**
+   * Sync the disabled state of the content with anonymous children.
+   */
+  void SyncDisabledState();
 };
 
 #endif
 
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1293,16 +1293,24 @@ public:
    * @param aModType Whether or not the attribute was added, changed, or removed.
    *   The constants are defined in nsIDOMMutationEvent.h.
    */
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
                                PRInt32         aModType) = 0;
 
   /**
+   * When the content states of a content object change, this method is invoked
+   * on the primary frame of that content object.
+   *
+   * @param aStates the changed states
+   */
+  virtual void ContentStatesChanged(nsEventStates aStates) { };
+
+  /**
    * Return how your frame can be split.
    */
   virtual nsSplittableType GetSplittableType() const = 0;
 
   /**
    * Continuation member functions
    */
   virtual nsIFrame* GetPrevContinuation() const = 0;
rename from layout/reftests/bugs/557087.html
rename to layout/reftests/bugs/557087-1.html
copy from layout/reftests/bugs/557087.html
copy to layout/reftests/bugs/557087-2.html
--- a/layout/reftests/bugs/557087.html
+++ b/layout/reftests/bugs/557087-2.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
-<html>
-  <body>
-    <fieldset disabled>
+<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>
       <button>foo</button>
       <textarea></textarea>
       <select><option>foo</option></select>
       <fieldset></fieldset>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1442,17 +1442,18 @@ random-if(!haveTestPlugin) == 546071-1.h
 == 550325-2.html 550325-1-ref.html
 == 550325-3.html 550325-1-ref.html
 == 550716-1.html 550716-1-ref.html
 == 551463-1.html 551463-1-ref.html
 == 551699-1.html 551699-1-ref.html
 == 552334-1.html 552334-1-ref.html
 random-if(d2d) == 555388-1.html 555388-1-ref.html
 == 556661-1.html 556661-1-ref.html
-== 557087.html 557087-ref.html
+== 557087-1.html 557087-ref.html
+== 557087-2.html 557087-ref.html
 == 557736-1.html 557736-1-ref.html
 == 559284-1.html 559284-1-ref.html
 == 560455-1.xul 560455-1-ref.xul
 == 561981-1.html 561981-1-ref.html
 == 561981-2.html 561981-2-ref.html
 == 561981-3.html 561981-3-ref.html
 == 561981-4.html 561981-4-ref.html
 == 561981-5.html 561981-5-ref.html