Changeset 236785 in webkit
- Timestamp:
- Oct 2, 2018 11:28:07 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 19 added
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r236781 r236785 1 2018-10-02 Ryosuke Niwa <rniwa@webkit.org> 2 3 Copying content with shadow DOM doesn't copy any contents 4 https://bugs.webkit.org/show_bug.cgi?id=157443 5 6 Reviewed by Wenson Hsieh. 7 8 Added tests for copying and pasting across shadow boundaries with HTML and plain text. 9 10 * editing/pasteboard/copy-paste-across-shadow-boundaries-1-expected.txt: Added. 11 * editing/pasteboard/copy-paste-across-shadow-boundaries-1.html: Added. 12 * editing/pasteboard/copy-paste-across-shadow-boundaries-2-expected.txt: Added. 13 * editing/pasteboard/copy-paste-across-shadow-boundaries-2.html: Added. 14 * editing/pasteboard/copy-paste-across-shadow-boundaries-3-expected.txt: Added. 15 * editing/pasteboard/copy-paste-across-shadow-boundaries-3.html: Added. 16 * editing/pasteboard/copy-paste-across-shadow-boundaries-4-expected.txt: Added. 17 * editing/pasteboard/copy-paste-across-shadow-boundaries-4.html: Added. 18 * editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-1-expected.txt: Added. 19 * editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-1.html: Added. 20 * editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-2-expected.txt: Added. 21 * editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-2.html: Added. 22 * editing/pasteboard/copy-paste-with-shadow-content-expected.txt: Added. 23 * editing/pasteboard/copy-paste-with-shadow-content.html: Added. 24 1 25 2018-10-01 Ryosuke Niwa <rniwa@webkit.org> 2 26 -
trunk/Source/WebCore/ChangeLog
r236784 r236785 1 2018-10-02 Ryosuke Niwa <rniwa@webkit.org> 2 3 Copying content with shadow DOM doesn't copy any contents 4 https://bugs.webkit.org/show_bug.cgi?id=157443 5 6 Reviewed by Wenson Hsieh. 7 8 This patch adds the support for copying and pasting content across shadow boundaries in HTML and plain text, 9 which is enabled whenever selection across shadow boundaries is enabled. 10 11 To do this, TextIterator now has a constructor which takes two Positions, and the node traversal code in 12 StyledMarkupAccumulator has been abstracted via helper functions as done for TextIterator. 13 14 When serializing a HTMl slot element, serialize it as a span with "display: contents" to make sure when 15 the content is pasted into a shadow tree, it wouldn't affect the slot assignment of the shadow tree. 16 17 Tests: editing/pasteboard/copy-paste-across-shadow-boundaries-1.html 18 editing/pasteboard/copy-paste-across-shadow-boundaries-2.html 19 editing/pasteboard/copy-paste-across-shadow-boundaries-3.html 20 editing/pasteboard/copy-paste-across-shadow-boundaries-4.html 21 editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-1.html 22 editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-2.html 23 editing/pasteboard/copy-paste-with-shadow-content.html 24 25 * dom/ComposedTreeIterator.h: 26 (WebCore::assignedSlotIgnoringUserAgentShadow): Moved from TextIterator.cpp. 27 (WebCore::shadowRootIgnoringUserAgentShadow): Ditto. 28 (WebCore::firstChildInComposedTreeIgnoringUserAgentShadow): Ditto. 29 (WebCore::nextSiblingInComposedTreeIgnoringUserAgentShadow): Ditto. 30 * dom/Position.h: 31 (WebCore::Position::treeScope const): Added. 32 * editing/EditingStyle.cpp: 33 (WebCore::EditingStyle::addDisplayContents): Added. 34 * editing/EditingStyle.h: 35 * editing/Editor.cpp: 36 (WebCore::Editor::selectedText const): Use the new behavior when selectionAcrossShadowBoundariesEnabled is set. 37 (WebCore::Editor::selectedTextForDataTransfer const): Ditto. 38 * editing/MarkupAccumulator.cpp: 39 (WebCore::MarkupAccumulator::appendEndElement): Renamed from appendEndTag. Now takes StringBuilder. 40 * editing/MarkupAccumulator.h: 41 (WebCore::MarkupAccumulator::appendEndTag): 42 * editing/TextIterator.cpp: 43 (WebCore::TextIterator::TextIterator): Added a new variant which takes two positions. 44 (WebCore::TextIterator::init): 45 (WebCore::firstChild): 46 (WebCore::nextSibling): 47 (WebCore::plainText): Ditto. 48 * editing/TextIterator.h: 49 * editing/cocoa/EditorCocoa.mm: 50 (WebCore::Editor::selectionInHTMLFormat): Use the new behavior if selectionAcrossShadowBoundariesEnabled is set. 51 * editing/gtk/EditorGtk.cpp: 52 (WebCore::Editor::writeSelectionToPasteboard): Ditto. 53 * editing/markup.cpp: 54 (WebCore::StyledMarkupAccumulator::parentNode): Added. 55 (WebCore::StyledMarkupAccumulator::firstChild): Added. 56 (WebCore::StyledMarkupAccumulator::nextSibling): Added. 57 (WebCore::StyledMarkupAccumulator::nextSkippingChildren): Added. 58 (WebCore::StyledMarkupAccumulator::hasChildNodes): Added. 59 (WebCore::StyledMarkupAccumulator::isDescendantOf): Added. 60 (WebCore::StyledMarkupAccumulator::StyledMarkupAccumulator): 61 (WebCore::StyledMarkupAccumulator::appendElement): Serialize a slot element as a span with display: contents. 62 (WebCore::StyledMarkupAccumulator::appendEndElement): Added. Ditto. 63 (WebCore::StyledMarkupAccumulator::serializeNodes): 64 (WebCore::StyledMarkupAccumulator::traverseNodesForSerialization): Use the newly added helper functions to 65 traverse the composed tree when m_useComposedTree is set. 66 (WebCore::commonShadowIncludingAncestor): Added. 67 (WebCore::serializePreservingVisualAppearanceInternal): Added SerializeComposedTree as an argument. Also use 68 StyledMarkupAccumulator::parentNode to serialize special common ancestors; e.g. to preserve b, i, etc... 69 (WebCore::serializePreservingVisualAppearance): Ditto to the variant which takes VisibleSelection. 70 (WebCore::sanitizedMarkupForFragmentInDocument): 71 * editing/markup.h: 72 * editing/wpe/EditorWPE.cpp: 73 (WebCore::Editor::writeSelectionToPasteboard): 74 * loader/archive/cf/LegacyWebArchive.cpp: 75 (WebCore::LegacyWebArchive::createFromSelection): 76 * page/PageSerializer.cpp: 77 (WebCore::PageSerializer::SerializerMarkupAccumulator::appendEndElement): 78 * testing/Internals.cpp: 79 (WebCore::Internals::setSelectionWithoutValidation): Added. A helper function to create a selection across 80 shadow boundaries for testing purposes. 81 * testing/Internals.h: 82 * testing/Internals.idl: 83 1 84 2018-10-02 Chris Dumez <cdumez@apple.com> 2 85 -
trunk/Source/WebCore/dom/ComposedTreeIterator.h
r208179 r236785 27 27 28 28 #include "ElementAndTextDescendantIterator.h" 29 #include "HTMLSlotElement.h" 29 30 #include "ShadowRoot.h" 30 31 … … 200 201 WEBCORE_EXPORT String composedTreeAsText(ContainerNode& root, ComposedTreeAsTextMode = ComposedTreeAsTextMode::Normal); 201 202 203 204 // Helper functions for walking the composed tree. 205 // FIXME: Use ComposedTreeIterator instead. These functions are more expensive because they might do O(n) work. 206 207 inline HTMLSlotElement* assignedSlotIgnoringUserAgentShadow(Node& node) 208 { 209 auto* slot = node.assignedSlot(); 210 if (!slot || slot->containingShadowRoot()->mode() == ShadowRootMode::UserAgent) 211 return nullptr; 212 return slot; 213 } 214 215 inline ShadowRoot* shadowRootIgnoringUserAgentShadow(Node& node) 216 { 217 auto* shadowRoot = node.shadowRoot(); 218 if (!shadowRoot || shadowRoot->mode() == ShadowRootMode::UserAgent) 219 return nullptr; 220 return shadowRoot; 221 } 222 223 inline Node* firstChildInComposedTreeIgnoringUserAgentShadow(Node& node) 224 { 225 if (auto* shadowRoot = shadowRootIgnoringUserAgentShadow(node)) 226 return shadowRoot->firstChild(); 227 if (is<HTMLSlotElement>(node)) { 228 if (auto* assignedNodes = downcast<HTMLSlotElement>(node).assignedNodes()) 229 return assignedNodes->at(0); 230 } 231 return node.firstChild(); 232 } 233 234 inline Node* nextSiblingInComposedTreeIgnoringUserAgentShadow(Node& node) 235 { 236 if (auto* slot = assignedSlotIgnoringUserAgentShadow(node)) { 237 auto* assignedNodes = slot->assignedNodes(); 238 ASSERT(assignedNodes); 239 auto nodeIndex = assignedNodes->find(&node); 240 ASSERT(nodeIndex != notFound); 241 if (assignedNodes->size() > nodeIndex + 1) 242 return assignedNodes->at(nodeIndex + 1); 243 return nullptr; 244 } 245 return node.nextSibling(); 246 } 247 202 248 } // namespace WebCore -
trunk/Source/WebCore/dom/Position.h
r236649 r236785 118 118 119 119 Document* document() const { return m_anchorNode ? &m_anchorNode->document() : nullptr; } 120 TreeScope* treeScope() const { return m_anchorNode ? &m_anchorNode->treeScope() : nullptr; } 120 121 Element* rootEditableElement() const 121 122 { -
trunk/Source/WebCore/editing/EditingStyle.cpp
r235914 r236785 1370 1370 } 1371 1371 1372 void EditingStyle::addDisplayContents() 1373 { 1374 if (!m_mutableStyle) 1375 m_mutableStyle = MutableStyleProperties::create(); 1376 m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueContents); 1377 } 1378 1372 1379 bool EditingStyle::convertPositionStyle() 1373 1380 { -
trunk/Source/WebCore/editing/EditingStyle.h
r234116 r236785 150 150 void removePropertiesInElementDefaultStyle(Element&); 151 151 void forceInline(); 152 void addDisplayContents(); 152 153 bool convertPositionStyle(); 153 154 bool isFloating(); -
trunk/Source/WebCore/editing/Editor.cpp
r236634 r236785 3183 3183 String Editor::selectedText() const 3184 3184 { 3185 return selectedText(TextIteratorDefaultBehavior); 3185 TextIteratorBehavior behavior = TextIteratorDefaultBehavior; 3186 if (m_frame.settings().selectionAcrossShadowBoundariesEnabled()) 3187 behavior |= TextIteratorTraversesFlatTree; 3188 return selectedText(behavior); 3186 3189 } 3187 3190 3188 3191 String Editor::selectedTextForDataTransfer() const 3189 3192 { 3190 return selectedText(TextIteratorEmitsImageAltText); 3193 TextIteratorBehavior behavior = TextIteratorEmitsImageAltText; 3194 if (m_frame.settings().selectionAcrossShadowBoundariesEnabled()) 3195 behavior |= TextIteratorTraversesFlatTree; 3196 return selectedText(behavior); 3191 3197 } 3192 3198 … … 3194 3200 { 3195 3201 // We remove '\0' characters because they are not visibly rendered to the user. 3196 return plainText(m_frame.selection().toNormalizedRange().get(), behavior).replaceWithLiteral('\0', ""); 3202 auto& selection = m_frame.selection().selection(); 3203 return plainText(selection.start(), selection.end(), behavior).replaceWithLiteral('\0', ""); 3197 3204 } 3198 3205 -
trunk/Source/WebCore/editing/MarkupAccumulator.cpp
r236649 r236785 193 193 } 194 194 195 void MarkupAccumulator::appendEnd Tag(const Element& element)196 { 197 appendEndMarkup( m_markup, element);195 void MarkupAccumulator::appendEndElement(StringBuilder& out, const Element& element) 196 { 197 appendEndMarkup(out, element); 198 198 } 199 199 -
trunk/Source/WebCore/editing/MarkupAccumulator.h
r236649 r236785 77 77 { 78 78 if (is<Element>(node)) 79 appendEnd Tag(downcast<Element>(node));79 appendEndElement(m_markup, downcast<Element>(node)); 80 80 } 81 81 82 virtual void appendEnd Tag(const Element&);82 virtual void appendEndElement(StringBuilder&, const Element&); 83 83 virtual void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*); 84 84 virtual void appendText(StringBuilder&, const Text&); -
trunk/Source/WebCore/editing/TextIterator.cpp
r236607 r236785 28 28 #include "TextIterator.h" 29 29 30 #include "ComposedTreeIterator.h" 30 31 #include "Document.h" 31 32 #include "Editing.h" … … 340 341 // -------- 341 342 343 344 TextIterator::TextIterator(Position start, Position end, TextIteratorBehavior behavior) 345 : m_behavior(behavior) 346 { 347 if (start.isNull() || end.isNull()) 348 return; 349 ASSERT(comparePositions(start, end) <= 0); 350 351 RELEASE_ASSERT(behavior & TextIteratorTraversesFlatTree || start.treeScope() == end.treeScope()); 352 353 start.document()->updateLayoutIgnorePendingStylesheets(); 354 355 // FIXME: Use Position / PositionIterator instead to avoid offset computation. 356 m_startContainer = start.containerNode(); 357 m_startOffset = start.computeOffsetInContainerNode(); 358 359 m_endContainer = end.containerNode(); 360 m_endOffset = end.computeOffsetInContainerNode(); 361 362 m_node = start.firstNode().get(); 363 if (!m_node) 364 return; 365 366 init(); 367 } 368 342 369 TextIterator::TextIterator(const Range* range, TextIteratorBehavior behavior) 343 370 : m_behavior(behavior) 344 371 { 345 // FIXME: Only m_positionNode above needs to be initialized if range is null.346 372 if (!range) 347 373 return; … … 359 385 m_endOffset = range->endOffset(); 360 386 361 // Set up the current node for processing.362 387 m_node = range->firstNode(); 363 388 if (!m_node) 364 389 return; 365 390 391 init(); 392 } 393 394 void TextIterator::init() 395 { 366 396 if (isClippedByFrameAncestor(m_node->document(), m_behavior)) 367 397 return; … … 380 410 TextIterator::~TextIterator() = default; 381 411 382 static HTMLSlotElement* assignedAuthorSlot(Node& node)383 {384 auto* slot = node.assignedSlot();385 if (!slot || slot->containingShadowRoot()->mode() == ShadowRootMode::UserAgent)386 return nullptr;387 return slot;388 }389 390 static ShadowRoot* authorShadowRoot(Node& node)391 {392 auto* shadowRoot = node.shadowRoot();393 if (!shadowRoot || shadowRoot->mode() == ShadowRootMode::UserAgent)394 return nullptr;395 return shadowRoot;396 }397 398 412 // FIXME: Use ComposedTreeIterator instead. These functions are more expensive because they might do O(n) work. 399 static inline Node* firstChildInFlatTreeIgnoringUserAgentShadow(Node& node) 400 { 401 if (auto* shadowRoot = authorShadowRoot(node)) 402 return shadowRoot->firstChild(); 403 if (is<HTMLSlotElement>(node)) { 404 if (auto* assignedNodes = downcast<HTMLSlotElement>(node).assignedNodes()) 405 return assignedNodes->at(0); 406 } 413 static inline Node* firstChild(TextIteratorBehavior options, Node& node) 414 { 415 if (UNLIKELY(options & TextIteratorTraversesFlatTree)) 416 return firstChildInComposedTreeIgnoringUserAgentShadow(node); 407 417 return node.firstChild(); 408 418 } 409 419 410 static inline Node* nextSiblingInFlatTreeIgnoringUserAgentShadow(Node& node) 411 { 412 if (auto* slot = assignedAuthorSlot(node)) { 413 auto* assignedNodes = slot->assignedNodes(); 414 ASSERT(assignedNodes); 415 auto nodeIndex = assignedNodes->find(&node); 416 ASSERT(nodeIndex != notFound); 417 if (assignedNodes->size() > nodeIndex + 1) 418 return assignedNodes->at(nodeIndex + 1); 419 return nullptr; 420 } 421 return node.nextSibling(); 422 } 423 424 static inline Node* firstChild(TextIteratorBehavior options, Node& node) 420 static inline Node* nextSibling(TextIteratorBehavior options, Node& node) 425 421 { 426 422 if (UNLIKELY(options & TextIteratorTraversesFlatTree)) 427 return firstChildInFlatTreeIgnoringUserAgentShadow(node); 428 return node.firstChild(); 429 } 430 431 static inline Node* nextSibling(TextIteratorBehavior options, Node& node) 432 { 433 if (UNLIKELY(options & TextIteratorTraversesFlatTree)) 434 return nextSiblingInFlatTreeIgnoringUserAgentShadow(node); 423 return nextSiblingInComposedTreeIgnoringUserAgentShadow(node); 435 424 return node.nextSibling(); 436 425 } … … 2655 2644 } 2656 2645 2657 String plainText( const Range* r, TextIteratorBehavior defaultBehavior, bool isDisplayString)2646 String plainText(Position start, Position end, TextIteratorBehavior defaultBehavior, bool isDisplayString) 2658 2647 { 2659 2648 // The initial buffer size can be critical for performance: https://bugs.webkit.org/show_bug.cgi?id=81192 2660 2649 static const unsigned initialCapacity = 1 << 15; 2650 2651 if (!start.document()) 2652 return { }; 2653 auto document = makeRef(*start.document()); 2661 2654 2662 2655 unsigned bufferLength = 0; … … 2666 2659 if (!isDisplayString) 2667 2660 behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsTextsWithoutTranscoding); 2668 2669 for (TextIterator it( r, behavior); !it.atEnd(); it.advance()) {2661 2662 for (TextIterator it(start, end, behavior); !it.atEnd(); it.advance()) { 2670 2663 it.appendTextToStringBuilder(builder); 2671 2664 bufferLength += it.text().length(); … … 2678 2671 2679 2672 if (isDisplayString) 2680 r->ownerDocument().displayStringModifiedByEncoding(result);2673 document->displayStringModifiedByEncoding(result); 2681 2674 2682 2675 return result; 2676 } 2677 2678 String plainText(const Range* range, TextIteratorBehavior defaultBehavior, bool isDisplayString) 2679 { 2680 if (!range) 2681 return emptyString(); 2682 return plainText(range->startPosition(), range->endPosition(), defaultBehavior, isDisplayString); 2683 2683 } 2684 2684 -
trunk/Source/WebCore/editing/TextIterator.h
r225117 r236785 44 44 } 45 45 46 WEBCORE_EXPORT String plainText(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false); 47 46 48 WEBCORE_EXPORT String plainText(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false); 47 49 WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false); … … 100 102 class TextIterator { 101 103 public: 104 explicit TextIterator(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior); 102 105 WEBCORE_EXPORT explicit TextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior); 103 106 WEBCORE_EXPORT ~TextIterator(); … … 119 122 120 123 private: 124 void init(); 121 125 void exitNode(Node*); 122 126 bool shouldRepresentNodeOffsetZero(); -
trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm
r236707 r236785 50 50 #import "RenderElement.h" 51 51 #import "RenderStyle.h" 52 #import "Settings.h" 52 53 #import "Text.h" 53 54 #import "WebContentReader.h" … … 76 77 String Editor::selectionInHTMLFormat() 77 78 { 78 return serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy); 79 return serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy, 80 m_frame.settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No); 79 81 } 80 82 -
trunk/Source/WebCore/editing/gtk/EditorGtk.cpp
r236707 r236785 44 44 #include "SVGImageElement.h" 45 45 #include "SelectionData.h" 46 #include "Settings.h" 46 47 #include "XLinkNames.h" 47 48 #include "markup.h" … … 147 148 pasteboardContent.canSmartCopyOrDelete = canSmartCopyOrDelete(); 148 149 pasteboardContent.text = selectedTextForDataTransfer(); 149 pasteboardContent.markup = serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy); 150 pasteboardContent.markup = serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy, 151 m_frame.settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No); 150 152 pasteboard.write(pasteboardContent); 151 153 } -
trunk/Source/WebCore/editing/markup.cpp
r236762 r236785 38 38 #include "ChildListMutationScope.h" 39 39 #include "Comment.h" 40 #include "ComposedTreeIterator.h" 40 41 #include "DocumentFragment.h" 41 42 #include "DocumentLoader.h" … … 220 221 enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode }; 221 222 222 StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, 223 ResolveURLs,AnnotateForInterchange, MSOListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr);223 StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs, SerializeComposedTree, 224 AnnotateForInterchange, MSOListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr); 224 225 225 226 Node* serializeNodes(const Position& start, const Position& end); … … 233 234 using MarkupAccumulator::appendString; 234 235 236 ContainerNode* parentNode(Node& node) 237 { 238 if (UNLIKELY(m_useComposedTree)) 239 return node.parentInComposedTree(); 240 return node.parentOrShadowHostNode(); 241 } 242 235 243 private: 236 244 void appendStyleNodeOpenTag(StringBuilder&, StyleProperties*, Document&, bool isBlock = false); … … 243 251 244 252 void appendElement(StringBuilder& out, const Element&, bool addDisplayInline, RangeFullySelectsNode); 253 void appendEndElement(StringBuilder& out, const Element&) override; 245 254 void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override; 246 255 … … 249 258 { 250 259 appendElement(out, element, false, DoesFullySelectNode); 260 } 261 262 Node* firstChild(Node& node) 263 { 264 if (UNLIKELY(m_useComposedTree)) 265 return firstChildInComposedTreeIgnoringUserAgentShadow(node); 266 return node.firstChild(); 267 } 268 269 Node* nextSibling(Node& node) 270 { 271 if (UNLIKELY(m_useComposedTree)) 272 return nextSiblingInComposedTreeIgnoringUserAgentShadow(node); 273 return node.nextSibling(); 274 } 275 276 Node* nextSkippingChildren(Node& node) 277 { 278 if (UNLIKELY(m_useComposedTree)) { 279 if (auto* sibling = nextSiblingInComposedTreeIgnoringUserAgentShadow(node)) 280 return sibling; 281 for (auto* ancestor = node.parentInComposedTree(); ancestor; ancestor = ancestor->parentInComposedTree()) { 282 if (auto* sibling = nextSiblingInComposedTreeIgnoringUserAgentShadow(*ancestor)) 283 return sibling; 284 } 285 return nullptr; 286 } 287 return NodeTraversal::nextSkippingChildren(node); 288 } 289 290 bool hasChildNodes(Node& node) 291 { 292 if (UNLIKELY(m_useComposedTree)) 293 return firstChildInComposedTreeIgnoringUserAgentShadow(node); 294 return node.hasChildNodes(); 295 } 296 297 bool isDescendantOf(Node& node, Node& possibleAncestor) 298 { 299 if (UNLIKELY(m_useComposedTree)) 300 return node.isDescendantOrShadowDescendantOf(&possibleAncestor); 301 return node.isDescendantOf(&possibleAncestor); 251 302 } 252 303 … … 272 323 RefPtr<Node> m_highestNodeToBeSerialized; 273 324 RefPtr<EditingStyle> m_wrappingStyle; 325 bool m_useComposedTree; 274 326 bool m_needsPositionStyleConversion; 275 327 bool m_needRelativeStyleWrapper { false }; … … 279 331 }; 280 332 281 inline StyledMarkupAccumulator::StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, 282 ResolveURLs urlsToResolve,AnnotateForInterchange annotate, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)333 inline StyledMarkupAccumulator::StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs urlsToResolve, SerializeComposedTree serializeComposedTree, 334 AnnotateForInterchange annotate, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized) 283 335 : MarkupAccumulator(nodes, urlsToResolve) 284 336 , m_start(start) … … 286 338 , m_annotate(annotate) 287 339 , m_highestNodeToBeSerialized(highestNodeToBeSerialized) 340 , m_useComposedTree(serializeComposedTree == SerializeComposedTree::Yes) 288 341 , m_needsPositionStyleConversion(needsPositionStyleConversion) 289 342 , m_shouldPreserveMSOList(msoListMode == MSOListMode::Preserve) … … 446 499 { 447 500 const bool documentIsHTML = element.document().isHTMLDocument(); 448 appendOpenTag(out, element, 0); 501 const bool isSlotElement = is<HTMLSlotElement>(element); 502 if (UNLIKELY(isSlotElement)) 503 out.append("<span"); 504 else 505 appendOpenTag(out, element, nullptr); 449 506 450 507 appendCustomAttributes(out, element, nullptr); 451 508 452 509 const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline); 453 bool shouldOverrideStyleAttr = (shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element) ) && !shouldPreserveMSOListStyleForElement(element);510 bool shouldOverrideStyleAttr = (shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element) || isSlotElement) && !shouldPreserveMSOListStyleForElement(element); 454 511 if (element.hasAttributes()) { 455 512 for (const Attribute& attribute : element.attributesIterator()) { … … 473 530 newInlineStyle = EditingStyle::create(); 474 531 532 if (isSlotElement) 533 newInlineStyle->addDisplayContents(); 534 475 535 if (is<StyledElement>(element) && downcast<StyledElement>(element).inlineStyle()) 476 536 newInlineStyle->overrideWithStyle(*downcast<StyledElement>(element).inlineStyle()); … … 504 564 } 505 565 566 void StyledMarkupAccumulator::appendEndElement(StringBuilder& out, const Element& element) 567 { 568 if (UNLIKELY(is<HTMLSlotElement>(element))) 569 out.append("</span>"); 570 else 571 MarkupAccumulator::appendEndElement(out, element); 572 } 573 506 574 Node* StyledMarkupAccumulator::serializeNodes(const Position& start, const Position& end) 507 575 { … … 509 577 auto startNode = start.firstNode(); 510 578 Node* pastEnd = end.computeNodeAfterPosition(); 511 if (!pastEnd )512 pastEnd = NodeTraversal::nextSkippingChildren(*end.containerNode());579 if (!pastEnd && end.containerNode()) 580 pastEnd = nextSkippingChildren(*end.containerNode()); 513 581 514 582 if (!m_highestNodeToBeSerialized) { … … 536 604 } 537 605 538 if (!node.renderer() && !enclosingElementWithTag(firstPositionInOrBeforeNode(&node), selectTag)) 606 bool isDisplayContents = is<Element>(node) && downcast<Element>(node).hasDisplayContents(); 607 if (!node.renderer() && !isDisplayContents && !enclosingElementWithTag(firstPositionInOrBeforeNode(&node), selectTag)) 539 608 return false; 540 609 … … 562 631 Node* lastNode = nullptr; 563 632 Node* next = nullptr; 564 for (Node* n = startNode; n != pastEnd; n = next) { 565 lastNode = n; 633 for (auto* n = startNode; n != pastEnd; lastNode = n, n = next) { 566 634 567 635 Vector<Node*, 8> exitedAncestors; 568 636 next = nullptr; 569 if (auto* firstChild = n->firstChild())570 next = firstChild;571 else if (auto* nextSibling = n->nextSibling())572 next = nextSibling;637 if (auto* child = firstChild(*n)) 638 next = child; 639 else if (auto* sibling = nextSibling(*n)) 640 next = sibling; 573 641 else { 574 for (auto* ancestor = n->parentNode(); ancestor; ancestor = ancestor->parentNode()) {642 for (auto* ancestor = parentNode(*n); ancestor; ancestor = parentNode(*ancestor)) { 575 643 exitedAncestors.append(ancestor); 576 if (auto* nextSibling = ancestor->nextSibling()) {577 next = nextSibling;644 if (auto* sibling = nextSibling(*ancestor)) { 645 next = sibling; 578 646 break; 579 647 } … … 589 657 next = NodeTraversal::nextSkippingChildren(*n); 590 658 // Don't skip over pastEnd. 591 if (pastEnd && pastEnd->isDescendantOf(*n))659 if (pastEnd && isDescendantOf(*pastEnd, *n)) 592 660 next = pastEnd; 593 661 } else { 594 if (! n->hasChildNodes())662 if (!hasChildNodes(*n)) 595 663 exitNode(*n); 596 664 } … … 602 670 } 603 671 } 604 605 for (auto* ancestor = (pastEnd ? pastEnd : lastNode)->parentNode(); ancestor && depth; ancestor = ancestor->parentNode()) 606 exitNode(*ancestor); 672 673 ASSERT(lastNode || !depth); 674 if (depth) { 675 for (auto* ancestor = parentNode(pastEnd ? *pastEnd : *lastNode); ancestor && depth; ancestor = parentNode(*ancestor)) 676 exitNode(*ancestor); 677 } 607 678 608 679 return lastClosed; … … 755 826 } 756 827 757 static String serializePreservingVisualAppearanceInternal(const Position& start, const Position& end, Vector<Node*>* nodes, 758 AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, ResolveURLs urlsToResolve, MSOListMode msoListMode) 828 static RefPtr<Node> commonShadowIncludingAncestor(const Position& a, const Position& b) 829 { 830 TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode()); 831 if (!commonScope) 832 return nullptr; 833 auto* nodeA = commonScope->ancestorNodeInThisScope(a.containerNode()); 834 ASSERT(nodeA); 835 auto* nodeB = commonScope->ancestorNodeInThisScope(b.containerNode()); 836 ASSERT(nodeB); 837 return Range::commonAncestorContainer(nodeA, nodeB); 838 } 839 840 static String serializePreservingVisualAppearanceInternal(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs urlsToResolve, SerializeComposedTree serializeComposedTree, 841 AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, MSOListMode msoListMode) 759 842 { 760 843 static NeverDestroyed<const String> interchangeNewlineString(MAKE_STATIC_STRING_IMPL("<br class=\"" AppleInterchangeNewline "\">")); … … 763 846 return emptyString(); 764 847 765 RefPtr<Node> commonAncestor = Range::commonAncestorContainer(start.containerNode(), end.containerNode());848 RefPtr<Node> commonAncestor = commonShadowIncludingAncestor(start, end); 766 849 if (!commonAncestor) 767 850 return emptyString(); … … 782 865 Node* specialCommonAncestor = highestAncestorToWrapMarkup(start, end, *commonAncestor, annotate); 783 866 784 StyledMarkupAccumulator accumulator(start, end, nodes, urlsToResolve, annotate, msoListMode, needsPositionStyleConversion, specialCommonAncestor);867 StyledMarkupAccumulator accumulator(start, end, nodes, urlsToResolve, serializeComposedTree, annotate, msoListMode, needsPositionStyleConversion, specialCommonAncestor); 785 868 786 869 Position startAdjustedForInterchangeNewline = start; … … 800 883 if (specialCommonAncestor && lastClosed) { 801 884 // Also include all of the ancestors of lastClosed up to this special ancestor. 802 for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {885 for (ContainerNode* ancestor = accumulator.parentNode(*lastClosed); ancestor; ancestor = accumulator.parentNode(*ancestor)) { 803 886 if (ancestor == fullySelectedRoot && convertBlocksToInlines == ConvertBlocksToInlines::No) { 804 887 RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(*fullySelectedRoot); … … 850 933 String serializePreservingVisualAppearance(const Range& range, Vector<Node*>* nodes, AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, ResolveURLs urlsToReslve) 851 934 { 852 return serializePreservingVisualAppearanceInternal(range.startPosition(), range.endPosition(), nodes, annotate, convertBlocksToInlines, urlsToReslve, MSOListMode::DoNotPreserve); 853 } 854 855 String serializePreservingVisualAppearance(const VisibleSelection& selection, ResolveURLs resolveURLs, Vector<Node*>* nodes) 856 { 857 return serializePreservingVisualAppearanceInternal(selection.start(), selection.end(), nodes, 858 AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, resolveURLs, MSOListMode::DoNotPreserve); 935 return serializePreservingVisualAppearanceInternal(range.startPosition(), range.endPosition(), nodes, urlsToReslve, SerializeComposedTree::No, 936 annotate, convertBlocksToInlines, MSOListMode::DoNotPreserve); 937 } 938 939 String serializePreservingVisualAppearance(const VisibleSelection& selection, ResolveURLs resolveURLs, SerializeComposedTree serializeComposedTree, Vector<Node*>* nodes) 940 { 941 return serializePreservingVisualAppearanceInternal(selection.start(), selection.end(), nodes, resolveURLs, serializeComposedTree, 942 AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, MSOListMode::DoNotPreserve); 859 943 } 860 944 … … 881 965 bodyElement->appendChild(fragment.get()); 882 966 967 // SerializeComposedTree::No because there can't be a shadow tree in the pasted fragment. 883 968 auto result = serializePreservingVisualAppearanceInternal(firstPositionInNode(bodyElement.get()), lastPositionInNode(bodyElement.get()), nullptr, 884 AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, ResolveURLs::YesExcludingLocalFileURLsForPrivacy,msoListMode);969 ResolveURLs::YesExcludingLocalFileURLsForPrivacy, SerializeComposedTree::No, AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, msoListMode); 885 970 886 971 if (msoListMode == MSOListMode::Preserve) { -
trunk/Source/WebCore/editing/markup.h
r236707 r236785 71 71 enum class ResolveURLs : uint8_t { No, Yes, YesExcludingLocalFileURLsForPrivacy }; 72 72 enum class ConvertBlocksToInlines : uint8_t { No, Yes }; 73 enum class SerializeComposedTree : uint8_t { No, Yes }; 73 74 WEBCORE_EXPORT String serializePreservingVisualAppearance(const Range&, Vector<Node*>* = nullptr, AnnotateForInterchange = AnnotateForInterchange::No, ConvertBlocksToInlines = ConvertBlocksToInlines::No, ResolveURLs = ResolveURLs::No); 74 String serializePreservingVisualAppearance(const VisibleSelection&, ResolveURLs = ResolveURLs::No, Vector<Node*>* = nullptr);75 String serializePreservingVisualAppearance(const VisibleSelection&, ResolveURLs = ResolveURLs::No, SerializeComposedTree = SerializeComposedTree::No, Vector<Node*>* = nullptr); 75 76 76 77 enum class SerializedNodes : uint8_t { SubtreeIncludingNode, SubtreesOfChildren }; -
trunk/Source/WebCore/editing/wpe/EditorWPE.cpp
r236707 r236785 65 65 PasteboardWebContent pasteboardContent; 66 66 pasteboardContent.text = selectedTextForDataTransfer(); 67 pasteboardContent.markup = serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy); 67 pasteboardContent.markup = serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy, 68 m_frame.settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No); 68 69 pasteboard.write(pasteboardContent); 69 70 } -
trunk/Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp
r236762 r236785 555 555 556 556 Vector<Node*> nodeList; 557 builder.append(serializePreservingVisualAppearance(frame->selection().selection(), ResolveURLs::No, &nodeList)); 557 auto serializeComposedTree = frame->settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No; 558 builder.append(serializePreservingVisualAppearance(frame->selection().selection(), ResolveURLs::No, serializeComposedTree, &nodeList)); 558 559 559 560 auto archive = create(builder.toString(), *frame, nodeList, nullptr); -
trunk/Source/WebCore/page/PageSerializer.cpp
r236762 r236785 106 106 void appendElement(StringBuilder&, const Element&, Namespaces*) override; 107 107 void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override; 108 void appendEnd Tag(const Element&) override;108 void appendEndElement(StringBuilder&, const Element&) override; 109 109 }; 110 110 … … 159 159 } 160 160 161 void PageSerializer::SerializerMarkupAccumulator::appendEnd Tag(const Element& element)161 void PageSerializer::SerializerMarkupAccumulator::appendEndElement(StringBuilder& out, const Element& element) 162 162 { 163 163 if (!shouldIgnoreElement(element)) 164 MarkupAccumulator::appendEnd Tag(element);164 MarkupAccumulator::appendEndElement(out, element); 165 165 } 166 166 -
trunk/Source/WebCore/testing/Internals.cpp
r236773 r236785 3450 3450 } 3451 3451 3452 void Internals::setSelectionWithoutValidation(Ref<Node> baseNode, unsigned baseOffset, RefPtr<Node> extentNode, unsigned extentOffset) 3453 { 3454 contextDocument()->frame()->selection().moveTo( 3455 VisiblePosition { createLegacyEditingPosition(baseNode.ptr(), baseOffset) }, 3456 VisiblePosition { createLegacyEditingPosition(extentNode.get(), extentOffset) }); 3457 } 3458 3452 3459 ExceptionOr<bool> Internals::isPluginUnavailabilityIndicatorObscured(Element& element) 3453 3460 { -
trunk/Source/WebCore/testing/Internals.h
r236773 r236785 535 535 536 536 ExceptionOr<Ref<DOMRect>> selectionBounds(); 537 void setSelectionWithoutValidation(Ref<Node> baseNode, unsigned baseOffset, RefPtr<Node> extentNode, unsigned extentOffset); 537 538 538 539 ExceptionOr<bool> isPluginUnavailabilityIndicatorObscured(Element&); -
trunk/Source/WebCore/testing/Internals.idl
r236773 r236785 542 542 543 543 [MayThrowException] DOMRect selectionBounds(); 544 void setSelectionWithoutValidation(Node baseNode, unsigned long baseOffset, Node? extentNode, unsigned long extentOffset); 544 545 545 546 [Conditional=MEDIA_SOURCE] void initializeMockMediaSource();
Note: See TracChangeset
for help on using the changeset viewer.