diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/editing/serializers')
7 files changed, 161 insertions, 76 deletions
diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/chromium/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc index 3b6c034c990..9eb48719842 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc +++ b/chromium/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc @@ -550,7 +550,6 @@ std::pair<Node*, Element*> MarkupAccumulator::GetAuxiliaryDOMTree( element.GetExecutionContext())); AtomicString shadowroot_type; switch (shadow_root->GetType()) { - case ShadowRootType::V0: case ShadowRootType::kUserAgent: // Don't serialize user agent shadow roots, only explicit shadow roots. return std::pair<Node*, Element*>(); diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc b/chromium/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc index 110c76bcd17..186bbc7e27e 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc +++ b/chromium/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc @@ -385,18 +385,28 @@ EntityMask MarkupFormatter::EntityMaskForText(const Text& text) const { if (text.parentElement()) parent_name = &(text.parentElement())->TagQName(); - if (parent_name && (*parent_name == html_names::kScriptTag || - *parent_name == html_names::kStyleTag || - *parent_name == html_names::kXmpTag || - *parent_name == html_names::kIFrameTag || - *parent_name == html_names::kPlaintextTag || - *parent_name == html_names::kNoembedTag || - *parent_name == html_names::kNoframesTag || - (*parent_name == html_names::kNoscriptTag && - text.GetExecutionContext() && - text.GetExecutionContext()->CanExecuteScripts( - kNotAboutToExecuteScript)))) - return kEntityMaskInCDATA; + if (parent_name) { + // For a NOSCRIPT tag, escape the string unless there's an execution context + // and scripting is enabled. Note that some documents (e.g. the one created + // by DOMParser) are created with a script-enabled execution context, but no + // DOMWindow. But per spec [1], they should behave as if they have no + // execution context. So check for a DOMWindow here. + // [1] https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html + bool is_noscript_tag_with_script_enabled = + *parent_name == html_names::kNoscriptTag && + text.GetExecutionContext() && text.GetDocument().domWindow() && + text.GetExecutionContext()->CanExecuteScripts(kNotAboutToExecuteScript); + if (*parent_name == html_names::kScriptTag || + *parent_name == html_names::kStyleTag || + *parent_name == html_names::kXmpTag || + *parent_name == html_names::kIFrameTag || + *parent_name == html_names::kPlaintextTag || + *parent_name == html_names::kNoembedTag || + *parent_name == html_names::kNoframesTag || + is_noscript_tag_with_script_enabled) { + return kEntityMaskInCDATA; + } + } return kEntityMaskInHTMLPCDATA; } diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/serialization.cc b/chromium/third_party/blink/renderer/core/editing/serializers/serialization.cc index af1d518ceca..654589ec635 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/serialization.cc +++ b/chromium/third_party/blink/renderer/core/editing/serializers/serialization.cc @@ -29,6 +29,7 @@ #include "third_party/blink/renderer/core/editing/serializers/serialization.h" +#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/renderer/core/css/css_identifier_value.h" #include "third_party/blink/renderer/core/css/css_property_value_set.h" #include "third_party/blink/renderer/core/css/css_value.h" @@ -197,12 +198,7 @@ static HTMLElement* HighestAncestorToWrapMarkup( FirstPositionInOrBeforeNode(*first_node); if (Node* parent_list_node = EnclosingNodeOfType(first_node_position, IsListItem)) { - EphemeralRangeTemplate<Strategy> markup_range = - EphemeralRangeTemplate<Strategy>(start_position, end_position); - EphemeralRangeTemplate<Strategy> node_range = - NormalizeRange(EphemeralRangeTemplate<Strategy>::RangeOfContents( - *parent_list_node)); - if (node_range == markup_range) { + if (AreSameRanges(parent_list_node, start_position, end_position)) { ContainerNode* ancestor = parent_list_node->parentNode(); while (ancestor && !IsHTMLListElement(ancestor)) ancestor = ancestor->parentNode(); @@ -609,7 +605,7 @@ DocumentFragment* CreateFragmentForInnerOuterHTML( Element* context_element, ParserContentPolicy parser_content_policy, const char* method, - bool allow_shadow_root, + bool include_shadow_roots, ExceptionState& exception_state) { DCHECK(context_element); const HTMLTemplateElement* template_element = @@ -623,7 +619,7 @@ DocumentFragment* CreateFragmentForInnerOuterHTML( ? context_element->GetDocument().EnsureTemplateDocument() : context_element->GetDocument(); DocumentFragment* fragment = DocumentFragment::Create(document); - document.setAllowDeclarativeShadowRoot(allow_shadow_root); + document.setAllowDeclarativeShadowRoots(include_shadow_roots); if (IsA<HTMLDocument>(document)) { fragment->ParseHTML(markup, context_element, parser_content_policy); @@ -691,7 +687,7 @@ DocumentFragment* CreateContextualFragment( DocumentFragment* fragment = CreateFragmentForInnerOuterHTML( markup, element, parser_content_policy, "createContextualFragment", - /*allow_shadow_root=*/false, exception_state); + /*include_shadow_roots=*/false, exception_state); if (!fragment) return nullptr; @@ -776,10 +772,11 @@ void MergeWithNextTextNode(Text* text_node, ExceptionState& exception_state) { text_next->remove(exception_state); } -static Document* CreateStagingDocumentForMarkupSanitization() { +static Document* CreateStagingDocumentForMarkupSanitization( + scheduler::WebAgentGroupScheduler& agent_group_scheduler) { Page::PageClients page_clients; FillWithEmptyClients(page_clients); - Page* page = Page::CreateNonOrdinary(page_clients); + Page* page = Page::CreateNonOrdinary(page_clients, agent_group_scheduler); page->GetSettings().SetScriptEnabled(false); page->GetSettings().SetPluginsEnabled(false); @@ -792,7 +789,7 @@ static Document* CreateStagingDocumentForMarkupSanitization() { nullptr, // FrameOwner* nullptr, // Frame* parent nullptr, // Frame* previous_sibling - FrameInsertType::kInsertInConstructor, base::UnguessableToken::Create(), + FrameInsertType::kInsertInConstructor, blink::LocalFrameToken(), nullptr, // WindowAgentFactory* nullptr, // InterfaceRegistry* nullptr // policy_container @@ -849,7 +846,8 @@ DocumentFragment* CreateSanitizedFragmentFromMarkupWithContext( if (raw_markup.IsEmpty()) return nullptr; - Document* staging_document = CreateStagingDocumentForMarkupSanitization(); + Document* staging_document = CreateStagingDocumentForMarkupSanitization( + *document.GetFrame()->GetFrameScheduler()->GetAgentGroupScheduler()); Element* body = staging_document->body(); DocumentFragment* fragment = CreateFragmentFromMarkupWithContext( diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/serialization.h b/chromium/third_party/blink/renderer/core/editing/serializers/serialization.h index a73b30f1965..41e05f71567 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/serialization.h +++ b/chromium/third_party/blink/renderer/core/editing/serializers/serialization.h @@ -66,7 +66,7 @@ DocumentFragment* CreateFragmentForInnerOuterHTML(const String&, Element*, ParserContentPolicy, const char* method, - bool allow_shadow_root, + bool include_shadow_roots, ExceptionState&); DocumentFragment* CreateFragmentForTransformToFragment( const String&, diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/serialization_test.cc b/chromium/third_party/blink/renderer/core/editing/serializers/serialization_test.cc index fb59874462f..cd32aa10b6d 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/serialization_test.cc +++ b/chromium/third_party/blink/renderer/core/editing/serializers/serialization_test.cc @@ -4,11 +4,31 @@ #include "third_party/blink/renderer/core/editing/serializers/serialization.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" +#include "third_party/blink/renderer/core/editing/position.h" #include "third_party/blink/renderer/core/editing/testing/editing_test_base.h" +#include "third_party/blink/renderer/core/style/computed_style.h" namespace blink { -class SerializationTest : public EditingTestBase {}; +// See third_party/googletest/src/googletest/docs/advanced.md for supported +// regexp operators. +using ::testing::MatchesRegex; + +class SerializationTest : public EditingTestBase { + protected: + std::string SerailizeToHTMLText(const Node& node) { + // We use same |CreateMarkupOptions| used in + // |FrameSelection::SelectedHTMLForClipboard()| + return CreateMarkup(Position::BeforeNode(node), Position::AfterNode(node), + CreateMarkupOptions::Builder() + .SetShouldAnnotateForInterchange(true) + .SetShouldResolveURLs(kResolveNonLocalURLs) + .Build()) + .Utf8(); + } +}; // Regression test for https://crbug.com/1032673 TEST_F(SerializationTest, CantCreateFragmentCrash) { @@ -29,6 +49,58 @@ TEST_F(SerializationTest, CantCreateFragmentCrash) { EXPECT_FALSE(sanitized); } +// http://crbug.com/938590 +TEST_F(SerializationTest, Link) { + InsertStyleElement( + "a { color: #010101; }" + "a:link { color: #020202; }" + "a:visited { color: #030303; }"); + SetBodyContent( + "<a id=a1>text</a>" + "<a id=a2 href=''>visited</a>" + "<a id=a3 href='https://1.1.1.1/'>unvisited</a>"); + + const auto& a1 = *GetElementById("a1"); + const auto& style1 = a1.ComputedStyleRef(); + const auto& a2 = *GetElementById("a2"); + const auto& style2 = a2.ComputedStyleRef(); + const auto& a3 = *GetElementById("a3"); + const auto& style3 = a3.ComputedStyleRef(); + + // a1 + ASSERT_THAT(style1.InsideLink(), EInsideLink::kNotInsideLink); + ASSERT_THAT(style1.VisitedDependentColor(GetCSSPropertyColor()), + MakeRGB(1, 1, 1)) + << "should not be :visited/:link color"; + EXPECT_THAT( + SerailizeToHTMLText(a1), + MatchesRegex( + R"re(<a id="a1" style=".*;? ?color: rgb\(1, 1, 1\);.*">text</a>)re")); + + // a2 + // Note: Because href="" means current document URI, it is visited. + // We should have :link color instead of :visited color not to expose + // visited/unvisited state of link for privacy reason. + ASSERT_THAT(style2.InsideLink(), EInsideLink::kInsideVisitedLink); + ASSERT_THAT(style2.VisitedDependentColor(GetCSSPropertyColor()), + MakeRGB(3, 3, 3)) + << "should be :visited color"; + EXPECT_THAT( + SerailizeToHTMLText(a2), + MatchesRegex( + R"re(<a id="a2" href="" style=".*;? ?color: rgb\(2, 2, 2\);.*">visited</a>)re")); + + // a3 + ASSERT_THAT(style3.InsideLink(), EInsideLink::kInsideUnvisitedLink); + ASSERT_THAT(style3.VisitedDependentColor(GetCSSPropertyColor()), + MakeRGB(2, 2, 2)) + << "should be :link color"; + EXPECT_THAT( + SerailizeToHTMLText(a3), + MatchesRegex( + R"re(<a id="a3" href="https://1.1.1.1/" style=".*;? ?color: rgb\(2, 2, 2\);.*">unvisited</a>)re")); +} + // Regression test for https://crbug.com/1032389 TEST_F(SerializationTest, SVGForeignObjectCrash) { const String markup = diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc b/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc index d9e9edf2bbb..cebb9e190eb 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc +++ b/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc @@ -159,19 +159,6 @@ static bool NeedInterchangeNewlineAt( return NeedInterchangeNewlineAfter(PreviousPositionOf(v)); } -template <typename Strategy> -static bool AreSameRanges(Node* node, - const PositionTemplate<Strategy>& start_position, - const PositionTemplate<Strategy>& end_position) { - DCHECK(node); - const EphemeralRange range = - CreateVisibleSelection( - SelectionInDOMTree::Builder().SelectAllChildren(*node).Build()) - .ToNormalizedEphemeralRange(); - return ToPositionInDOMTree(start_position) == range.StartPosition() && - ToPositionInDOMTree(end_position) == range.EndPosition(); -} - static EditingStyle* StyleFromMatchedRulesAndInlineDecl( const HTMLElement* element) { EditingStyle* style = diff --git a/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer_test.cc b/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer_test.cc index 62bb40ef2e6..102e7d25025 100644 --- a/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer_test.cc +++ b/chromium/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer_test.cc @@ -44,10 +44,10 @@ class StyledMarkupSerializerTest : public EditingTestBase { template <typename Strategy> std::string StyledMarkupSerializerTest::Serialize( const CreateMarkupOptions& options) { - PositionTemplate<Strategy> start = PositionTemplate<Strategy>( - GetDocument().body(), PositionAnchorType::kBeforeChildren); - PositionTemplate<Strategy> end = PositionTemplate<Strategy>( - GetDocument().body(), PositionAnchorType::kAfterChildren); + PositionTemplate<Strategy> start = + PositionTemplate<Strategy>::FirstPositionInNode(*GetDocument().body()); + PositionTemplate<Strategy> end = + PositionTemplate<Strategy>::LastPositionInNode(*GetDocument().body()); return CreateMarkup(start, end, options).Utf8(); } @@ -133,65 +133,75 @@ TEST_F(StyledMarkupSerializerTest, Mixed) { TEST_F(StyledMarkupSerializerTest, ShadowTreeDistributeOrder) { const char* body_content = - "<p id=\"host\">00<b id=\"one\">11</b><b id=\"two\">22</b>33</p>"; + "<p id=\"host\">00<b slot='#one' id=\"one\">11</b><b slot='#two' " + "id=\"two\">22</b>33</p>"; const char* shadow_content = - "<a><content select=#two></content><content select=#one></content></a>"; + "<a><slot name='#two'></slot><slot name='#one'></slot></a>"; SetBodyContent(body_content); SetShadowContent(shadow_content, "host"); - EXPECT_EQ("<p id=\"host\"><b id=\"one\">11</b><b id=\"two\">22</b></p>", - Serialize<EditingStrategy>()) + EXPECT_EQ( + "<p id=\"host\"><b slot=\"#one\" id=\"one\">11</b><b slot=\"#two\" " + "id=\"two\">22</b></p>", + Serialize<EditingStrategy>()) << "00 and 33 aren't appeared since they aren't distributed."; EXPECT_EQ( - "<p id=\"host\"><a><b id=\"two\">22</b><b id=\"one\">11</b></a></p>", + "<p id=\"host\"><a><slot name=\"#two\"><b slot=\"#two\" " + "id=\"two\">22</b></slot><slot name=\"#one\"><b slot=\"#one\" " + "id=\"one\">11</b></slot></a></p>", Serialize<EditingInFlatTreeStrategy>()) << "00 and 33 aren't appeared since they aren't distributed."; } TEST_F(StyledMarkupSerializerTest, ShadowTreeInput) { const char* body_content = - "<p id=\"host\">00<b id=\"one\">11</b><b id=\"two\"><input " - "value=\"22\"></b>33</p>"; + "<p id=\"host\">00<b slot='#one' id=\"one\">11</b><b slot='#two' " + "id=\"two\"><input value=\"22\"></b>33</p>"; const char* shadow_content = - "<a><content select=#two></content><content select=#one></content></a>"; + "<a><slot name='#two'></slot><slot name='#one'></slot></a>"; SetBodyContent(body_content); SetShadowContent(shadow_content, "host"); EXPECT_EQ( - "<p id=\"host\"><b id=\"one\">11</b><b id=\"two\"><input " - "value=\"22\"></b></p>", + "<p id=\"host\"><b slot=\"#one\" id=\"one\">11</b><b slot=\"#two\" " + "id=\"two\"><input value=\"22\"></b></p>", Serialize<EditingStrategy>()) << "00 and 33 aren't appeared since they aren't distributed."; EXPECT_EQ( - "<p id=\"host\"><a><b id=\"two\"><input value=\"22\"></b><b " - "id=\"one\">11</b></a></p>", + "<p id=\"host\"><a><slot name=\"#two\"><b slot=\"#two\" " + "id=\"two\"><input value=\"22\"></b></slot><slot name=\"#one\"><b " + "slot=\"#one\" id=\"one\">11</b></slot></a></p>", Serialize<EditingInFlatTreeStrategy>()) << "00 and 33 aren't appeared since they aren't distributed."; } TEST_F(StyledMarkupSerializerTest, ShadowTreeNested) { const char* body_content = - "<p id=\"host\">00<b id=\"one\">11</b><b id=\"two\">22</b>33</p>"; + "<p id='host'>00<b slot='#one' id='one'>11</b><b slot='#two' " + "id='two'>22</b>33</p>"; const char* shadow_content1 = - "<a><content select=#two></content><b id=host2></b><content " - "select=#one></content></a>"; + "<a><slot name='#two'></slot><span id=host2></span><slot " + "name='#one'></slot></a>"; const char* shadow_content2 = "NESTED"; SetBodyContent(body_content); ShadowRoot* shadow_root1 = SetShadowContent(shadow_content1, "host"); CreateShadowRootForElementWithIDAndSetInnerHTML(*shadow_root1, "host2", shadow_content2); - EXPECT_EQ("<p id=\"host\"><b id=\"one\">11</b><b id=\"two\">22</b></p>", - Serialize<EditingStrategy>()) + EXPECT_EQ( + "<p id=\"host\"><b slot=\"#one\" id=\"one\">11</b><b slot=\"#two\" " + "id=\"two\">22</b></p>", + Serialize<EditingStrategy>()) << "00 and 33 aren't appeared since they aren't distributed."; EXPECT_EQ( - "<p id=\"host\"><a><b id=\"two\">22</b><b id=\"host2\">NESTED</b><b " - "id=\"one\">11</b></a></p>", + "<p id=\"host\"><a><slot name=\"#two\"><b slot=\"#two\" " + "id=\"two\">22</b></slot><span id=\"host2\">NESTED</span><slot " + "name=\"#one\"><b slot=\"#one\" id=\"one\">11</b></slot></a></p>", Serialize<EditingInFlatTreeStrategy>()) << "00 and 33 aren't appeared since they aren't distributed."; } TEST_F(StyledMarkupSerializerTest, ShadowTreeInterchangedNewline) { - const char* body_content = "<a id=host><b id=one>1</b></a>"; - const char* shadow_content = "<content select=#one></content><div><br></div>"; + const char* body_content = "<span id=host><b slot='#one' id=one>1</b></span>"; + const char* shadow_content = "<slot name='#one'></slot><div><br></div>"; SetBodyContent(body_content); SetShadowContent(shadow_content, "host"); @@ -205,9 +215,13 @@ TEST_F(StyledMarkupSerializerTest, ShadowTreeInterchangedNewline) { // Note: We check difference between DOM tree result and flat tree // result, because results contain "style" attribute and this test // doesn't care about actual value of "style" attribute. - EXPECT_EQ("/a>", result_from_dom_tree.substr(mismatched_index)); - EXPECT_EQ("div><br></div></a><br class=\"Apple-interchange-newline\">", - result_from_flat_tree.substr(mismatched_index)); + EXPECT_EQ("b slot=\"#one\" id=\"one\">1</b></span>", + result_from_dom_tree.substr(mismatched_index)); + EXPECT_EQ( + "slot name=\"#one\"><b slot=\"#one\" " + "id=\"one\">1</b></slot><div><br></div></span><br " + "class=\"Apple-interchange-newline\">", + result_from_flat_tree.substr(mismatched_index)); } TEST_F(StyledMarkupSerializerTest, StyleDisplayNone) { @@ -238,9 +252,10 @@ TEST_F(StyledMarkupSerializerTest, ShadowTreeStyle) { start_dom, end_dom, ShouldAnnotateOptions()); body_content = - "<p id='host' style='color: red'>00<span id='one'>11</span>22</p>\n"; + "<p id='host' style='color: red'>00<span slot='#one' " + "id='one'>11</span>22</p>\n"; const char* shadow_content = - "<span style='font-weight: bold'><content select=#one></content></span>"; + "<span style='font-weight: bold'><slot name='#one'></slot></span>"; SetBodyContent(body_content); SetShadowContent(shadow_content, "host"); one = GetDocument().getElementById("one"); @@ -253,7 +268,8 @@ TEST_F(StyledMarkupSerializerTest, ShadowTreeStyle) { EXPECT_EQ(serialized_dom, serialized_ict); } -TEST_F(StyledMarkupSerializerTest, AcrossShadow) { +// TODO(crbug.com/1157146): This test breaks without Shadow DOM v0. +TEST_F(StyledMarkupSerializerTest, DISABLED_AcrossShadow) { const char* body_content = "<p id='host1'>[<span id='one'>11</span>]</p><p id='host2'>[<span " "id='two'>22</span>]</p>"; @@ -266,10 +282,11 @@ TEST_F(StyledMarkupSerializerTest, AcrossShadow) { start_dom, end_dom, ShouldAnnotateOptions()); body_content = - "<p id='host1'><span id='one'>11</span></p><p id='host2'><span " - "id='two'>22</span></p>"; - const char* shadow_content1 = "[<content select=#one></content>]"; - const char* shadow_content2 = "[<content select=#two></content>]"; + "<p id='host1'><span slot='#one' id='one'>11</span></p><p " + "id='host2'><span " + "slot='#two' id='two'>22</span></p>"; + const char* shadow_content1 = "[<slot name='#one'></slot>]"; + const char* shadow_content2 = "[<slot name='#two'></slot>]"; SetBodyContent(body_content); SetShadowContent(shadow_content1, "host1"); SetShadowContent(shadow_content2, "host2"); @@ -280,6 +297,8 @@ TEST_F(StyledMarkupSerializerTest, AcrossShadow) { const std::string& serialized_ict = SerializePart<EditingInFlatTreeStrategy>( start_ict, end_ict, ShouldAnnotateOptions()); + // TODO(crbug.com/1157146): serialized_ict contains the <slot> elements, while + // serialized_dom does not. EXPECT_EQ(serialized_dom, serialized_ict); } |