[CSS] Support ::part with ::before/::after

This enables it for ::before, ::after and ::placeholder and adds a
test for all 3 but for reasons I haven't dug into yet, ::placeholder
doesn't work with ::part, so this includes a failing expectation.

Bug: 921908
Change-Id: I60a859ede98f8284cb3a1e44c81a33230b490239
Reviewed-on: https://chromium-review.googlesource.com/c/1414536
Commit-Queue: Fergal Daly <fergal@chromium.org>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#623198}
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index 619de882..5dfcf39 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -270,6 +270,10 @@
       return simple_selector.Match() != CSSSelector::kPseudoElement;
     case CSSSelector::kPseudoSlotted:
       return simple_selector.IsTreeAbidingPseudoElement();
+    case CSSSelector::kPseudoPart:
+      if (simple_selector.IsTreeAbidingPseudoElement())
+        return true;
+      break;
     default:
       break;
   }
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index a795cba..26b759c 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -563,6 +563,24 @@
   }
 }
 
+TEST(CSSSelectorParserTest, ShadowPartAndBeforeAfterPseudoElementValid) {
+  const char* test_cases[] = {"::part(ident)::before", "::part(ident)::after",
+                              "::part(ident)::placeholder"};
+
+  for (auto* test_case : test_cases) {
+    SCOPED_TRACE(test_case);
+    CSSTokenizer tokenizer(test_case);
+    const auto tokens = tokenizer.TokenizeToEOF();
+    CSSParserTokenRange range(tokens);
+    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+        range,
+        CSSParserContext::Create(kHTMLStandardMode,
+                                 SecureContextMode::kInsecureContext),
+        nullptr);
+    EXPECT_STREQ(test_case, list.SelectorsText().Ascii().data());
+  }
+}
+
 TEST(CSSSelectorParserTest, UseCountShadowPseudo) {
   std::unique_ptr<DummyPageHolder> dummy_holder =
       DummyPageHolder::Create(IntSize(500, 500));
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding-expected.txt
new file mode 100644
index 0000000..129e043
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS ::before in selected host is styled
+PASS ::after in selected host is styled
+FAIL ::placeholder in selected host is styled assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding.html
new file mode 100644
index 0000000..c11da7d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Interaction with tree-abiding</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>
+      #c-e::part(before-p)::before { color: green; }
+      #c-e::part(after-p)::after { color: green; }
+      #c-e::part(placeholder-p)::placeholder { color: green; }
+    </style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <style>
+        #before-i::before { content: "this text"; color: red; }
+        #after-i::after { content: "this text"; color: red; }
+        #placeholder-i::placeholder { color: red; }
+      </style>
+      <div>
+        The following text should be green:
+        <span id="before-i" part="before-p"></span>
+      </div>
+      <div>
+        The following text should be green:
+        <span id="after-i" part="after-p"></span>
+      </div>
+      <div>
+        The following text should be green:
+        <input id="placeholder-i" part="placeholder-p" placeholder="this text"></input>
+      </div>
+    </template>
+    <custom-element id="c-e"></custom-element>
+    <script>
+      "use strict";
+      const colorGreen = "rgb(0, 128, 0)";
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "before-i"]);
+        assert_equals(window.getComputedStyle(el, '::before').color, colorGreen);
+      }, "::before in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "after-i"]);
+        assert_equals(window.getComputedStyle(el, '::after').color, colorGreen);
+      }, "::after in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "placeholder-i"]);
+        assert_equals(window.getComputedStyle(el, '::placeholder').color, colorGreen);
+      }, "::placeholder in selected host is styled");
+    </script>
+  </body>
+</html>