Bug 737786 - 1/5 - Show/hide placeholder based on display lists instead of CSS class. r=bz
authorMounir Lamouri <mounir.lamouri@gmail.com>
Fri, 09 Nov 2012 10:22:29 +0000
changeset 121454 b3c3d66e557a2474d79f0e0537d90676a6a4cb22
parent 121453 eef85b6308178536cda3bc5fd5b4d7ac7be830c1
child 121455 6b9ba4944378fd7eb5695c36a8530f330bea4ca2
push id273
push userlsblakk@mozilla.com
push dateThu, 14 Feb 2013 23:19:38 +0000
treeherdermozilla-release@c5e807a3f8b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs737786
milestone19.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 737786 - 1/5 - Show/hide placeholder based on display lists instead of CSS class. r=bz
content/html/content/public/nsITextControlElement.h
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/src/nsTextEditorState.cpp
content/html/content/src/nsTextEditorState.h
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
layout/style/forms.css
--- a/content/html/content/public/nsITextControlElement.h
+++ b/content/html/content/public/nsITextControlElement.h
@@ -13,18 +13,18 @@ class nsIContent;
 class nsAString;
 class nsIEditor;
 class nsISelectionController;
 class nsFrameSelection;
 class nsTextControlFrame;
 
 // IID for the nsITextControl interface
 #define NS_ITEXTCONTROLELEMENT_IID    \
-{ 0xe0a05008, 0xef02, 0x4fa2,    \
-  { 0x93, 0xf2, 0x78, 0xe1, 0xec, 0xf7, 0x5b, 0x79 } }
+{ 0x669bd7ca, 0x42af, 0x4f1e, \
+  { 0xa6, 0xe2, 0x86, 0xc4, 0x0a, 0x14, 0x73, 0x4e } }
 
 /**
  * This interface is used for the text control frame to get the editor and
  * selection controller objects, and some helper properties.
  */
 class nsITextControlElement : public nsISupports {
 public:
 
@@ -148,17 +148,22 @@ public:
   /**
    * Initialize the keyboard event listeners.
    */
   NS_IMETHOD_(void) InitializeKeyboardEventListeners() = 0;
 
   /**
    * Show/hide the placeholder for the control.
    */
-  NS_IMETHOD_(void) SetPlaceholderClass(bool aVisible, bool aNotify) = 0;
+  NS_IMETHOD_(void) SetPlaceholderVisibility(bool aVisible, bool aNotify) = 0;
+
+  /**
+   * Returns the current expected placeholder visibility state.
+   */
+  NS_IMETHOD_(bool) GetPlaceholderVisibility() = 0;
 
   /**
    * Callback called whenever the value is changed.
    */
   NS_IMETHOD_(void) OnValueChanged(bool aNotify) = 0;
 
   static const int32_t DEFAULT_COLS = 20;
   static const int32_t DEFAULT_ROWS = 1;
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -1469,22 +1469,33 @@ nsHTMLInputElement::GetPlaceholderNode()
   nsTextEditorState *state = GetEditorState();
   if (state) {
     return state->GetPlaceholderNode();
   }
   return nullptr;
 }
 
 NS_IMETHODIMP_(void)
-nsHTMLInputElement::SetPlaceholderClass(bool aVisible, bool aNotify)
+nsHTMLInputElement::SetPlaceholderVisibility(bool aVisible, bool aNotify)
 {
   nsTextEditorState *state = GetEditorState();
   if (state) {
-    state->SetPlaceholderClass(aVisible, aNotify);
-  }
+    state->SetPlaceholderVisibility(aVisible, aNotify);
+  }
+}
+
+NS_IMETHODIMP_(bool)
+nsHTMLInputElement::GetPlaceholderVisibility()
+{
+  nsTextEditorState* state = GetEditorState();
+  if (!state) {
+    return false;
+  }
+
+  return state->GetPlaceholderVisibility();
 }
 
 void
 nsHTMLInputElement::GetDisplayFileName(nsAString& aValue) const
 {
   if (OwnerDoc()->IsStaticDocument()) {
     aValue = mStaticDocFileList;
     return;
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -145,17 +145,18 @@ public:
   NS_IMETHOD_(nsISelectionController*) GetSelectionController();
   NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection();
   NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame);
   NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame);
   NS_IMETHOD CreateEditor();
   NS_IMETHOD_(nsIContent*) GetRootEditorNode();
   NS_IMETHOD_(nsIContent*) CreatePlaceholderNode();
   NS_IMETHOD_(nsIContent*) GetPlaceholderNode();
-  NS_IMETHOD_(void) SetPlaceholderClass(bool aVisible, bool aNotify);
+  NS_IMETHOD_(void) SetPlaceholderVisibility(bool aVisible, bool aNotify);
+  NS_IMETHOD_(bool) GetPlaceholderVisibility();
   NS_IMETHOD_(void) InitializeKeyboardEventListeners();
   NS_IMETHOD_(void) OnValueChanged(bool aNotify);
   NS_IMETHOD_(bool) HasCachedSelection();
 
   void GetDisplayFileName(nsAString& aFileName) const;
   const nsCOMArray<nsIDOMFile>& GetFiles() const;
   void SetFiles(const nsCOMArray<nsIDOMFile>& aFiles, bool aSetValueChanged);
   void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged);
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -117,17 +117,18 @@ public:
   NS_IMETHOD_(nsISelectionController*) GetSelectionController();
   NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection();
   NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame);
   NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame);
   NS_IMETHOD CreateEditor();
   NS_IMETHOD_(nsIContent*) GetRootEditorNode();
   NS_IMETHOD_(nsIContent*) CreatePlaceholderNode();
   NS_IMETHOD_(nsIContent*) GetPlaceholderNode();
-  NS_IMETHOD_(void) SetPlaceholderClass(bool aVisible, bool aNotify);
+  NS_IMETHOD_(void) SetPlaceholderVisibility(bool aVisible, bool aNotify);
+  NS_IMETHOD_(bool) GetPlaceholderVisibility();
   NS_IMETHOD_(void) InitializeKeyboardEventListeners();
   NS_IMETHOD_(void) OnValueChanged(bool aNotify);
   NS_IMETHOD_(bool) HasCachedSelection();
 
   // nsIContent
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                nsIContent* aBindingParent,
                                bool aCompileEventHandlers);
@@ -508,19 +509,25 @@ nsHTMLTextAreaElement::CreatePlaceholder
 
 NS_IMETHODIMP_(nsIContent*)
 nsHTMLTextAreaElement::GetPlaceholderNode()
 {
   return mState.GetPlaceholderNode();
 }
 
 NS_IMETHODIMP_(void)
-nsHTMLTextAreaElement::SetPlaceholderClass(bool aVisible, bool aNotify)
+nsHTMLTextAreaElement::SetPlaceholderVisibility(bool aVisible, bool aNotify)
 {
-  mState.SetPlaceholderClass(aVisible, aNotify);
+  mState.SetPlaceholderVisibility(aVisible, aNotify);
+}
+
+NS_IMETHODIMP_(bool)
+nsHTMLTextAreaElement::GetPlaceholderVisibility()
+{
+  return mState.GetPlaceholderVisibility();
 }
 
 nsresult
 nsHTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
                                         bool aUserInput)
 {
   // Need to set the value changed flag here, so that
   // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -928,17 +928,18 @@ nsTextEditorState::nsTextEditorState(nsI
     mRestoringSelection(nullptr),
     mBoundFrame(nullptr),
     mTextListener(nullptr),
     mEverInited(false),
     mEditorInitialized(false),
     mInitializing(false),
     mValueTransferInProgress(false),
     mSelectionCached(true),
-    mSelectionRestoreEagerInit(false)
+    mSelectionRestoreEagerInit(false),
+    mPlaceholderVisibility(false)
 {
   MOZ_COUNT_CTOR(nsTextEditorState);
 }
 
 nsTextEditorState::~nsTextEditorState()
 {
   MOZ_COUNT_DTOR(nsTextEditorState);
   Clear();
@@ -1655,16 +1656,20 @@ be called if @placeholder is the empty s
 
   // Create the text node for the placeholder text before doing anything else
   rv = NS_NewTextNode(getter_AddRefs(placeholderText), pNodeInfoManager);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mPlaceholderDiv->AppendChildTo(placeholderText, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  mPlaceholderDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+                           NS_LITERAL_STRING("anonymous-div placeholder"),
+                           false);
+
   // initialize the text
   UpdatePlaceholderText(false);
 
   return NS_OK;
 }
 
 bool
 nsTextEditorState::GetMaxLength(int32_t* aMaxLength)
@@ -1964,17 +1969,17 @@ nsTextEditorState::ValueWasChanged(bool 
 {
   // placeholder management
   if (!mPlaceholderDiv) {
     return;
   }
 
   nsAutoString valueString;
   GetValue(valueString, true);
-  SetPlaceholderClass(valueString.IsEmpty(), aNotify);
+  SetPlaceholderVisibility(valueString.IsEmpty(), aNotify);
 }
 
 void
 nsTextEditorState::UpdatePlaceholderText(bool aNotify)
 {
   NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
                                 "mPlaceholderDiv isn't set");
 
@@ -1988,38 +1993,27 @@ nsTextEditorState::UpdatePlaceholderText
   content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
   nsContentUtils::RemoveNewlines(placeholderValue);
   NS_ASSERTION(mPlaceholderDiv->GetFirstChild(), "placeholder div has no child");
   mPlaceholderDiv->GetFirstChild()->SetText(placeholderValue, aNotify);
   ValueWasChanged(aNotify);
 }
 
 void
-nsTextEditorState::SetPlaceholderClass(bool aVisible,
-                                       bool aNotify)
+nsTextEditorState::SetPlaceholderVisibility(bool aVisible,
+                                            bool aNotify)
 {
   NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
                                 "mPlaceholderDiv isn't set");
 
-  // No need to do anything if we don't have a frame yet
-  if (!mBoundFrame)
-    return;
-
-  nsAutoString classValue;
-
-  classValue.Assign(NS_LITERAL_STRING("anonymous-div placeholder"));
+  mPlaceholderVisibility = aVisible;
 
-  if (!aVisible)
-    classValue.AppendLiteral(" hidden");
-
-  nsIContent* placeholderDiv = GetPlaceholderNode();
-  NS_ENSURE_TRUE_VOID(placeholderDiv);
-
-  placeholderDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
-                          classValue, aNotify);
+  if (mBoundFrame && aNotify) {
+    mBoundFrame->InvalidateFrame();
+  }
 }
 
 void
 nsTextEditorState::HideSelectionIfBlurred()
 {
   NS_ABORT_IF_FALSE(mSelCon, "Should have a selection controller if we have a frame!");
   nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
   if (!nsContentUtils::IsFocusedContent(content)) {
--- a/content/html/content/src/nsTextEditorState.h
+++ b/content/html/content/src/nsTextEditorState.h
@@ -169,17 +169,20 @@ public:
   int32_t GetWrapCols() {
     return mTextCtrlElement->GetWrapCols();
   }
   int32_t GetRows() {
     return mTextCtrlElement->GetRows();
   }
 
   // placeholder methods
-  void SetPlaceholderClass(bool aVisible, bool aNotify);
+  void SetPlaceholderVisibility(bool aVisible, bool aNotify);
+  bool GetPlaceholderVisibility() {
+    return mPlaceholderVisibility;
+  }
   void UpdatePlaceholderText(bool aNotify); 
 
   /**
    * Get the maxlength attribute
    * @param aMaxLength the value of the max length attr
    * @returns false if attr not defined
    */
   bool GetMaxLength(int32_t* aMaxLength);
@@ -273,11 +276,12 @@ private:
   mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
   bool mEverInited; // Have we ever been initialized?
   bool mEditorInitialized;
   bool mInitializing; // Whether we're in the process of initialization
   bool mValueTransferInProgress; // Whether a value is being transferred to the frame
   bool mSelectionCached; // Whether mSelectionProperties is valid
   mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
   SelectionProperties mSelectionProperties;
+  bool mPlaceholderVisibility;
 };
 
 #endif
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1302,17 +1302,17 @@ nsTextControlFrame::SetValueChanged(bool
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   if (mUsePlaceholder) {
     int32_t textLength;
     GetTextLength(&textLength);
 
     nsWeakFrame weakFrame(this);
-    txtCtrl->SetPlaceholderClass(!textLength, true);
+    txtCtrl->SetPlaceholderVisibility(!textLength, true);
     if (!weakFrame.IsAlive()) {
       return;
     }
   }
 
   txtCtrl->SetValueChanged(aValueChanged);
 }
 
@@ -1360,17 +1360,17 @@ nsTextControlFrame::UpdateValueDisplay(b
   }
 
   // Update the display of the placeholder value if needed.
   // We don't need to do this if we're about to initialize the
   // editor, since EnsureEditorInitialized takes care of this.
   if (mUsePlaceholder && !aBeforeEditorInit)
   {
     nsWeakFrame weakFrame(this);
-    txtCtrl->SetPlaceholderClass(value.IsEmpty(), aNotify);
+    txtCtrl->SetPlaceholderVisibility(value.IsEmpty(), aNotify);
     NS_ENSURE_STATE(weakFrame.IsAlive());
   }
 
   if (aBeforeEditorInit && value.IsEmpty()) {
     rootNode->RemoveChildAt(0, true);
     return NS_OK;
   }
 
@@ -1450,8 +1450,43 @@ nsTextControlFrame::RestoreState(nsPresS
 }
 
 NS_IMETHODIMP
 nsTextControlFrame::PeekOffset(nsPeekOffsetStruct *aPos)
 {
   return NS_ERROR_FAILURE;
 }
 
+NS_IMETHODIMP
+nsTextControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+                                     const nsRect&           aDirtyRect,
+                                     const nsDisplayListSet& aLists)
+{
+  /*
+   * The implementation of this method is equivalent as:
+   * nsContainerFrame::BuildDisplayList()
+   * with the difference that we filter-out the placeholder frame when it
+   * should not be visible.
+   */
+  DO_GLOBAL_REFLOW_COUNT_DSP("nsTextControlFrame");
+
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element!");
+
+  nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsIFrame* kid = mFrames.FirstChild();
+  nsDisplayListSet set(aLists, aLists.Content());
+
+  while (kid) {
+    // If the frame is the placeholder frame, we should only show it if the
+    // placeholder has to be visible.
+    if (kid->GetContent() != txtCtrl->GetPlaceholderNode() ||
+        txtCtrl->GetPlaceholderVisibility()) {
+      nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, 0);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    kid = kid->GetNextSibling();
+  }
+
+  return NS_OK;
+}
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -62,18 +62,16 @@ public:
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   virtual nsSize GetMinSize(nsBoxLayoutState& aBoxLayoutState);
   virtual bool IsCollapsed();
 
-  DECL_DO_GLOBAL_REFLOW_COUNT_DSP(nsTextControlFrame, nsContainerFrame)
-
   virtual bool IsLeaf() const;
   
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
 #endif
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
@@ -96,16 +94,20 @@ public:
   virtual void AppendAnonymousContentTo(nsBaseContentList& aElements,
                                         uint32_t aFilter) MOZ_OVERRIDE;
 
   // Utility methods to set current widget state
 
   NS_IMETHOD SetInitialChildList(ChildListID     aListID,
                                  nsFrameList&    aChildList) MOZ_OVERRIDE;
 
+  NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+                              const nsRect&           aDirtyRect,
+                              const nsDisplayListSet& aLists);
+
 //==== BEGIN NSIFORMCONTROLFRAME
   virtual void SetFocus(bool aOn , bool aRepaint); 
   virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue);
   virtual nsresult GetFormProperty(nsIAtom* aName, nsAString& aValue) const; 
 
 
 //==== END NSIFORMCONTROLFRAME
 
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -156,21 +156,16 @@ textarea > .placeholder {
   pointer-events: none;
   resize: none;
 }
 
 textarea > .placeholder {
   white-space: pre-wrap;
 }
 
-input > .placeholder.hidden,
-textarea > .placeholder.hidden {
-  visibility: hidden;
-}
-
 input:-moz-read-write,
 textarea:-moz-read-write {
   -moz-user-modify: read-write !important;
 }
 
 select {
   margin: 0;
   border-color: ThreeDFace;