CSS: Implement the :placeholder-shown pseudo-class from Selectors Level 4
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Aug 2014 03:33:18 +0000 (03:33 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Aug 2014 03:33:18 +0000 (03:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=118162

Reviewed by Antti Koivisto.

Source/WebCore:

Previously, HTMLTextFormControlElement was using some mix of its own state
and style to change the visibility of the placeholder. That approach was a little
bit too fragile, and we do not want the style to depends on the renderer() since
that creates circular dependencies.

The biggest change here is refactoring HTMLTextFormControlElement to have
1) An explicit "visible placeholder" state.
2) Separate the textUpdate() from the visibilityUpdate().
3) Remove the dependencies between the Element's style and the placeholder's style.
   This is done by simply using display:none; on the placeholder so that its parent's visibility
   is irrelevant.

When matching the selector, the style is set as unique since style sharing does not deal with
the changes of HTMLTextFormControlElement.

Tests: fast/css/placeholder-shown-basics.html
       fast/selectors/placeholder-shown-long-adjacent-backtracking.html
       fast/selectors/placeholder-shown-sibling-style-update.html
       fast/selectors/placeholder-shown-style-update.html
       fast/selectors/placeholder-shown-with-input-basics.html
       fast/selectors/placeholder-shown-with-textarea-basics.html

* css/CSSSelector.cpp:
(WebCore::CSSSelector::selectorText):
Add the CSS Selector description for CSSOM.

* css/CSSSelector.h:
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne):
* css/SelectorCheckerTestFunctions.h:
(WebCore::isPlaceholderShown):
* css/SelectorPseudoClassAndCompatibilityElementMap.in:
* css/html.css:
(::-webkit-input-placeholder):
Previously, the display was forced through the UA stylesheet. Since the display is now part
of the placeholder visibility, it is explicitly handled by HTMLTextFormControlElement and
its subclasses.

* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::addPseudoClassType):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementMatching):
(WebCore::SelectorCompiler::makeUniqueIfNecessaryAndTestIsPlaceholderShown):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementHasPlaceholderShown):
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::parseAttribute):
* html/HTMLTextAreaElement.cpp:
(WebCore::HTMLTextAreaElement::updateValue):
(WebCore::HTMLTextAreaElement::setValueCommon):
(WebCore::HTMLTextAreaElement::updatePlaceholderText):
* html/HTMLTextFormControlElement.cpp:
(WebCore::HTMLTextFormControlElement::HTMLTextFormControlElement):
(WebCore::HTMLTextFormControlElement::dispatchFocusEvent):
(WebCore::HTMLTextFormControlElement::dispatchBlurEvent):
(WebCore::HTMLTextFormControlElement::placeholderShouldBeVisible):
(WebCore::HTMLTextFormControlElement::updatePlaceholderVisibility):
(WebCore::HTMLTextFormControlElement::selectionDirection):
(WebCore::HTMLTextFormControlElement::restoreCachedSelection):
(WebCore::HTMLTextFormControlElement::parseAttribute):
(WebCore::HTMLTextFormControlElement::hidePlaceholder):
(WebCore::HTMLTextFormControlElement::showPlaceholderIfNecessary):
* html/HTMLTextFormControlElement.h:
(WebCore::HTMLTextFormControlElement::isPlaceholderVisible):
(WebCore::HTMLTextFormControlElement::cachedSelectionDirection):
* html/TextFieldInputType.cpp:
(WebCore::TextFieldInputType::updatePlaceholderText):
(WebCore::TextFieldInputType::subtreeHasChanged):
(WebCore::TextFieldInputType::updateInnerTextValue):
* rendering/RenderTextControl.cpp:
(WebCore::RenderTextControl::styleDidChange):
* testing/Internals.cpp:
(WebCore::Internals::visiblePlaceholder):

LayoutTests:

Add basic test coverage for common operations: styling, querySelector, CSSOM.

The layout test failure of placeholder-shown-sibling-style-update.html seems unrelated
to this patch, it fails in many more cases. This will be investigated separately, the failure
are used as expected values for now.

* fast/css/css-selector-text-expected.txt:
* fast/css/css-selector-text.html:
* fast/css/css-set-selector-text-expected.txt:
* fast/css/css-set-selector-text.html:
* fast/css/placeholder-shown-basics-expected.html: Added.
* fast/css/placeholder-shown-basics.html: Added.
* fast/selectors/placeholder-shown-long-adjacent-backtracking-expected.txt: Added.
* fast/selectors/placeholder-shown-long-adjacent-backtracking.html: Added.
* fast/selectors/placeholder-shown-sibling-style-update-expected.txt: Added.
* fast/selectors/placeholder-shown-sibling-style-update.html: Added.
* fast/selectors/placeholder-shown-style-update-expected.txt: Added.
* fast/selectors/placeholder-shown-style-update.html: Added.
* fast/selectors/placeholder-shown-with-input-basics-expected.txt: Added.
* fast/selectors/placeholder-shown-with-input-basics.html: Added.
* fast/selectors/placeholder-shown-with-textarea-basics-expected.txt: Added.
* fast/selectors/placeholder-shown-with-textarea-basics.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@172826 268f45cc-cd09-0410-ab3c-d52691b4dbfc

34 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/css-selector-text-expected.txt
LayoutTests/fast/css/css-selector-text.html
LayoutTests/fast/css/css-set-selector-text-expected.txt
LayoutTests/fast/css/css-set-selector-text.html
LayoutTests/fast/css/placeholder-shown-basics-expected.html [new file with mode: 0644]
LayoutTests/fast/css/placeholder-shown-basics.html [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-long-adjacent-backtracking-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-long-adjacent-backtracking.html [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-sibling-style-update-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-sibling-style-update.html [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-style-update-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-style-update.html [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-with-input-basics-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-with-input-basics.html [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-with-textarea-basics-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/placeholder-shown-with-textarea-basics.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/css/CSSSelector.cpp
Source/WebCore/css/CSSSelector.h
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/css/SelectorCheckerTestFunctions.h
Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in
Source/WebCore/css/html.css
Source/WebCore/cssjit/SelectorCompiler.cpp
Source/WebCore/html/HTMLInputElement.cpp
Source/WebCore/html/HTMLTextAreaElement.cpp
Source/WebCore/html/HTMLTextFormControlElement.cpp
Source/WebCore/html/HTMLTextFormControlElement.h
Source/WebCore/html/TextFieldInputType.cpp
Source/WebCore/rendering/RenderTextControl.cpp
Source/WebCore/testing/Internals.cpp
Source/WebKit/WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in

index 05bb7c8..78969f7 100644 (file)
@@ -1,3 +1,33 @@
+2014-08-20  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS: Implement the :placeholder-shown pseudo-class from Selectors Level 4
+        https://bugs.webkit.org/show_bug.cgi?id=118162
+
+        Reviewed by Antti Koivisto.
+
+        Add basic test coverage for common operations: styling, querySelector, CSSOM.
+
+        The layout test failure of placeholder-shown-sibling-style-update.html seems unrelated
+        to this patch, it fails in many more cases. This will be investigated separately, the failure
+        are used as expected values for now.
+
+        * fast/css/css-selector-text-expected.txt:
+        * fast/css/css-selector-text.html:
+        * fast/css/css-set-selector-text-expected.txt:
+        * fast/css/css-set-selector-text.html:
+        * fast/css/placeholder-shown-basics-expected.html: Added.
+        * fast/css/placeholder-shown-basics.html: Added.
+        * fast/selectors/placeholder-shown-long-adjacent-backtracking-expected.txt: Added.
+        * fast/selectors/placeholder-shown-long-adjacent-backtracking.html: Added.
+        * fast/selectors/placeholder-shown-sibling-style-update-expected.txt: Added.
+        * fast/selectors/placeholder-shown-sibling-style-update.html: Added.
+        * fast/selectors/placeholder-shown-style-update-expected.txt: Added.
+        * fast/selectors/placeholder-shown-style-update.html: Added.
+        * fast/selectors/placeholder-shown-with-input-basics-expected.txt: Added.
+        * fast/selectors/placeholder-shown-with-input-basics.html: Added.
+        * fast/selectors/placeholder-shown-with-textarea-basics-expected.txt: Added.
+        * fast/selectors/placeholder-shown-with-textarea-basics.html: Added.
+
 2014-08-20  Benjamin Poulain  <bpoulain@apple.com>
 
         Remove HTMLInputElement's suggestedValue
index 5c6a211..6100c5a 100644 (file)
@@ -40,6 +40,8 @@ PASS parseThenSerializeRule(':focus { }') is ':focus { }'
 PASS parseThenSerializeRule(':hover { }') is ':hover { }'
 PASS parseThenSerializeRule(':indeterminate { }') is ':indeterminate { }'
 PASS parseThenSerializeRule(':link { }') is ':link { }'
+PASS parseThenSerializeRule(':not(:placeholder-shown) { }') is ':not(:placeholder-shown) { }'
+PASS parseThenSerializeRule(':placeholder-shown { }') is ':placeholder-shown { }'
 PASS parseThenSerializeRule(':root { }') is ':root { }'
 PASS parseThenSerializeRule(':target { }') is ':target { }'
 PASS parseThenSerializeRule(':visited { }') is ':visited { }'
index 875d04a..440e69b 100644 (file)
@@ -71,6 +71,8 @@ testSelectorRoundTrip(":focus");
 testSelectorRoundTrip(":hover");
 testSelectorRoundTrip(":indeterminate");
 testSelectorRoundTrip(":link");
+testSelectorRoundTrip(":not(:placeholder-shown)");
+testSelectorRoundTrip(":placeholder-shown");
 testSelectorRoundTrip(":root");
 testSelectorRoundTrip(":target");
 testSelectorRoundTrip(":visited");
index b786352..5bcc3bf 100644 (file)
@@ -50,6 +50,8 @@ PASS setThenReadSelectorText(':focus') is ':focus'
 PASS setThenReadSelectorText(':hover') is ':hover'
 PASS setThenReadSelectorText(':indeterminate') is ':indeterminate'
 PASS setThenReadSelectorText(':link') is ':link'
+PASS setThenReadSelectorText(':not(:placeholder-shown)') is ':not(:placeholder-shown)'
+PASS setThenReadSelectorText(':placeholder-shown') is ':placeholder-shown'
 PASS setThenReadSelectorText(':root') is ':root'
 PASS setThenReadSelectorText(':target') is ':target'
 PASS setThenReadSelectorText(':visited') is ':visited'
index c53a1f3..fe385d8 100644 (file)
@@ -88,6 +88,8 @@ testSelectorRoundTrip(":focus");
 testSelectorRoundTrip(":hover");
 testSelectorRoundTrip(":indeterminate");
 testSelectorRoundTrip(":link");
+testSelectorRoundTrip(":not(:placeholder-shown)");
+testSelectorRoundTrip(":placeholder-shown");
 testSelectorRoundTrip(":root");
 testSelectorRoundTrip(":target");
 testSelectorRoundTrip(":visited");
diff --git a/LayoutTests/fast/css/placeholder-shown-basics-expected.html b/LayoutTests/fast/css/placeholder-shown-basics-expected.html
new file mode 100644 (file)
index 0000000..22d3e7c
--- /dev/null
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        input, textarea {
+            height: 8px;
+        }
+        .placeholder-shown {
+            background-color: green;
+        }
+    </style>
+</head>
+<body>
+    <p>This test checks how various input elements are styled when :placeholder-shown is applied.</p>
+    <div>
+        <textarea></textarea>
+        <textarea placeholder></textarea>
+        <textarea placeholder="WebKit" class="placeholder-shown"></textarea>
+        <textarea placeholder="WebKit">Foobar</textarea>
+    </div>
+    <div>
+        <input>
+        <input placeholder>
+        <input placeholder="WebKit" class="placeholder-shown">
+        <input placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="">
+        <input type="" placeholder>
+        <input type="" placeholder="WebKit" class="placeholder-shown">
+        <input type="" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="hidden">
+        <input type="hidden" placeholder>
+        <input type="hidden" placeholder="WebKit" class="placeholder-shown">
+        <input type="hidden" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="text">
+        <input type="text" placeholder>
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="tel">
+        <input type="tel" placeholder>
+        <input type="tel" placeholder="WebKit" class="placeholder-shown">
+        <input type="tel" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="url">
+        <input type="url" placeholder>
+        <input type="url" placeholder="WebKit" class="placeholder-shown">
+        <input type="url" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="email">
+        <input type="email" placeholder>
+        <input type="email" placeholder="WebKit" class="placeholder-shown">
+        <input type="email" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="password">
+        <input type="password" placeholder>
+        <input type="password" placeholder="WebKit" class="placeholder-shown">
+        <input type="password" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="datetime">
+        <input type="datetime" placeholder>
+        <input type="datetime" placeholder="WebKit" class="placeholder-shown">
+        <input type="datetime" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="number">
+        <input type="number" placeholder>
+        <input type="number" placeholder="WebKit" class="placeholder-shown">
+        <input type="number" placeholder="WebKit" value="0">
+    </div>
+    <div>
+        <input type="range">
+        <input type="range" placeholder>
+        <input type="range" placeholder="WebKit">
+        <input type="range" placeholder="WebKit" value="1">
+    </div>
+    <div>
+        <input type="checkbox">
+        <input type="checkbox" placeholder>
+        <input type="checkbox" placeholder="WebKit">
+        <input type="checkbox" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="radio">
+        <input type="radio" placeholder>
+        <input type="radio" placeholder="WebKit">
+        <input type="radio" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="submit">
+        <input type="submit" placeholder>
+        <input type="submit" placeholder="WebKit">
+        <input type="submit" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="color">
+        <input type="color" placeholder>
+        <input type="color" placeholder="WebKit">
+        <input type="color" placeholder="WebKit" value="Foobar">
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css/placeholder-shown-basics.html b/LayoutTests/fast/css/placeholder-shown-basics.html
new file mode 100644 (file)
index 0000000..4ec40f6
--- /dev/null
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        input, textarea {
+            height: 8px;
+        }
+        :placeholder-shown {
+            background-color: green;
+        }
+    </style>
+</head>
+<body>
+    <p>This test checks how various input elements are styled when :placeholder-shown is applied.</p>
+    <div>
+        <textarea></textarea>
+        <textarea placeholder></textarea>
+        <textarea placeholder="WebKit"></textarea>
+        <textarea placeholder="WebKit">Foobar</textarea>
+    </div>
+    <div>
+        <input>
+        <input placeholder>
+        <input placeholder="WebKit">
+        <input placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="">
+        <input type="" placeholder>
+        <input type="" placeholder="WebKit">
+        <input type="" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="hidden">
+        <input type="hidden" placeholder>
+        <input type="hidden" placeholder="WebKit">
+        <input type="hidden" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="text">
+        <input type="text" placeholder>
+        <input type="text" placeholder="WebKit">
+        <input type="text" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="tel">
+        <input type="tel" placeholder>
+        <input type="tel" placeholder="WebKit">
+        <input type="tel" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="url">
+        <input type="url" placeholder>
+        <input type="url" placeholder="WebKit">
+        <input type="url" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="email">
+        <input type="email" placeholder>
+        <input type="email" placeholder="WebKit">
+        <input type="email" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="password">
+        <input type="password" placeholder>
+        <input type="password" placeholder="WebKit">
+        <input type="password" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="datetime">
+        <input type="datetime" placeholder>
+        <input type="datetime" placeholder="WebKit">
+        <input type="datetime" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="number">
+        <input type="number" placeholder>
+        <input type="number" placeholder="WebKit">
+        <input type="number" placeholder="WebKit" value="0">
+    </div>
+    <div>
+        <input type="range">
+        <input type="range" placeholder>
+        <input type="range" placeholder="WebKit">
+        <input type="range" placeholder="WebKit" value="1">
+    </div>
+    <div>
+        <input type="checkbox">
+        <input type="checkbox" placeholder>
+        <input type="checkbox" placeholder="WebKit">
+        <input type="checkbox" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="radio">
+        <input type="radio" placeholder>
+        <input type="radio" placeholder="WebKit">
+        <input type="radio" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="submit">
+        <input type="submit" placeholder>
+        <input type="submit" placeholder="WebKit">
+        <input type="submit" placeholder="WebKit" value="Foobar">
+    </div>
+    <div>
+        <input type="color">
+        <input type="color" placeholder>
+        <input type="color" placeholder="WebKit">
+        <input type="color" placeholder="WebKit" value="Foobar">
+    </div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/selectors/placeholder-shown-long-adjacent-backtracking-expected.txt b/LayoutTests/fast/selectors/placeholder-shown-long-adjacent-backtracking-expected.txt
new file mode 100644 (file)
index 0000000..6b49233
--- /dev/null
@@ -0,0 +1,75 @@
+Test backtracking of a long chain of :placeholder-shown siblings to catch any issue with register allocation.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)").length is 3
+PASS document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)")[0] is document.getElementById("target1")
+PASS document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)")[1] is document.getElementById("target2")
+PASS document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)")[2] is document.getElementById("target3")
+PASS document.querySelectorAll("input").length is 61
+PASS getComputedStyle(document.querySelectorAll("input")[0]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[1]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[2]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[3]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[4]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[5]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[6]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[7]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[8]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[9]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[10]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[11]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[12]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[13]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[14]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[15]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[16]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[17]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[18]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[19]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[20]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("input")[21]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[22]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[23]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[24]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[25]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[26]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[27]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[28]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[29]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[30]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[31]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[32]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[33]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[34]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[35]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[36]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[37]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[38]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[39]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[40]).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelectorAll("input")[41]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[42]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[43]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[44]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[45]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[46]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[47]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[48]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[49]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[50]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[51]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[52]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[53]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[54]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[55]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[56]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[57]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[58]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[59]).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.querySelectorAll("input")[60]).backgroundColor is "rgb(1, 2, 3)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/placeholder-shown-long-adjacent-backtracking.html b/LayoutTests/fast/selectors/placeholder-shown-long-adjacent-backtracking.html
new file mode 100644 (file)
index 0000000..eb75b5c
--- /dev/null
@@ -0,0 +1,104 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+input {
+    background-color: white;
+}
+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown) {
+    background-color: rgb(1, 2, 3);
+}
+</style>
+</head>
+<body>
+    <div style="display:none">
+        <!-- 20 empty -->
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+
+        <input id="target1" type="text" placeholder="WebKit" class="placeholder-not-shown" value="Not Shown!">
+
+        <!-- 19 empty -->
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+
+        <input id="target2" type="text" placeholder="WebKit" class="placeholder-not-shown" value="Not Shown!">
+
+        <!-- 19 empty -->
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+        <input type="text" placeholder="WebKit" class="placeholder-shown">
+
+        <input id="target3" type="text" placeholder="WebKit" class="placeholder-not-shown" value="Not Shown!">
+    </div>
+</body>
+<script>
+description('Test backtracking of a long chain of :placeholder-shown siblings to catch any issue with register allocation.');
+
+shouldBe('document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)").length', '3');
+shouldBe('document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)")[0]', 'document.getElementById("target1")');
+shouldBe('document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)")[1]', 'document.getElementById("target2")');
+shouldBe('document.querySelectorAll(":placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown+:placeholder-shown~:not(:placeholder-shown)")[2]', 'document.getElementById("target3")');
+
+shouldBe('document.querySelectorAll("input").length', '61')
+
+var allTestCases = document.querySelectorAll("input");
+for (var i = 0; i < 61; ++i)
+    shouldBeEqualToString('getComputedStyle(document.querySelectorAll("input")[' + i + ']).backgroundColor', allTestCases[i].classList.contains('placeholder-shown') ? 'rgb(255, 255, 255)' : 'rgb(1, 2, 3)');
+
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/placeholder-shown-sibling-style-update-expected.txt b/LayoutTests/fast/selectors/placeholder-shown-sibling-style-update-expected.txt
new file mode 100644 (file)
index 0000000..04b17ed
--- /dev/null
@@ -0,0 +1,19 @@
+Test style update of the :placeholder-shown pseudo class.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initial state is without placehoder.
+PASS getComputedStyle(document.getElementById("target1")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("target2")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("target3")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("target4")).backgroundColor is "rgb(255, 255, 255)"
+Adding a placeholder, the targets should match the style.
+FAIL getComputedStyle(document.getElementById("target1")).backgroundColor should be rgb(1, 2, 3). Was rgb(255, 255, 255).
+FAIL getComputedStyle(document.getElementById("target2")).backgroundColor should be rgb(1, 2, 3). Was rgb(255, 255, 255).
+FAIL getComputedStyle(document.getElementById("target3")).backgroundColor should be rgb(1, 2, 3). Was rgb(255, 255, 255).
+FAIL getComputedStyle(document.getElementById("target4")).backgroundColor should be rgb(1, 2, 3). Was rgb(255, 255, 255).
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  
diff --git a/LayoutTests/fast/selectors/placeholder-shown-sibling-style-update.html b/LayoutTests/fast/selectors/placeholder-shown-sibling-style-update.html
new file mode 100644 (file)
index 0000000..8a26efc
--- /dev/null
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+target {
+    background-color: white;
+}
+:placeholder-shown + target {
+    background-color: rgb(1, 2, 3);
+}
+</style>
+</head>
+<body>
+    <div>
+        <input id="input-with-renderer">
+        <target id="target1"></target>
+        <textarea id="textarea-with-renderer"></textarea>
+        <target id="target2"></target>
+    </div>
+    <div style="display:none;">
+        <input id="input-without-renderer">
+        <target id="target3"></target>
+        <textarea id="textarea-without-renderer"></textarea>
+        <target id="target4"></target>
+    </div>
+</body>
+<script>
+description('Test style update of the :placeholder-shown pseudo class.');
+
+function testBackgroundColor(expectMatch) {
+    shouldBeEqualToString('getComputedStyle(document.getElementById("target1")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.getElementById("target2")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.getElementById("target3")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.getElementById("target4")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+}
+
+var inputCaseWithRenderer = document.getElementById("input-with-renderer");
+var textareaCaseWithRenderer = document.getElementById("textarea-with-renderer");
+var inputCaseWithoutRenderer = document.getElementById("input-without-renderer");
+var textareaCaseWithoutRenderer = document.getElementById("textarea-without-renderer");
+
+function setAttribute(attribute, value) {
+    inputCaseWithRenderer.setAttribute(attribute, value);
+    textareaCaseWithRenderer.setAttribute(attribute, value);
+    inputCaseWithoutRenderer.setAttribute(attribute, value);
+    textareaCaseWithoutRenderer.setAttribute(attribute, value);
+}
+
+debug("Initial state is without placehoder.");
+testBackgroundColor(false);
+
+debug("Adding a placeholder, the targets should match the style.");
+setAttribute("placeholder", "WebKit")
+testBackgroundColor(true);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/placeholder-shown-style-update-expected.txt b/LayoutTests/fast/selectors/placeholder-shown-style-update-expected.txt
new file mode 100644 (file)
index 0000000..796594d
--- /dev/null
@@ -0,0 +1,69 @@
+Test style update of the :placeholder-shown pseudo class.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initial state is without placehoder.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Adding a valid placeholder matches.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+Using an invalid placeholder value does not match.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Adding back a placehoder and an empty value. An empty value does not prevent matching.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+Changing the type of the input to something that does not support placeholder. The input element should not match.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+Changing the type of the input to text should add the style back.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+Adding a non empty value should remove the style.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Removing the placeholder, we should not match.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Removing the value. We should still not match since the placeholder attribute was removed.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Putting back a value, no reason to match.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Adding back the placeholder, the value is still there so we cannot match yet.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(255, 255, 255)"
+Removing the value. A placeholder was added while the value was up, we should get the style now.
+PASS getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor is "rgb(1, 2, 3)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  
diff --git a/LayoutTests/fast/selectors/placeholder-shown-style-update.html b/LayoutTests/fast/selectors/placeholder-shown-style-update.html
new file mode 100644 (file)
index 0000000..eadc423
--- /dev/null
@@ -0,0 +1,115 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+input, textarea {
+    background-color: white;
+}
+:placeholder-shown {
+    background-color: rgb(1, 2, 3);
+}
+</style>
+</head>
+<body>
+    <div>
+        <input id="input-with-renderer">
+        <textarea id="textarea-with-renderer"></textarea>
+    </div>
+    <div style="display:none;">
+        <input id="input-without-renderer">
+        <textarea id="textarea-without-renderer"></textarea>
+    </div>
+</body>
+<script>
+description('Test style update of the :placeholder-shown pseudo class.');
+
+var inputCaseWithRenderer = document.getElementById("input-with-renderer");
+var textareaCaseWithRenderer = document.getElementById("textarea-with-renderer");
+var inputCaseWithoutRenderer = document.getElementById("input-without-renderer");
+var textareaCaseWithoutRenderer = document.getElementById("textarea-without-renderer");
+
+function testBackgroundColor(expectMatch) {
+    shouldBeEqualToString('getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+    shouldBeEqualToString('getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor', expectMatch ? 'rgb(1, 2, 3)' : 'rgb(255, 255, 255)');
+}
+
+function setAttribute(attribute, value) {
+    inputCaseWithRenderer.setAttribute(attribute, value);
+    textareaCaseWithRenderer.setAttribute(attribute, value);
+    inputCaseWithoutRenderer.setAttribute(attribute, value);
+    textareaCaseWithoutRenderer.setAttribute(attribute, value);
+}
+
+debug("Initial state is without placehoder.");
+testBackgroundColor(false);
+
+debug("Adding a valid placeholder matches.");
+setAttribute("placeholder", "WebKit!");
+testBackgroundColor(true);
+
+debug("Using an invalid placeholder value does not match.");
+setAttribute("placeholder", "\n");
+testBackgroundColor(false);
+
+debug("Adding back a placehoder and an empty value. An empty value does not prevent matching.");
+setAttribute("placeholder", "WebKit!");
+inputCaseWithRenderer.value = "";
+textareaCaseWithRenderer.appendChild(document.createTextNode(""));
+inputCaseWithoutRenderer.value = "";
+textareaCaseWithoutRenderer.appendChild(document.createTextNode(""));
+testBackgroundColor(true);
+
+debug("Changing the type of the input to something that does not support placeholder. The input element should not match.");
+inputCaseWithRenderer.type = "button";
+inputCaseWithoutRenderer.type = "button";
+shouldBeEqualToString('getComputedStyle(document.getElementById("input-with-renderer")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("textarea-with-renderer")).backgroundColor', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("input-without-renderer")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("textarea-without-renderer")).backgroundColor', 'rgb(1, 2, 3)');
+
+debug("Changing the type of the input to text should add the style back.");
+inputCaseWithRenderer.type = "text";
+inputCaseWithoutRenderer.type = "text";
+testBackgroundColor(true);
+
+debug("Adding a non empty value should remove the style.");
+inputCaseWithRenderer.value = "Foobar";
+textareaCaseWithRenderer.appendChild(document.createTextNode("Foobar"));
+inputCaseWithoutRenderer.value = "Foobar";
+textareaCaseWithoutRenderer.appendChild(document.createTextNode("Foobar"));
+testBackgroundColor(false);
+
+debug("Removing the placeholder, we should not match.");
+setAttribute("placeholder", "");
+testBackgroundColor(false);
+
+debug("Removing the value. We should still not match since the placeholder attribute was removed.");
+inputCaseWithRenderer.value = "";
+textareaCaseWithRenderer.removeChild(textareaCaseWithRenderer.lastChild);
+inputCaseWithoutRenderer.value = "";
+textareaCaseWithoutRenderer.removeChild(textareaCaseWithoutRenderer.lastChild);
+testBackgroundColor(false);
+
+debug("Putting back a value, no reason to match.");
+inputCaseWithRenderer.value = "Foobar";
+textareaCaseWithRenderer.appendChild(document.createTextNode("Foobar"));
+inputCaseWithoutRenderer.value = "Foobar";
+textareaCaseWithoutRenderer.appendChild(document.createTextNode("Foobar"));
+testBackgroundColor(false);
+
+debug("Adding back the placeholder, the value is still there so we cannot match yet.");
+setAttribute("placeholder", "WebKit!");
+testBackgroundColor(false);
+
+debug("Removing the value. A placeholder was added while the value was up, we should get the style now.");
+inputCaseWithRenderer.value = "";
+textareaCaseWithRenderer.removeChild(textareaCaseWithRenderer.lastChild);
+inputCaseWithoutRenderer.value = "";
+textareaCaseWithoutRenderer.removeChild(textareaCaseWithoutRenderer.lastChild);
+testBackgroundColor(true);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/placeholder-shown-with-input-basics-expected.txt b/LayoutTests/fast/selectors/placeholder-shown-with-input-basics-expected.txt
new file mode 100644 (file)
index 0000000..e22ca68
--- /dev/null
@@ -0,0 +1,39 @@
+Check the basic features of the ":placeholder-shown" pseudo class with the <input> element.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll(":placeholder-shown").length is 3
+PASS document.querySelectorAll(":placeholder-shown")[0] is document.getElementById("valid-placeholder")
+PASS document.querySelectorAll(":placeholder-shown")[1] is document.getElementById("valid-placeholder-with-empty-value")
+PASS document.querySelectorAll(":placeholder-shown")[2] is document.getElementById("valid-placeholder-with-empty-value2")
+PASS getComputedStyle(document.getElementById("no-placeholder")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("empty-placeholder")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("empty-placeholder2")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-newline")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("with-value")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("valid-placeholder")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-empty-value")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-empty-value2")).backgroundColor is "rgb(1, 2, 3)"
+
+PASS document.querySelectorAll("input:not(:placeholder-shown)").length is 6
+PASS document.querySelectorAll("input:not(:placeholder-shown)")[0] is document.getElementById("no-placeholder")
+PASS document.querySelectorAll("input:not(:placeholder-shown)")[1] is document.getElementById("empty-placeholder")
+PASS document.querySelectorAll("input:not(:placeholder-shown)")[2] is document.getElementById("empty-placeholder2")
+PASS document.querySelectorAll("input:not(:placeholder-shown)")[3] is document.getElementById("placeholder-contains-only-newline")
+PASS document.querySelectorAll("input:not(:placeholder-shown)")[4] is document.getElementById("placeholder-contains-only-carriageReturn")
+PASS document.querySelectorAll("input:not(:placeholder-shown)")[5] is document.getElementById("with-value")
+PASS getComputedStyle(document.getElementById("no-placeholder")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("empty-placeholder")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("empty-placeholder2")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-newline")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("with-value")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("valid-placeholder")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-empty-value")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-empty-value2")).color is "rgb(0, 0, 0)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/placeholder-shown-with-input-basics.html b/LayoutTests/fast/selectors/placeholder-shown-with-input-basics.html
new file mode 100644 (file)
index 0000000..57a995c
--- /dev/null
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+input {
+    background-color: white;
+    color: black;
+}
+input:placeholder-shown {
+    background-color: rgb(1, 2, 3);
+}
+input:not(:placeholder-shown) {
+    color: rgb(4, 5, 6);
+}
+</style>
+</head>
+<body>
+    <div style="display:none">
+        <!-- Does not match: no placeholder defined. -->
+        <input type="text" id="no-placeholder">
+
+        <!-- Does not match: empty placeholder. -->
+        <input type="text" id="empty-placeholder" placeholder>
+        <input type="text" id="empty-placeholder2" placeholder="">
+
+        <!-- Does not match: placeholder contains only newline or carriage return characters. -->
+        <input type="text" id="placeholder-contains-only-newline">
+        <input type="text" id="placeholder-contains-only-carriageReturn">
+
+        <!-- Does not match: the placeholder is not shown when a value is set -->
+        <input type="text" id="with-value" placeholder="WebKit" value="FooBar">
+
+        <!-- Valid cases -->
+        <input type="text" id="valid-placeholder" placeholder="WebKit">
+        <input type="text" id="valid-placeholder-with-empty-value" placeholder="WebKit" value>
+        <input type="text" id="valid-placeholder-with-empty-value2" placeholder="WebKit" value="">
+    </div>
+</body>
+<script>
+description('Check the basic features of the ":placeholder-shown" pseudo class with the &lt;input&gt; element.');
+
+document.getElementById('placeholder-contains-only-newline').setAttribute('placeholder', '\n');
+document.getElementById('placeholder-contains-only-carriageReturn').setAttribute('placeholder', '\r');
+
+shouldBe('document.querySelectorAll(":placeholder-shown").length', '3');
+shouldBe('document.querySelectorAll(":placeholder-shown")[0]', 'document.getElementById("valid-placeholder")');
+shouldBe('document.querySelectorAll(":placeholder-shown")[1]', 'document.getElementById("valid-placeholder-with-empty-value")');
+shouldBe('document.querySelectorAll(":placeholder-shown")[2]', 'document.getElementById("valid-placeholder-with-empty-value2")');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("no-placeholder")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder2")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-newline")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("with-value")).backgroundColor', 'rgb(255, 255, 255)');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder")).backgroundColor', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-empty-value")).backgroundColor', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-empty-value2")).backgroundColor', 'rgb(1, 2, 3)');
+
+debug("");
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)").length', '6');
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)")[0]', 'document.getElementById("no-placeholder")');
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)")[1]', 'document.getElementById("empty-placeholder")');
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)")[2]', 'document.getElementById("empty-placeholder2")');
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)")[3]', 'document.getElementById("placeholder-contains-only-newline")');
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)")[4]', 'document.getElementById("placeholder-contains-only-carriageReturn")');
+shouldBe('document.querySelectorAll("input:not(:placeholder-shown)")[5]', 'document.getElementById("with-value")');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("no-placeholder")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder2")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-newline")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("with-value")).color', 'rgb(4, 5, 6)');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder")).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-empty-value")).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-empty-value2")).color', 'rgb(0, 0, 0)');
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/placeholder-shown-with-textarea-basics-expected.txt b/LayoutTests/fast/selectors/placeholder-shown-with-textarea-basics-expected.txt
new file mode 100644 (file)
index 0000000..f0bd115
--- /dev/null
@@ -0,0 +1,42 @@
+Check the basic features of the ":placeholder-shown" pseudo class with the <textarea> element.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.querySelectorAll(":placeholder-shown").length is 4
+PASS document.querySelectorAll(":placeholder-shown")[0] is document.getElementById("valid-placeholder")
+PASS document.querySelectorAll(":placeholder-shown")[1] is document.getElementById("valid-placeholder-with-value-attribute")
+PASS document.querySelectorAll(":placeholder-shown")[2] is document.getElementById("valid-placeholder-with-value-attribute2")
+PASS document.querySelectorAll(":placeholder-shown")[3] is document.getElementById("valid-placeholder-with-value-attribute3")
+PASS getComputedStyle(document.getElementById("no-placeholder")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("empty-placeholder")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("empty-placeholder2")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-newline")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("with-value")).backgroundColor is "rgb(255, 255, 255)"
+PASS getComputedStyle(document.getElementById("valid-placeholder")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute2")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute3")).backgroundColor is "rgb(1, 2, 3)"
+
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)").length is 6
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)")[0] is document.getElementById("no-placeholder")
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)")[1] is document.getElementById("empty-placeholder")
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)")[2] is document.getElementById("empty-placeholder2")
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)")[3] is document.getElementById("placeholder-contains-only-newline")
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)")[4] is document.getElementById("placeholder-contains-only-carriageReturn")
+PASS document.querySelectorAll("textarea:not(:placeholder-shown)")[5] is document.getElementById("with-value")
+PASS getComputedStyle(document.getElementById("no-placeholder")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("empty-placeholder")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("empty-placeholder2")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-newline")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("with-value")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById("valid-placeholder")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute2")).color is "rgb(0, 0, 0)"
+PASS getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute3")).color is "rgb(0, 0, 0)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/placeholder-shown-with-textarea-basics.html b/LayoutTests/fast/selectors/placeholder-shown-with-textarea-basics.html
new file mode 100644 (file)
index 0000000..0d7ba08
--- /dev/null
@@ -0,0 +1,89 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+textarea {
+    background-color: white;
+    color: black;
+}
+textarea:placeholder-shown {
+    background-color: rgb(1, 2, 3);
+}
+textarea:not(:placeholder-shown) {
+    color: rgb(4, 5, 6);
+}
+</style>
+</head>
+<body>
+    <div style="display:none">
+        <!-- Does not match: no placeholder defined. -->
+        <textarea id="no-placeholder"></textarea>
+
+        <!-- Does not match: empty placeholder. -->
+        <textarea id="empty-placeholder" placeholder></textarea>
+        <textarea id="empty-placeholder2" placeholder=""></textarea>
+
+        <!-- Does not match: placeholder contains only newline or carriage return characters. -->
+        <textarea id="placeholder-contains-only-newline"></textarea>
+        <textarea id="placeholder-contains-only-carriageReturn"></textarea>
+
+        <!-- Does not match: the placeholder is not shown when a value is set -->
+        <textarea id="with-value" placeholder="WebKit">FooBar</textarea>
+
+        <!-- Valid cases -->
+        <textarea id="valid-placeholder" placeholder="WebKit"></textarea>
+
+        <!-- Value does not do anything on <textarea>, the content is the innerText -->
+        <textarea id="valid-placeholder-with-value-attribute" placeholder="WebKit" value></textarea>
+        <textarea id="valid-placeholder-with-value-attribute2" placeholder="WebKit" value=""></textarea>
+        <textarea id="valid-placeholder-with-value-attribute3" placeholder="WebKit" value="Rocks!"></textarea>
+    </div>
+</body>
+<script>
+description('Check the basic features of the ":placeholder-shown" pseudo class with the &lt;textarea&gt; element.');
+
+document.getElementById('placeholder-contains-only-newline').setAttribute('placeholder', '\n');
+document.getElementById('placeholder-contains-only-carriageReturn').setAttribute('placeholder', '\r');
+
+shouldBe('document.querySelectorAll(":placeholder-shown").length', '4');
+shouldBe('document.querySelectorAll(":placeholder-shown")[0]', 'document.getElementById("valid-placeholder")');
+shouldBe('document.querySelectorAll(":placeholder-shown")[1]', 'document.getElementById("valid-placeholder-with-value-attribute")');
+shouldBe('document.querySelectorAll(":placeholder-shown")[2]', 'document.getElementById("valid-placeholder-with-value-attribute2")');
+shouldBe('document.querySelectorAll(":placeholder-shown")[3]', 'document.getElementById("valid-placeholder-with-value-attribute3")');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("no-placeholder")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder2")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-newline")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).backgroundColor', 'rgb(255, 255, 255)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("with-value")).backgroundColor', 'rgb(255, 255, 255)');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder")).backgroundColor', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute")).backgroundColor', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute2")).backgroundColor', 'rgb(1, 2, 3)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute3")).backgroundColor', 'rgb(1, 2, 3)');
+
+debug("");
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)").length', '6');
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)")[0]', 'document.getElementById("no-placeholder")');
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)")[1]', 'document.getElementById("empty-placeholder")');
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)")[2]', 'document.getElementById("empty-placeholder2")');
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)")[3]', 'document.getElementById("placeholder-contains-only-newline")');
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)")[4]', 'document.getElementById("placeholder-contains-only-carriageReturn")');
+shouldBe('document.querySelectorAll("textarea:not(:placeholder-shown)")[5]', 'document.getElementById("with-value")');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("no-placeholder")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("empty-placeholder2")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-newline")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("placeholder-contains-only-carriageReturn")).color', 'rgb(4, 5, 6)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("with-value")).color', 'rgb(4, 5, 6)');
+
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder")).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute")).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute2")).color', 'rgb(0, 0, 0)');
+shouldBeEqualToString('getComputedStyle(document.getElementById("valid-placeholder-with-value-attribute3")).color', 'rgb(0, 0, 0)');
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
index 7ae72bc..678cd13 100644 (file)
@@ -1,3 +1,82 @@
+2014-08-20  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS: Implement the :placeholder-shown pseudo-class from Selectors Level 4
+        https://bugs.webkit.org/show_bug.cgi?id=118162
+
+        Reviewed by Antti Koivisto.
+
+        Previously, HTMLTextFormControlElement was using some mix of its own state
+        and style to change the visibility of the placeholder. That approach was a little
+        bit too fragile, and we do not want the style to depends on the renderer() since
+        that creates circular dependencies.
+
+        The biggest change here is refactoring HTMLTextFormControlElement to have
+        1) An explicit "visible placeholder" state.
+        2) Separate the textUpdate() from the visibilityUpdate().
+        3) Remove the dependencies between the Element's style and the placeholder's style.
+           This is done by simply using display:none; on the placeholder so that its parent's visibility
+           is irrelevant.
+
+        When matching the selector, the style is set as unique since style sharing does not deal with
+        the changes of HTMLTextFormControlElement.
+
+        Tests: fast/css/placeholder-shown-basics.html
+               fast/selectors/placeholder-shown-long-adjacent-backtracking.html
+               fast/selectors/placeholder-shown-sibling-style-update.html
+               fast/selectors/placeholder-shown-style-update.html
+               fast/selectors/placeholder-shown-with-input-basics.html
+               fast/selectors/placeholder-shown-with-textarea-basics.html
+
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::selectorText):
+        Add the CSS Selector description for CSSOM.
+
+        * css/CSSSelector.h:
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOne):
+        * css/SelectorCheckerTestFunctions.h:
+        (WebCore::isPlaceholderShown):
+        * css/SelectorPseudoClassAndCompatibilityElementMap.in:
+        * css/html.css:
+        (::-webkit-input-placeholder):
+        Previously, the display was forced through the UA stylesheet. Since the display is now part
+        of the placeholder visibility, it is explicitly handled by HTMLTextFormControlElement and
+        its subclasses.
+
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::addPseudoClassType):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementMatching):
+        (WebCore::SelectorCompiler::makeUniqueIfNecessaryAndTestIsPlaceholderShown):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementHasPlaceholderShown):
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::parseAttribute):
+        * html/HTMLTextAreaElement.cpp:
+        (WebCore::HTMLTextAreaElement::updateValue):
+        (WebCore::HTMLTextAreaElement::setValueCommon):
+        (WebCore::HTMLTextAreaElement::updatePlaceholderText):
+        * html/HTMLTextFormControlElement.cpp:
+        (WebCore::HTMLTextFormControlElement::HTMLTextFormControlElement):
+        (WebCore::HTMLTextFormControlElement::dispatchFocusEvent):
+        (WebCore::HTMLTextFormControlElement::dispatchBlurEvent):
+        (WebCore::HTMLTextFormControlElement::placeholderShouldBeVisible):
+        (WebCore::HTMLTextFormControlElement::updatePlaceholderVisibility):
+        (WebCore::HTMLTextFormControlElement::selectionDirection):
+        (WebCore::HTMLTextFormControlElement::restoreCachedSelection):
+        (WebCore::HTMLTextFormControlElement::parseAttribute):
+        (WebCore::HTMLTextFormControlElement::hidePlaceholder):
+        (WebCore::HTMLTextFormControlElement::showPlaceholderIfNecessary):
+        * html/HTMLTextFormControlElement.h:
+        (WebCore::HTMLTextFormControlElement::isPlaceholderVisible):
+        (WebCore::HTMLTextFormControlElement::cachedSelectionDirection):
+        * html/TextFieldInputType.cpp:
+        (WebCore::TextFieldInputType::updatePlaceholderText):
+        (WebCore::TextFieldInputType::subtreeHasChanged):
+        (WebCore::TextFieldInputType::updateInnerTextValue):
+        * rendering/RenderTextControl.cpp:
+        (WebCore::RenderTextControl::styleDidChange):
+        * testing/Internals.cpp:
+        (WebCore::Internals::visiblePlaceholder):
+
 2014-08-20  Mark Rowe  <mrowe@apple.com>
 
         Fix the release build after r172806.
index e05a3ac..07c2967 100644 (file)
@@ -1852,7 +1852,6 @@ __ZNK7WebCore23AuthenticationChallenge20authenticationClientEv
 __ZNK7WebCore23FrameLoaderStateMachine15firstLayoutDoneEv
 __ZNK7WebCore23FrameLoaderStateMachine23committingFirstRealLoadEv
 __ZNK7WebCore26HTMLTextFormControlElement21lastChangeWasUserEditEv
-__ZNK7WebCore26HTMLTextFormControlElement26placeholderShouldBeVisibleEv
 __ZNK7WebCore26NetscapePlugInStreamLoader6isDoneEv
 __ZNK7WebCore27AuthenticationChallengeBase15failureResponseEv
 __ZNK7WebCore27AuthenticationChallengeBase15protectionSpaceEv
index 724c5df..6cec439 100644 (file)
@@ -417,6 +417,11 @@ String CSSSelector::selectorText(const String& rightSide) const
             case CSSSelector::PseudoClassOptional:
                 str.appendLiteral(":optional");
                 break;
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+            case CSSSelector::PseudoClassPlaceholderShown:
+                str.appendLiteral(":placeholder-shown");
+                break;
+#endif
             case CSSSelector::PseudoClassOutOfRange:
                 str.appendLiteral(":out-of-range");
                 break;
@@ -461,7 +466,7 @@ String CSSSelector::selectorText(const String& rightSide) const
             case CSSSelector::PseudoClassWindowInactive:
                 str.appendLiteral(":window-inactive");
                 break;
-            default:
+            case CSSSelector::PseudoClassUnknown:
                 ASSERT_NOT_REACHED();
             }
         } else if (cs->m_match == CSSSelector::PseudoElement) {
index f2acf7f..31c53b0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
  *               1999 Waldo Bastian (bastian@kde.org)
- * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013, 2014 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -106,6 +106,9 @@ namespace WebCore {
             PseudoClassDefault,
             PseudoClassDisabled,
             PseudoClassOptional,
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+            PseudoClassPlaceholderShown,
+#endif
             PseudoClassRequired,
             PseudoClassReadOnly,
             PseudoClassReadWrite,
index 0140f36..cb358a4 100644 (file)
@@ -604,6 +604,14 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const
                 return isFirstOfType(element, element->tagQName()) && isLastOfType(element, element->tagQName());
             }
             break;
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+        case CSSSelector::PseudoClassPlaceholderShown:
+            if (m_mode == Mode::ResolvingStyle) {
+                if (RenderStyle* style = context.elementStyle ? context.elementStyle : element->renderStyle())
+                    style->setUnique();
+            }
+            return isPlaceholderShown(element);
+#endif
         case CSSSelector::PseudoClassNthChild:
             if (!selector->parseNth())
                 break;
index 98ffa19..66a0192 100644 (file)
@@ -114,6 +114,15 @@ ALWAYS_INLINE bool isOptionalFormControl(const Element* element)
     return element->isOptionalFormControl();
 }
 
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+ALWAYS_INLINE bool isPlaceholderShown(Element* element)
+{
+    if (isHTMLTextFormControlElement(*element))
+        return toHTMLTextFormControlElement(*element).isPlaceholderVisible();
+    return false;
+}
+#endif
+
 ALWAYS_INLINE bool isRequiredFormControl(const Element* element)
 {
     return element->isRequiredFormControl();
index 5f0c200..f34e315 100644 (file)
@@ -41,6 +41,9 @@ only-child
 only-of-type
 optional
 out-of-range
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+placeholder-shown
+#endif
 read-only
 read-write
 required
index c392e75..6dc210d 100644 (file)
@@ -611,7 +611,6 @@ textarea {
 ::-webkit-input-placeholder {
     -webkit-text-security: none;
     color: darkGray;
-    display: block !important;
     pointer-events: none !important;
 }
 
index 61757f0..7e2af89 100644 (file)
@@ -244,6 +244,9 @@ private:
     void generateElementIsInLanguage(Assembler::JumpList& failureCases, const AtomicString&);
     void generateElementIsLastChild(Assembler::JumpList& failureCases, const SelectorFragment&);
     void generateElementIsOnlyChild(Assembler::JumpList& failureCases, const SelectorFragment&);
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+    void generateElementHasPlaceholderShown(Assembler::JumpList& failureCases, const SelectorFragment&);
+#endif
     void generateSynchronizeStyleAttribute(Assembler::RegisterID elementDataArraySizeAndFlags);
     void generateSynchronizeAllAnimatedSVGAttribute(Assembler::RegisterID elementDataArraySizeAndFlags);
     void generateElementAttributesMatching(Assembler::JumpList& failureCases, const LocalRegister& elementDataAddress, const SelectorFragment&);
@@ -536,6 +539,9 @@ static inline FunctionType addPseudoClassType(const CSSSelector& selector, Selec
     case CSSSelector::PseudoClassHover:
     case CSSSelector::PseudoClassLastChild:
     case CSSSelector::PseudoClassOnlyChild:
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+    case CSSSelector::PseudoClassPlaceholderShown:
+#endif
         fragment.pseudoClasses.add(type);
         if (selectorContext == SelectorContext::QuerySelector)
             return FunctionType::SimpleSelectorChecker;
@@ -1972,6 +1978,10 @@ void SelectorCodeGenerator::generateElementMatching(Assembler::JumpList& matchin
         generateElementIsHovered(matchingPostTagNameFailureCases, fragment);
     if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassOnlyChild))
         generateElementIsOnlyChild(matchingPostTagNameFailureCases, fragment);
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+    if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassPlaceholderShown))
+        generateElementHasPlaceholderShown(matchingPostTagNameFailureCases, fragment);
+#endif
     if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassFirstChild))
         generateElementIsFirstChild(matchingPostTagNameFailureCases, fragment);
     if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassLastChild))
@@ -2798,6 +2808,51 @@ void SelectorCodeGenerator::generateElementIsOnlyChild(Assembler::JumpList& fail
     failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isOnlyChildRegister));
 }
 
+#if ENABLE(CSS_SELECTORS_LEVEL4)
+static bool makeUniqueIfNecessaryAndTestIsPlaceholderShown(Element* element, const CheckingContext* checkingContext)
+{
+    if (checkingContext->resolvingMode == SelectorChecker::Mode::ResolvingStyle) {
+        if (RenderStyle* style = element->renderStyle())
+            style->setUnique();
+    }
+    return isPlaceholderShown(element);
+}
+
+void SelectorCodeGenerator::generateElementHasPlaceholderShown(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
+{
+    if (m_selectorContext == SelectorContext::QuerySelector) {
+        FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+        functionCall.setFunctionAddress(isPlaceholderShown);
+        functionCall.setOneArgument(elementAddressRegister);
+        failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
+        return;
+    }
+
+    if (fragmentMatchesTheRightmostElement(m_selectorContext, fragment)) {
+        {
+            LocalRegister checkingContext(m_registerAllocator);
+            Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext);
+            addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::flagIsUnique());
+            notResolvingStyle.link(&m_assembler);
+        }
+
+        FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+        functionCall.setFunctionAddress(isPlaceholderShown);
+        functionCall.setOneArgument(elementAddressRegister);
+        failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
+    } else {
+        Assembler::RegisterID checkingContext = m_registerAllocator.allocateRegisterWithPreference(JSC::GPRInfo::argumentGPR1);
+        loadCheckingContext(checkingContext);
+        m_registerAllocator.deallocateRegister(checkingContext);
+
+        FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+        functionCall.setFunctionAddress(makeUniqueIfNecessaryAndTestIsPlaceholderShown);
+        functionCall.setTwoArguments(elementAddressRegister, checkingContext);
+        failureCases.append(functionCall.callAndBranchOnBooleanReturnValue(Assembler::Zero));
+    }
+}
+#endif
+
 inline void SelectorCodeGenerator::generateElementHasTagName(Assembler::JumpList& failureCases, const QualifiedName& nameToMatch)
 {
     if (nameToMatch == anyQName())
index bbdf956..3e66853 100644 (file)
@@ -636,7 +636,7 @@ void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicStr
         }
         // We only need to setChanged if the form is looking at the default value right now.
         if (!hasDirtyValue()) {
-            updatePlaceholderVisibility(false);
+            updatePlaceholderVisibility();
             setNeedsStyleRecalc();
         }
         setFormControlValueMatchesRenderer(false);
index f46b9cd..7920c2e 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2014 Apple Inc. All rights reserved.
  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
  * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
  *
@@ -345,7 +345,7 @@ void HTMLTextAreaElement::updateValue() const
     const_cast<HTMLTextAreaElement*>(this)->setFormControlValueMatchesRenderer(true);
     m_isDirty = true;
     m_wasModifiedByUser = true;
-    const_cast<HTMLTextAreaElement*>(this)->updatePlaceholderVisibility(false);
+    const_cast<HTMLTextAreaElement*>(this)->updatePlaceholderVisibility();
 }
 
 String HTMLTextAreaElement::value() const
@@ -385,7 +385,7 @@ void HTMLTextAreaElement::setValueCommon(const String& newValue)
     m_value = normalizedValue;
     setInnerTextValue(m_value);
     setLastChangeWasNotUserEdit();
-    updatePlaceholderVisibility(false);
+    updatePlaceholderVisibility();
     setNeedsStyleRecalc();
     setFormControlValueMatchesRenderer(true);
 
@@ -539,6 +539,7 @@ void HTMLTextAreaElement::updatePlaceholderText()
         RefPtr<HTMLDivElement> placeholder = HTMLDivElement::create(document());
         m_placeholder = placeholder.get();
         m_placeholder->setPseudo(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
+        m_placeholder->setInlineStyleProperty(CSSPropertyDisplay, isPlaceholderVisible() ? CSSValueBlock : CSSValueNone, true);
         userAgentShadowRoot()->insertBefore(m_placeholder, innerTextElement()->nextSibling());
     }
     m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION);
index 64caa34..7d9191b 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2014 Apple Inc. All rights reserved.
  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
  *
  * This library is free software; you can redistribute it and/or
@@ -38,6 +38,7 @@
 #include "HTMLInputElement.h"
 #include "HTMLNames.h"
 #include "NodeTraversal.h"
+#include "Page.h"
 #include "RenderBlockFlow.h"
 #include "RenderTextControlSingleLine.h"
 #include "RenderTheme.h"
@@ -55,10 +56,11 @@ static Position positionForIndex(TextControlInnerTextElement*, unsigned);
 
 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
     : HTMLFormControlElementWithState(tagName, document, form)
-    , m_lastChangeWasUserEdit(false)
     , m_cachedSelectionStart(-1)
     , m_cachedSelectionEnd(-1)
     , m_cachedSelectionDirection(SelectionHasNoDirection)
+    , m_lastChangeWasUserEdit(false)
+    , m_isPlaceholderVisible(false)
 {
 }
 
@@ -88,7 +90,7 @@ Node::InsertionNotificationRequest HTMLTextFormControlElement::insertedInto(Cont
 void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Element> oldFocusedElement, FocusDirection direction)
 {
     if (supportsPlaceholder())
-        updatePlaceholderVisibility(false);
+        updatePlaceholderVisibility();
     handleFocusEvent(oldFocusedElement.get(), direction);
     HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, direction);
 }
@@ -96,7 +98,7 @@ void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Element> oldFocus
 void HTMLTextFormControlElement::dispatchBlurEvent(PassRefPtr<Element> newFocusedElement)
 {
     if (supportsPlaceholder())
-        updatePlaceholderVisibility(false);
+        updatePlaceholderVisibility();
     handleBlurEvent();
     HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
 }
@@ -147,23 +149,26 @@ bool HTMLTextFormControlElement::isPlaceholderEmpty() const
 
 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
 {
+    // This function is used by the style resolver to match the :placeholder-shown pseudo class.
+    // Since it is used for styling, it must not use any value depending on the style.
     return supportsPlaceholder()
         && isEmptyValue()
         && !isPlaceholderEmpty()
-        && (document().focusedElement() != this || (renderer() && renderer()->theme().shouldShowPlaceholderWhenFocused()))
-        && (!renderer() || renderer()->style().visibility() == VISIBLE);
+        && (document().focusedElement() != this || (document().page()->theme().shouldShowPlaceholderWhenFocused()));
 }
 
-void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
+void HTMLTextFormControlElement::updatePlaceholderVisibility()
 {
-    if (!supportsPlaceholder())
-        return;
-    if (!placeholderElement() || placeholderValueChanged)
-        updatePlaceholderText();
-    HTMLElement* placeholder = placeholderElement();
-    if (!placeholder)
+    bool placeHolderWasVisible = m_isPlaceholderVisible;
+    m_isPlaceholderVisible = placeholderShouldBeVisible();
+
+    if (placeHolderWasVisible == m_isPlaceholderVisible)
         return;
-    placeholder->setInlineStyleProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? CSSValueVisible : CSSValueHidden);
+
+    setNeedsStyleRecalc();
+
+    if (HTMLElement* placeholder = placeholderElement())
+        placeholder->setInlineStyleProperty(CSSPropertyDisplay, m_isPlaceholderVisible ? CSSValueBlock : CSSValueNone, true);
 }
 
 void HTMLTextFormControlElement::setSelectionStart(int start)
@@ -396,7 +401,7 @@ const AtomicString& HTMLTextFormControlElement::selectionDirection() const
     if (!isTextFormControl())
         return directionString(SelectionHasNoDirection);
     if (document().focusedElement() != this && hasCachedSelection())
-        return directionString(m_cachedSelectionDirection);
+        return directionString(cachedSelectionDirection());
 
     return directionString(computeSelectionDirection());
 }
@@ -466,7 +471,7 @@ PassRefPtr<Range> HTMLTextFormControlElement::selection() const
 
 void HTMLTextFormControlElement::restoreCachedSelection()
 {
-    setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection);
+    setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, cachedSelectionDirection());
 }
 
 void HTMLTextFormControlElement::selectionChanged(bool shouldFireSelectEvent)
@@ -484,9 +489,10 @@ void HTMLTextFormControlElement::selectionChanged(bool shouldFireSelectEvent)
 
 void HTMLTextFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
 {
-    if (name == placeholderAttr)
-        updatePlaceholderVisibility(true);
-    else
+    if (name == placeholderAttr) {
+        updatePlaceholderText();
+        updatePlaceholderVisibility();
+    } else
         HTMLFormControlElementWithState::parseAttribute(name, value);
 }
 
@@ -626,19 +632,14 @@ unsigned HTMLTextFormControlElement::indexForPosition(const Position& passedPosi
 #if PLATFORM(IOS)
 void HTMLTextFormControlElement::hidePlaceholder()
 {
-    if (!supportsPlaceholder())
-        return;
-    HTMLElement* placeholder = placeholderElement();
-    if (!placeholder) {
-        updatePlaceholderText();
-        return;
-    }
-    placeholder->setInlineStyleProperty(CSSPropertyVisibility, ASCIILiteral("hidden"));
+    if (HTMLElement* placeholder = placeholderElement())
+        placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueHidden, true);
 }
 
 void HTMLTextFormControlElement::showPlaceholderIfNecessary()
 {
-    updatePlaceholderVisibility(false);
+    if (HTMLElement* placeholder = placeholderElement())
+        placeholder->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible, true);
 }
 #endif
 
index bf47de7..c22f59c 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -50,11 +50,11 @@ public:
     virtual InsertionNotificationRequest insertedInto(ContainerNode&) override;
 
     // The derived class should return true if placeholder processing is needed.
+    bool isPlaceholderVisible() const { return m_isPlaceholderVisible; }
     virtual bool supportsPlaceholder() const = 0;
     String strippedPlaceholder() const;
-    bool placeholderShouldBeVisible() const;
     virtual HTMLElement* placeholderElement() const = 0;
-    void updatePlaceholderVisibility(bool);
+    void updatePlaceholderVisibility();
 
     int indexForVisiblePosition(const VisiblePosition&) const;
     VisiblePosition visiblePositionForIndex(int index) const;
@@ -120,6 +120,8 @@ protected:
     String valueWithHardLineBreaks() const;
 
 private:
+    TextFieldSelectionDirection cachedSelectionDirection() const { return static_cast<TextFieldSelectionDirection>(m_cachedSelectionDirection); }
+
     int computeSelectionStart() const;
     int computeSelectionEnd() const;
     TextFieldSelectionDirection computeSelectionDirection() const;
@@ -137,12 +139,16 @@ private:
     // Called in dispatchBlurEvent(), after placeholder process, before calling parent's dispatchBlurEvent().
     virtual void handleBlurEvent() { }
 
+    bool placeholderShouldBeVisible() const;
+
     String m_textAsOfLastFormControlChangeEvent;
-    bool m_lastChangeWasUserEdit;
-    
+
     int m_cachedSelectionStart;
     int m_cachedSelectionEnd;
-    TextFieldSelectionDirection m_cachedSelectionDirection;
+
+    unsigned char m_cachedSelectionDirection : 2;
+    unsigned char m_lastChangeWasUserEdit : 1;
+    unsigned char m_isPlaceholderVisible : 1;
 };
 
 void isHTMLTextFormControlElement(const HTMLTextFormControlElement&); // Catch unnecessary runtime check of type known at compile time.
index 673e247..e150149 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -409,6 +409,7 @@ void TextFieldInputType::updatePlaceholderText()
     if (!m_placeholder) {
         m_placeholder = HTMLDivElement::create(element().document());
         m_placeholder->setPseudo(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
+        m_placeholder->setInlineStyleProperty(CSSPropertyDisplay, element().isPlaceholderVisible() ? CSSValueBlock : CSSValueNone, true);
         element().userAgentShadowRoot()->insertBefore(m_placeholder, m_container ? m_container.get() : innerTextElement(), ASSERT_NO_EXCEPTION);
     }
     m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION);
@@ -438,7 +439,7 @@ void TextFieldInputType::subtreeHasChanged()
     // sanitizeUserInputValue().
     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
     element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerTextValue())));
-    element().updatePlaceholderVisibility(false);
+    element().updatePlaceholderVisibility();
     // Recalc for :invalid change.
     element().setNeedsStyleRecalc();
 
@@ -472,7 +473,7 @@ void TextFieldInputType::updateInnerTextValue()
         // Update the renderer value if the formControlValueMatchesRenderer() flag is false.
         // It protects an unacceptable renderer value from being overwritten with the DOM value.
         element().setInnerTextValue(visibleValue());
-        element().updatePlaceholderVisibility(false);
+        element().updatePlaceholderVisibility();
     }
 }
 
index bc71d04..ae5b0a6 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2014 Apple Inc. All rights reserved.
  *           (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)  
  *
  * This library is free software; you can redistribute it and/or
@@ -70,7 +70,7 @@ void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle*
         innerTextRenderer->style().setWidth(Length());
         innerTextRenderer->setStyle(createInnerTextStyle(&style()));
     }
-    textFormControlElement().updatePlaceholderVisibility(false);
+    textFormControlElement().updatePlaceholderVisibility();
 }
 
 void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
index a2effe2..12fbed4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -617,8 +617,11 @@ void Internals::setShadowPseudoId(Element* element, const String& id, ExceptionC
 String Internals::visiblePlaceholder(Element* element)
 {
     if (element && isHTMLTextFormControlElement(*element)) {
-        if (toHTMLTextFormControlElement(*element).placeholderShouldBeVisible())
-            return toHTMLTextFormControlElement(*element).placeholderElement()->textContent();
+        const HTMLTextFormControlElement& textFormControlElement = toHTMLTextFormControlElement(*element);
+        if (!textFormControlElement.isPlaceholderVisible())
+            return String();
+        if (HTMLElement* placeholderElement = textFormControlElement.placeholderElement())
+            return placeholderElement->textContent();
     }
 
     return String();
index d957bc1..ed408d6 100644 (file)
@@ -262,7 +262,6 @@ EXPORTS
         symbolWithPointer(?page@Document@WebCore@@QBEPAVPage@2@XZ, ?page@Document@WebCore@@QEBAPEAVPage@2@XZ)
         symbolWithPointer(?pageNumberForElement@PrintContext@WebCore@@SAHPAVElement@2@ABVFloatSize@2@@Z, ?pageNumberForElement@PrintContext@WebCore@@SAHPEAVElement@2@AEBVFloatSize@2@@Z)
         symbolWithPointer(?paintControlTints@FrameView@WebCore@@AAEXXZ, ?paintControlTints@FrameView@WebCore@@AEAAXXZ)
-        symbolWithPointer(?placeholderShouldBeVisible@HTMLTextFormControlElement@WebCore@@QBE_NXZ, ?placeholderShouldBeVisible@HTMLTextFormControlElement@WebCore@@QEBA_NXZ)
         symbolWithPointer(?rangeFromLocationAndLength@TextIterator@WebCore@@SA?AV?$PassRefPtr@VRange@WebCore@@@WTF@@PAVContainerNode@2@HH_N@Z, ?rangeFromLocationAndLength@TextIterator@WebCore@@SA?AV?$PassRefPtr@VRange@WebCore@@@WTF@@PEAVContainerNode@2@HH_N@Z)
         symbolWithPointer(?rectBasedTestResult@HitTestResult@WebCore@@QBEABV?$ListHashSet@V?$RefPtr@VNode@WebCore@@@WTF@@$0BAA@U?$PtrHash@V?$RefPtr@VNode@WebCore@@@WTF@@@2@@WTF@@XZ, ?rectBasedTestResult@HitTestResult@WebCore@@QEBAAEBV?$ListHashSet@V?$RefPtr@VNode@WebCore@@@WTF@@$0BAA@U?$PtrHash@V?$RefPtr@VNode@WebCore@@@WTF@@@2@@WTF@@XZ)
         symbolWithPointer(?rectForPoint@HitTestLocation@WebCore@@SA?AVIntRect@2@ABVLayoutPoint@2@IIII@Z, ?rectForPoint@HitTestLocation@WebCore@@SA?AVIntRect@2@AEBVLayoutPoint@2@IIII@Z)