Bug 553097 - Do not create a frame and DOM node for placeholder if not needed. r=ehsan,roc a=blocking
authorMounir Lamouri <mounir.lamouri@gmail.com>
Tue, 19 Oct 2010 15:11:07 +0200
changeset 56086 ed1eff4526ce30786f12d53b9a9097b13d1a546e
parent 56085 f218d3480d61c64a2133742703d99e6c0895cfea
child 56087 7d13edc2927276fc663646bbe38fd1fd62dc4dd1
push id16402
push usermlamouri@mozilla.com
push dateTue, 19 Oct 2010 13:12:47 +0000
treeherdermozilla-central@ed1eff4526ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, roc, blocking
bugs553097
milestone2.0b8pre
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 553097 - Do not create a frame and DOM node for placeholder if not needed. r=ehsan,roc a=blocking
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/generic/crashtests/crashtests.list
layout/reftests/forms/placeholder/placeholder-2-textarea.html
layout/reftests/forms/placeholder/placeholder-20.html
layout/reftests/forms/placeholder/placeholder-21.html
layout/reftests/forms/placeholder/placeholder-22.html
layout/reftests/forms/placeholder/placeholder-6-textarea.html
layout/reftests/forms/placeholder/placeholder-7.html
layout/reftests/forms/placeholder/placeholder-8.html
layout/reftests/forms/placeholder/placeholder-9.html
layout/reftests/forms/placeholder/placeholder-overflow-textarea-ref.html
layout/reftests/forms/placeholder/placeholder-readonly-focus-ref.html
layout/reftests/forms/placeholder/reftest.list
--- a/content/html/content/public/nsITextControlElement.h
+++ b/content/html/content/public/nsITextControlElement.h
@@ -170,16 +170,21 @@ public:
   NS_IMETHOD CreateEditor() = 0;
 
   /**
    * Get the anonymous root node for the text control.
    */
   NS_IMETHOD_(nsIContent*) GetRootEditorNode() = 0;
 
   /**
+   * Create the placeholder anonymous node for the text control and returns it.
+   */
+  NS_IMETHOD_(nsIContent*) CreatePlaceholderNode() = 0;
+
+  /**
    * Get the placeholder anonymous node for the text control.
    */
   NS_IMETHOD_(nsIContent*) GetPlaceholderNode() = 0;
 
   /**
    * Initialize the keyboard event listeners.
    */
   NS_IMETHOD_(void) InitializeKeyboardEventListeners() = 0;
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -1285,16 +1285,27 @@ nsHTMLInputElement::GetRootEditorNode()
   nsTextEditorState *state = GetEditorState();
   if (state) {
     return state->GetRootNode();
   }
   return nsnull;
 }
 
 NS_IMETHODIMP_(nsIContent*)
+nsHTMLInputElement::CreatePlaceholderNode()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    NS_ENSURE_SUCCESS(state->CreatePlaceholderNode(), nsnull);
+    return state->GetPlaceholderNode();
+  }
+  return nsnull;
+}
+
+NS_IMETHODIMP_(nsIContent*)
 nsHTMLInputElement::GetPlaceholderNode()
 {
   nsTextEditorState *state = GetEditorState();
   if (state) {
     return state->GetPlaceholderNode();
   }
   return nsnull;
 }
@@ -2742,16 +2753,18 @@ nsHTMLInputElement::GetAttributeChangeHi
     // We might need to rebuild our alt text.  Just go ahead and
     // reconstruct our frame.  This should be quite rare..
     NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
   } else if (aAttribute == nsGkAtoms::value) {
     NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
   } else if (aAttribute == nsGkAtoms::size &&
              IsSingleLineTextControl(PR_FALSE)) {
     NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
+  } else if (PlaceholderApplies() && aAttribute == nsGkAtoms::placeholder) {
+    NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
   }
   return retval;
 }
 
 NS_IMETHODIMP_(PRBool)
 nsHTMLInputElement::IsAttributeMapped(const nsIAtom* aAttribute) const
 {
   static const MappedAttributeEntry attributes[] = {
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -201,16 +201,17 @@ public:
   NS_IMETHOD_(void) SetTextEditorValue(const nsAString& aValue, PRBool aUserInput);
   NS_IMETHOD_(nsIEditor*) GetTextEditor();
   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) UpdatePlaceholderText(PRBool aNotify);
   NS_IMETHOD_(void) SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
   NS_IMETHOD_(void) InitializeKeyboardEventListeners();
   NS_IMETHOD_(void) OnValueChanged(PRBool aNotify);
 
   // nsIFileControlElement
   void GetDisplayFileName(nsAString& aFileName) const;
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -150,16 +150,17 @@ public:
   NS_IMETHOD_(void) SetTextEditorValue(const nsAString& aValue, PRBool aUserInput);
   NS_IMETHOD_(nsIEditor*) GetTextEditor();
   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) UpdatePlaceholderText(PRBool aNotify);
   NS_IMETHOD_(void) SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
   NS_IMETHOD_(void) InitializeKeyboardEventListeners();
   NS_IMETHOD_(void) OnValueChanged(PRBool aNotify);
 
   // nsIContent
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
@@ -497,16 +498,23 @@ nsHTMLTextAreaElement::CreateEditor()
 
 NS_IMETHODIMP_(nsIContent*)
 nsHTMLTextAreaElement::GetRootEditorNode()
 {
   return mState->GetRootNode();
 }
 
 NS_IMETHODIMP_(nsIContent*)
+nsHTMLTextAreaElement::CreatePlaceholderNode()
+{
+  NS_ENSURE_SUCCESS(mState->CreatePlaceholderNode(), nsnull);
+  return mState->GetPlaceholderNode();
+}
+
+NS_IMETHODIMP_(nsIContent*)
 nsHTMLTextAreaElement::GetPlaceholderNode()
 {
   return mState->GetPlaceholderNode();
 }
 
 NS_IMETHODIMP_(void)
 nsHTMLTextAreaElement::UpdatePlaceholderText(PRBool aNotify)
 {
@@ -619,16 +627,18 @@ nsHTMLTextAreaElement::GetAttributeChang
 {
   nsChangeHint retval =
       nsGenericHTMLFormElement::GetAttributeChangeHint(aAttribute, aModType);
   if (aAttribute == nsGkAtoms::rows ||
       aAttribute == nsGkAtoms::cols) {
     NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
   } else if (aAttribute == nsGkAtoms::wrap) {
     NS_UpdateHint(retval, nsChangeHint_ReconstructFrame);
+  } else if (aAttribute == nsGkAtoms::placeholder) {
+    NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
   }
   return retval;
 }
 
 NS_IMETHODIMP_(PRBool)
 nsHTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
 {
   static const MappedAttributeEntry* const map[] = {
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -1530,16 +1530,30 @@ nsTextEditorState::CreateRootNode()
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
 
 nsresult
 nsTextEditorState::CreatePlaceholderNode()
 {
+#ifdef DEBUG
+  {
+    nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+    if (content) {
+      nsAutoString placeholderTxt;
+      content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
+                       placeholderTxt);
+      nsContentUtils::RemoveNewlines(placeholderTxt);
+      NS_ASSERTION(!placeholderTxt.IsEmpty(), "CreatePlaceholderNode() shouldn't \
+be called if @placeholder is the empty string when trimmed from line breaks");
+    }
+  }
+#endif // DEBUG
+
   NS_ENSURE_TRUE(!mPlaceholderDiv, NS_ERROR_UNEXPECTED);
   NS_ENSURE_ARG_POINTER(mBoundFrame);
 
   nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
   NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
 
   nsIDocument *doc = shell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
@@ -1856,31 +1870,38 @@ nsTextEditorState::ShutDown()
   NS_IF_RELEASE(sNativeTextAreaBindings);
   NS_IF_RELEASE(sNativeInputBindings);
 }
 
 void
 nsTextEditorState::ValueWasChanged(PRBool aNotify)
 {
   // placeholder management
+  if (!mPlaceholderDiv) {
+    return;
+  }
+
   PRBool showPlaceholder = PR_FALSE;
   nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
   if (!nsContentUtils::IsFocusedContent(content)) {
     // If the content is focused, we don't care about the changes because
     // the placeholder is going to be hidden/shown on blur.
     nsAutoString valueString;
     GetValue(valueString, PR_TRUE);
     showPlaceholder = valueString.IsEmpty();
   }
   SetPlaceholderClass(showPlaceholder, aNotify);
 }
 
 void
 nsTextEditorState::UpdatePlaceholderText(PRBool aNotify)
 {
+  NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
+                                "mPlaceholderDiv isn't set");
+
   // If we don't have a placeholder div, there's nothing to do.
   if (!mPlaceholderDiv)
     return;
 
   nsAutoString placeholderValue;
 
   nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
   content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
@@ -1889,16 +1910,19 @@ nsTextEditorState::UpdatePlaceholderText
   mPlaceholderDiv->GetChildAt(0)->SetText(placeholderValue, aNotify);
   ValueWasChanged(aNotify);
 }
 
 void
 nsTextEditorState::SetPlaceholderClass(PRBool aVisible,
                                        PRBool 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"));
 
--- a/content/html/content/src/nsTextEditorState.h
+++ b/content/html/content/src/nsTextEditorState.h
@@ -156,24 +156,24 @@ public:
   nsresult PrepareEditor(const nsAString *aValue = nsnull);
   void InitializeKeyboardEventListeners();
 
   void SetValue(const nsAString& aValue, PRBool aUserInput);
   void GetValue(nsAString& aValue, PRBool aIgnoreWrap) const;
   void EmptyValue() { if (mValue) mValue->Truncate(); }
   PRBool IsEmpty() const { return mValue ? mValue->IsEmpty() : PR_TRUE; }
 
+  nsresult CreatePlaceholderNode();
+
   nsIContent* GetRootNode() {
     if (!mRootNode)
       CreateRootNode();
     return mRootNode;
   }
   nsIContent* GetPlaceholderNode() {
-    if (!mPlaceholderDiv)
-      CreatePlaceholderNode();
     return mPlaceholderDiv;
   }
 
   PRBool IsSingleLineTextControl() const {
     return mTextCtrlElement->IsSingleLineTextControl();
   }
   PRBool IsTextArea() const {
     return mTextCtrlElement->IsTextArea();
@@ -212,17 +212,16 @@ public:
 
 private:
   // not copy constructible
   nsTextEditorState(const nsTextEditorState&);
   // not assignable
   void operator= (const nsTextEditorState&);
 
   nsresult CreateRootNode();
-  nsresult CreatePlaceholderNode();
 
   void ValueWasChanged(PRBool aNotify);
 
   void DestroyEditor();
   void Clear();
 
   class InitializationGuard {
   public:
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -417,36 +417,48 @@ nsTextControlFrame::EnsureEditorInitiali
   mUseEditor = PR_TRUE;
 
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
 {
+  NS_ASSERTION(mContent, "We should have a content!");
+
   mState |= NS_FRAME_INDEPENDENT_SELECTION;
 
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   // Bind the frame to its text control
   nsresult rv = txtCtrl->BindToFrame(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIContent* rootNode = txtCtrl->GetRootEditorNode();
   NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY);
 
   if (!aElements.AppendElement(rootNode))
     return NS_ERROR_OUT_OF_MEMORY;
 
-  nsIContent* placeholderNode = txtCtrl->GetPlaceholderNode();
-  NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
+  // Do we need a placeholder node?
+  nsAutoString placeholderTxt;
+  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
+                    placeholderTxt);
+  nsContentUtils::RemoveNewlines(placeholderTxt);
+  mUsePlaceholder = !placeholderTxt.IsEmpty();
 
-  if (!aElements.AppendElement(placeholderNode))
-    return NS_ERROR_OUT_OF_MEMORY;
+  // Create the placeholder anonymous content if needed.
+  if (mUsePlaceholder) {
+    nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode();
+    NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
+
+    if (!aElements.AppendElement(placeholderNode))
+      return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   rv = UpdateValueDisplay(PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // textareas are eagerly initialized
   PRBool initEagerly = !IsSingleLineTextControl();
   if (!initEagerly) {
     nsCOMPtr<nsIDOMNSHTMLElement> element = do_QueryInterface(txtCtrl);
@@ -654,44 +666,47 @@ void nsTextControlFrame::SetFocus(PRBool
 {
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   // Revoke the previous scroll event if one exists
   mScrollEvent.Revoke();
 
   if (!aOn) {
-    nsWeakFrame weakFrame(this);
+    if (mUsePlaceholder) {
+      PRInt32 textLength;
+      GetTextLength(&textLength);
+
+      if (!textLength) {
+        nsWeakFrame weakFrame(this);
 
-    PRInt32 length;
-    nsresult rv = GetTextLength(&length);
-    NS_ENSURE_SUCCESS(rv, );
-    if (!length)
-      txtCtrl->SetPlaceholderClass(PR_TRUE, PR_TRUE);
+        txtCtrl->SetPlaceholderClass(PR_TRUE, PR_TRUE);
 
-    if (!weakFrame.IsAlive())
-    {
-      return;
+        if (!weakFrame.IsAlive()) {
+          return;
+        }
+      }
     }
 
     MaybeEndSecureKeyboardInput();
     return;
   }
 
   nsISelectionController* selCon = txtCtrl->GetSelectionController();
   if (!selCon)
     return;
 
-  nsWeakFrame weakFrame(this);
-
-  txtCtrl->SetPlaceholderClass(PR_FALSE, PR_TRUE);
+  if (mUsePlaceholder) {
+    nsWeakFrame weakFrame(this);
 
-  if (!weakFrame.IsAlive())
-  {
-    return;
+    txtCtrl->SetPlaceholderClass(PR_FALSE, PR_TRUE);
+
+    if (!weakFrame.IsAlive()) {
+      return;
+    }
   }
 
   if (NS_SUCCEEDED(InitFocusedValue()))
     MaybeBeginSecureKeyboardInput();
 
   nsCOMPtr<nsISelection> ourSel;
   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 
     getter_AddRefs(ourSel));
@@ -1150,27 +1165,16 @@ nsTextControlFrame::GetSelectionRange(PR
 /////END INTERFACE IMPLEMENTATIONS
 
 ////NSIFRAME
 NS_IMETHODIMP
 nsTextControlFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      PRInt32         aModType)
 {
-  // First, check for the placeholder attribute, because it doesn't
-  // depend on the editor being present.
-  if (nsGkAtoms::placeholder == aAttribute)
-  {
-    nsWeakFrame weakFrame(this);
-    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
-    NS_ASSERTION(txtCtrl, "Content not a text control element");
-    txtCtrl->UpdatePlaceholderText(PR_TRUE);
-    NS_ENSURE_STATE(weakFrame.IsAlive());
-  }
-
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
   nsISelectionController* selCon = txtCtrl->GetSelectionController();
   const PRBool needEditor = nsGkAtoms::maxlength == aAttribute ||
                             nsGkAtoms::readonly == aAttribute ||
                             nsGkAtoms::disabled == aAttribute ||
                             nsGkAtoms::spellcheck == aAttribute;
   nsCOMPtr<nsIEditor> editor;
@@ -1399,17 +1403,18 @@ nsTextControlFrame::UpdateValueDisplay(P
 
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
   nsIContent* rootNode = txtCtrl->GetRootEditorNode();
 
   NS_PRECONDITION(rootNode, "Must have a div content\n");
   NS_PRECONDITION(!mUseEditor,
                   "Do not call this after editor has been initialized");
-  NS_ASSERTION(txtCtrl->GetPlaceholderNode(), "A placeholder div must exist");
+  NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(),
+               "A placeholder div must exist");
 
   nsIContent *textContent = rootNode->GetChildAt(0);
   if (!textContent) {
     // Set up a textnode with our value
     nsCOMPtr<nsIContent> textNode;
     nsresult rv = NS_NewTextNode(getter_AddRefs(textNode),
                                  mContent->NodeInfo()->NodeInfoManager());
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1428,17 +1433,17 @@ nsTextControlFrame::UpdateValueDisplay(P
     value = *aValue;
   } else {
     txtCtrl->GetTextEditorValue(value, PR_TRUE);
   }
 
   // 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 (!aBeforeEditorInit)
+  if (mUsePlaceholder && !aBeforeEditorInit)
   {
     nsWeakFrame weakFrame(this);
     txtCtrl->SetPlaceholderClass(value.IsEmpty(), aNotify);
     NS_ENSURE_STATE(weakFrame.IsAlive());
   }
 
   if (aBeforeEditorInit && value.IsEmpty()) {
     rootNode->RemoveChildAt(0, PR_TRUE, PR_FALSE);
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -378,16 +378,18 @@ private:
   // these packed bools could instead use the high order bits on mState, saving 4 bytes 
   PRPackedBool mUseEditor;
   PRPackedBool mIsProcessing;
   PRPackedBool mNotifyOnInput;//default this to off to stop any notifications until setup is complete
   // Calls to SetValue will be treated as user values (i.e. trigger onChange
   // eventually) when mFireChangeEventState==true, this is used by nsFileControlFrame.
   PRPackedBool mFireChangeEventState;
   PRPackedBool mInSecureKeyboardInputMode;
+  // Keep track if we have asked a placeholder node creation.
+  PRPackedBool mUsePlaceholder;
 
 #ifdef DEBUG
   PRPackedBool mInEditorInitialization;
   friend class EditorInitializerEntryTracker;
 #endif
 
   nsString mFocusedValue;
   nsRevocableEventPtr<ScrollOnFocusEvent> mScrollEvent;
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -9,17 +9,17 @@ load 289864-1.html
 load 295292-1.html
 load 295292-2.html
 load 302260-1.html
 load 307979-1.html
 load 310556-1.xhtml
 load 322780-1.xul
 load 323381-1.html
 load 323381-2.html
-asserts(2) asserts-if(gtk2Widget,12) load 323386-1.html # Bug 575011
+asserts(2) asserts-if(gtk2Widget,8) load 323386-1.html # Bug 575011
 load 323389-1.html
 load 323389-2.html
 load 323493-1.html
 load 323495-1.html
 load 324318-1.html
 load 328946-1.html
 load 331284-1.xhtml
 load 334105-1.xhtml
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/placeholder/placeholder-2-textarea.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: placeholder has to be used if set via javascript -->
+  <script type="text/javascript">
+    function setPlaceholder()
+    {
+      document.getElementById('p1').placeholder = "my placeholder";
+    }
+    function disableReftestWait()
+    {
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="setPlaceholder(); disableReftestWait();">
+    <textarea id="p1" placeholder=""></textarea>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/placeholder/placeholder-20.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when focused, placeholder update shouldn't show placeholder. -->
+  <script type="text/javascript">
+    function focusPlaceholder()
+    {
+      document.getElementById('p1').focus();
+    }
+    function setPlaceholder()
+    {
+      document.getElementById('p1').placeholder = 'new placeholder';
+    }
+    function disableReftestWait()
+    {
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="focusPlaceholder();">
+    <input type="text" id="p1" value="" onfocus="setPlaceholder(); disableReftestWait();">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/placeholder/placeholder-21.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when focused, placeholder update shouldn't show placeholder. -->
+  <script type="text/javascript">
+    function focusPlaceholder()
+    {
+      document.getElementById('p1').focus();
+    }
+    function setPlaceholder()
+    {
+      document.getElementById('p1').removeAttribute('placeholder');
+    }
+    function disableReftestWait()
+    {
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="focusPlaceholder();">
+    <input type="text" id="p1" value="" placeholder="my placeholder" onfocus="setPlaceholder(); disableReftestWait();">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/placeholder/placeholder-22.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when focused, placeholder update shouldn't show placeholder. -->
+  <script type="text/javascript">
+    function focusPlaceholder()
+    {
+      document.getElementById('p1').focus();
+    }
+    function setPlaceholder()
+    {
+      document.getElementById('p1').placeholder = '';
+    }
+    function disableReftestWait()
+    {
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="focusPlaceholder();">
+    <input type="text" id="p1" value="" placeholder="my placeholder" onfocus="setPlaceholder(); disableReftestWait();">
+  </body>
+</html>
--- a/layout/reftests/forms/placeholder/placeholder-6-textarea.html
+++ b/layout/reftests/forms/placeholder/placeholder-6-textarea.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
   <!-- Test placeholder behavior when textarea is too small -->
   <body>
-    <textarea cols="5" placeholder="my placeholder"></textarea>
+    <textarea cols="5" rows="3" placeholder="my placeholder"></textarea>
   </body>
 </html>
--- a/layout/reftests/forms/placeholder/placeholder-7.html
+++ b/layout/reftests/forms/placeholder/placeholder-7.html
@@ -8,11 +8,11 @@
     }
     function disableReftestWait()
     {
       document.documentElement.className = '';
     }
   </script>
 
   <body onload="focusPlaceholder();">
-    <input type="text" id="p1" value="" placeholder="my placeholder" readonly onfocus="disableReftestWait();">
+    <input type="text" id="p1" value="" placeholder="my placeholder" onfocus="disableReftestWait();">
   </body>
 </html>
--- a/layout/reftests/forms/placeholder/placeholder-8.html
+++ b/layout/reftests/forms/placeholder/placeholder-8.html
@@ -12,11 +12,11 @@
     }
     function disableReftestWait()
     {
       document.documentElement.className = '';
     }
   </script>
 
   <body onload="focusPlaceholder();">
-    <input type="text" id="p1" value="" placeholder="my placeholder" readonly onfocus="setPlaceholder(); disableReftestWait();">
+    <input type="text" id="p1" value="" placeholder="my placeholder" onfocus="setPlaceholder(); disableReftestWait();">
   </body>
 </html>
--- a/layout/reftests/forms/placeholder/placeholder-9.html
+++ b/layout/reftests/forms/placeholder/placeholder-9.html
@@ -12,11 +12,11 @@
     }
     function disableReftestWait()
     {
       document.documentElement.className = '';
     }
   </script>
 
   <body onload="focusPlaceholder();">
-    <input type="text" id="p1" value="my value" placeholder="my placeholder" readonly onfocus="resetValue(); disableReftestWait();">
+    <input type="text" id="p1" value="my value" placeholder="my placeholder" onfocus="resetValue(); disableReftestWait();">
   </body>
 </html>
--- a/layout/reftests/forms/placeholder/placeholder-overflow-textarea-ref.html
+++ b/layout/reftests/forms/placeholder/placeholder-overflow-textarea-ref.html
@@ -3,11 +3,11 @@
   <!--
     This test 'emulate' the placeholder by setting a style
     to the value of a textarea.
     This test may break if placeholder default style is changed.
   -->
   <link rel='stylesheet' type='text/css' href='placeholder-style.css'>
 
   <body>
-    <textarea class="placeholder" cols="5">my placeholder</textarea>
+    <textarea class="placeholder" cols="5" rows="3">my placeholder</textarea>
   </body>
 </html>
deleted file mode 100644
--- a/layout/reftests/forms/placeholder/placeholder-readonly-focus-ref.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-  <script type="text/javascript">
-    function focusInput()
-    {
-      document.getElementById('t1').focus();
-    }
-    function disableReftestWait()
-    {
-      document.documentElement.className = '';
-    }
-  </script>
-  <body onload="focusInput();">
-    <input id='t1' type="text" readonly onfocus="disableReftestWait();">
-  </body>
-</html>
--- a/layout/reftests/forms/placeholder/reftest.list
+++ b/layout/reftests/forms/placeholder/reftest.list
@@ -1,22 +1,26 @@
 == placeholder-1-text.html placeholder-visible-ref.html
 == placeholder-1-password.html placeholder-visible-ref.html
 == placeholder-1-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-2.html placeholder-visible-ref.html
+== placeholder-2-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-3.html placeholder-overridden-ref.html
 == placeholder-4.html placeholder-overridden-ref.html
 == placeholder-5.html placeholder-visible-ref.html
 == placeholder-6.html placeholder-overflow-ref.html
 == placeholder-6-textarea.html placeholder-overflow-textarea-ref.html
-== placeholder-7.html placeholder-readonly-focus-ref.html
-== placeholder-8.html placeholder-readonly-focus-ref.html
-== placeholder-9.html placeholder-readonly-focus-ref.html
+== placeholder-7.html placeholder-focus-ref.html
+== placeholder-8.html placeholder-focus-ref.html
+== placeholder-9.html placeholder-focus-ref.html
 == placeholder-10.html placeholder-visible-ref.html
 == placeholder-11.html placeholder-visible-ref.html
 == placeholder-12.html placeholder-visible-ref.html
 == placeholder-13.html placeholder-visible-ref.html
 == placeholder-14.html placeholder-visible-ref.html
 == placeholder-15.html placeholder-focus-ref.html
 == placeholder-16.html placeholder-focus-ref.html
 == placeholder-17.html placeholder-focus-ref.html
 == placeholder-18.html placeholder-overridden-ref.html
 == placeholder-19.xul  placeholder-overridden-ref.xul
+== placeholder-20.html placeholder-focus-ref.html
+== placeholder-21.html placeholder-focus-ref.html
+== placeholder-22.html placeholder-focus-ref.html