Skip to content

Commit

Permalink
Content with -webkit-user-select: none should be skipped during copy
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=80159

Reviewed by Darin Adler.

Exclude content with -webkit-user-select: none when copying. This patch introduces
TextIteratorBehavior::IgnoresUserSelectNone to exclude such content from plain text,
and adds IgnoreUserSelectNone as an argument to serializePreservingVisualAppearance
to exclude such content from HTML. It also updates HTMLConverter::_enterElement to
exclude such content from RTFD.

Finally, this patch also updates DOMSelection::toString to have the same treatment.

* LayoutTests/editing/pasteboard/copy-content-with-user-select-none-expected.txt: Added.
* LayoutTests/editing/pasteboard/copy-content-with-user-select-none.html: Added.

* Source/WebCore/editing/Editor.cpp:
(WebCore::Editor::selectedText const):
(WebCore::Editor::selectedTextForDataTransfer const):

* Source/WebCore/editing/TextIterator.cpp:
(WebCore::isRendererVisible): Added.
(WebCore::TextIterator::advance):
(WebCore::hasVisibleTextNode):
(WebCore::TextIterator::handleTextNodeFirstLetter):

* Source/WebCore/editing/TextIteratorBehavior.h:

* Source/WebCore/editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::selectionInHTMLFormat):

* Source/WebCore/editing/cocoa/HTMLConverter.mm:
(HTMLConverter::_enterElement):

* Source/WebCore/editing/gtk/EditorGtk.cpp:
(WebCore::Editor::writeSelectionToPasteboard):

* Source/WebCore/editing/libwpe/EditorLibWPE.cpp:
(WebCore::Editor::writeSelectionToPasteboard):

* Source/WebCore/editing/mac/EditorMac.mm:
(WebCore::Editor::writeImageToPasteboard):

* Source/WebCore/editing/markup.cpp:
(WebCore::StyledMarkupAccumulator::StyledMarkupAccumulator):
(WebCore::StyledMarkupAccumulator::renderedTextRespectingRange):
(WebCore::StyledMarkupAccumulator::traverseNodesForSerialization):
(WebCore::serializePreservingVisualAppearanceInternal):
(WebCore::serializePreservingVisualAppearance):
(WebCore::sanitizedMarkupForFragmentInDocument):

* Source/WebCore/editing/markup.h:

* Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp:
(WebCore::LegacyWebArchive::createFromSelection):

* Source/WebCore/page/DOMSelection.cpp:
(WebCore::DOMSelection::toString const):

* Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm:
(CopyRTF.StripsUserSelectNone):

Canonical link: https://commits.webkit.org/257749@main
  • Loading branch information
rniwa committed Dec 12, 2022
1 parent 3a91df7 commit 166aef8
Show file tree
Hide file tree
Showing 15 changed files with 120 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This tests copying excludes content with user-select: none.
To manually test, copy "hello world foo bar" below then paste.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS getSelection().toString().includes("hello") is true
PASS getSelection().toString().includes("world") is false
PASS getSelection().toString().includes("foo") is false
PASS getSelection().toString().includes("bar") is true
PASS event.clipboardData.getData("text/plain").includes("hello") is true
PASS event.clipboardData.getData("text/plain").includes("world") is false
PASS event.clipboardData.getData("text/plain").includes("foo") is false
PASS event.clipboardData.getData("text/plain").includes("bar") is true
PASS event.clipboardData.getData("text/html").includes("hello") is true
PASS event.clipboardData.getData("text/html").includes("world") is false
PASS event.clipboardData.getData("text/html").includes("foo") is false
PASS event.clipboardData.getData("text/html").includes("bar") is true
PASS successfullyParsed is true

TEST COMPLETE
hello world foo bar
hello bar

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<body>
<div id="source">hello <span style="-webkit-user-select: none; user-select: none;">world </span><span inert>foo </span>bar</div>
<div id="destination" contenteditable></div>
<pre id="output"></pre>
</body>
<script src="../../resources/js-test.js"></script>
<script>

description(`This tests copying excludes content with user-select: none.<br>
To manually test, copy "hello world foo bar" below then paste.`);

jsTestIsAsync = true;
getSelection().setBaseAndExtent(source, 0, source, source.childNodes.length);

source.addEventListener("copy", () => {
shouldBeTrue('getSelection().toString().includes("hello")');
shouldBeFalse('getSelection().toString().includes("world")');
shouldBeFalse('getSelection().toString().includes("foo")');
shouldBeTrue('getSelection().toString().includes("bar")');
});

destination.addEventListener("paste", () => {
shouldBeTrue('event.clipboardData.getData("text/plain").includes("hello")');
shouldBeFalse('event.clipboardData.getData("text/plain").includes("world")');
shouldBeFalse('event.clipboardData.getData("text/plain").includes("foo")');
shouldBeTrue('event.clipboardData.getData("text/plain").includes("bar")');
shouldBeTrue('event.clipboardData.getData("text/html").includes("hello")');
shouldBeFalse('event.clipboardData.getData("text/html").includes("world")');
shouldBeFalse('event.clipboardData.getData("text/html").includes("foo")');
shouldBeTrue('event.clipboardData.getData("text/html").includes("bar")');
finishJSTest();
});

if (window.testRunner && window.internals) {
testRunner.dumpAsText();
testRunner.execCommand("Copy");
destination.focus();
testRunner.execCommand("Paste");
} else {
source.addEventListener("copy", () => {
setTimeout(() => destination.focus(), 0);
});
}

</script>
</html>
4 changes: 2 additions & 2 deletions Source/WebCore/editing/Editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3367,12 +3367,12 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, O

String Editor::selectedText() const
{
return selectedText(TextIteratorBehavior::TraversesFlatTree);
return selectedText({ TextIteratorBehavior::TraversesFlatTree, TextIteratorBehavior::IgnoresUserSelectNone });
}

String Editor::selectedTextForDataTransfer() const
{
return selectedText(OptionSet { TextIteratorBehavior::EmitsImageAltText, TextIteratorBehavior::TraversesFlatTree });
return selectedText(OptionSet { TextIteratorBehavior::EmitsImageAltText, TextIteratorBehavior::TraversesFlatTree, TextIteratorBehavior::IgnoresUserSelectNone });
}

String Editor::selectedText(TextIteratorBehaviors behaviors) const
Expand Down
11 changes: 8 additions & 3 deletions Source/WebCore/editing/TextIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,11 @@ static inline bool hasDisplayContents(Node& node)
return is<Element>(node) && downcast<Element>(node).hasDisplayContents();
}

static bool isRendererVisible(const RenderObject* renderer, TextIteratorBehaviors behaviors)
{
return renderer && !(renderer->style().effectiveUserSelect() == UserSelect::None && behaviors.contains(TextIteratorBehavior::IgnoresUserSelectNone));
}

void TextIterator::advance()
{
ASSERT(!atEnd());
Expand Down Expand Up @@ -470,7 +475,7 @@ void TextIterator::advance()

auto* renderer = m_node->renderer();
if (!m_handledNode) {
if (!renderer) {
if (!isRendererVisible(renderer, m_behaviors)) {
m_handledNode = true;
m_handledChildren = !hasDisplayContents(*m_node);
} else {
Expand Down Expand Up @@ -498,7 +503,7 @@ void TextIterator::advance()
while (!next && parentNode) {
if ((pastEnd && parentNode == m_endContainer) || isDescendantOf(m_behaviors, *m_endContainer, *parentNode))
return;
bool haveRenderer = m_node->renderer();
bool haveRenderer = isRendererVisible(m_node->renderer(), m_behaviors);
Node* exitedNode = m_node;
m_node = parentNode;
m_fullyClippedStack.pop();
Expand All @@ -511,7 +516,7 @@ void TextIterator::advance()
return;
}
next = nextSibling(m_behaviors, *m_node);
if (next && m_node->renderer())
if (next && isRendererVisible(m_node->renderer(), m_behaviors))
exitNode(m_node);
}
}
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/editing/TextIteratorBehavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ enum class TextIteratorBehavior : uint16_t {
EntersImageOverlays = 1 << 10,

IgnoresWhiteSpaceAtEndOfRun = 1 << 11,

IgnoresUserSelectNone = 1 << 12,
};

using TextIteratorBehaviors = OptionSet<TextIteratorBehavior>;
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/editing/cocoa/EditorCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
{
if (ImageOverlay::isInsideOverlay(m_document.selection().selection()))
return { };
return serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes);
return serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes, IgnoreUserSelectNone::Yes);
}

#if ENABLE(ATTACHMENT_ELEMENT)
Expand Down
3 changes: 2 additions & 1 deletion Source/WebCore/editing/cocoa/HTMLConverter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1608,10 +1608,11 @@ static NSInteger _colCompare(id block1, id block2, void *)
BOOL HTMLConverter::_enterElement(Element& element, BOOL embedded)
{
String displayValue = _caches->propertyValueForNode(element, CSSPropertyDisplay);
bool hasUserSelectNone = element.renderer() && element.renderer()->style().effectiveUserSelect() == UserSelect::None;

if (element.hasTagName(headTag) && !embedded)
_processHeadElement(element);
else if (!displayValue.length() || !(displayValue == noneAtom() || displayValue == "table-column"_s || displayValue == "table-column-group"_s)) {
else if (!hasUserSelectNone && (!displayValue.length() || !(displayValue == noneAtom() || displayValue == "table-column"_s || displayValue == "table-column-group"_s))) {
if (_caches->isBlockElement(element) && !element.hasTagName(brTag) && !(displayValue == "table-cell"_s && ![_textTables count])
&& !([_textLists count] > 0 && displayValue == "block"_s && !element.hasTagName(liTag) && !element.hasTagName(ulTag) && !element.hasTagName(olTag)))
_newParagraphForElement(element, element.tagName(), NO, YES);
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/editing/gtk/EditorGtk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
PasteboardWebContent pasteboardContent;
pasteboardContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
pasteboardContent.text = selectedTextForDataTransfer();
pasteboardContent.markup = serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes);
pasteboardContent.markup = serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes, IgnoreUserSelectNone::Yes);
pasteboardContent.contentOrigin = m_document.originIdentifierForPasteboard();
pasteboard.write(pasteboardContent);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/editing/libwpe/EditorLibWPE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
{
PasteboardWebContent pasteboardContent;
pasteboardContent.text = selectedTextForDataTransfer();
pasteboardContent.markup = serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes);
pasteboardContent.markup = serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes, IgnoreUserSelectNone::Yes);
pasteboard.write(pasteboardContent);
}

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/editing/mac/EditorMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ static void getImage(Element& imageElement, RefPtr<Image>& image, CachedImage*&
pasteboardImage.dataInWebArchiveFormat = imageInWebArchiveFormat(imageElement);

if (auto imageRange = makeRangeSelectingNode(imageElement))
pasteboardImage.dataInHTMLFormat = serializePreservingVisualAppearance(VisibleSelection { *imageRange }, ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes);
pasteboardImage.dataInHTMLFormat = serializePreservingVisualAppearance(VisibleSelection { *imageRange }, ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::Yes, IgnoreUserSelectNone::Yes);

pasteboardImage.url.url = url;
pasteboardImage.url.title = title;
Expand Down
25 changes: 17 additions & 8 deletions Source/WebCore/editing/markup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class StyledMarkupAccumulator final : public MarkupAccumulator {
public:
enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };

StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs, SerializeComposedTree,
StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs, SerializeComposedTree, IgnoreUserSelectNone,
AnnotateForInterchange, StandardFontFamilySerializationMode, MSOListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr);

Node* serializeNodes(const Position& start, const Position& end);
Expand Down Expand Up @@ -340,6 +340,7 @@ class StyledMarkupAccumulator final : public MarkupAccumulator {
RefPtr<Node> m_highestNodeToBeSerialized;
RefPtr<EditingStyle> m_wrappingStyle;
bool m_useComposedTree;
bool m_ignoresUserSelectNone;
bool m_needsPositionStyleConversion;
StandardFontFamilySerializationMode m_standardFontFamilySerializationMode;
bool m_shouldPreserveMSOList;
Expand All @@ -348,13 +349,16 @@ class StyledMarkupAccumulator final : public MarkupAccumulator {
bool m_inMSOList { false };
};

inline StyledMarkupAccumulator::StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs resolveURLs, SerializeComposedTree serializeComposedTree, AnnotateForInterchange annotate, StandardFontFamilySerializationMode standardFontFamilySerializationMode, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)
inline StyledMarkupAccumulator::StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs resolveURLs,
SerializeComposedTree serializeComposedTree, IgnoreUserSelectNone ignoreUserSelectNone, AnnotateForInterchange annotate,
StandardFontFamilySerializationMode standardFontFamilySerializationMode, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)
: MarkupAccumulator(nodes, resolveURLs)
, m_start(start)
, m_end(end)
, m_annotate(annotate)
, m_highestNodeToBeSerialized(highestNodeToBeSerialized)
, m_useComposedTree(serializeComposedTree == SerializeComposedTree::Yes)
, m_ignoresUserSelectNone(ignoreUserSelectNone == IgnoreUserSelectNone::Yes)
, m_needsPositionStyleConversion(needsPositionStyleConversion)
, m_standardFontFamilySerializationMode(standardFontFamilySerializationMode)
, m_shouldPreserveMSOList(msoListMode == MSOListMode::Preserve)
Expand Down Expand Up @@ -466,6 +470,8 @@ String StyledMarkupAccumulator::renderedTextRespectingRange(const Text& text)
if (!m_end.isNull())
behaviors.add(TextIteratorBehavior::BehavesAsIfNodesFollowing);
}
if (m_ignoresUserSelectNone)
behaviors.add(TextIteratorBehavior::IgnoresUserSelectNone);

auto range = makeSimpleRange(start, end);
return range ? plainText(*range, behaviors) : emptyString();
Expand Down Expand Up @@ -660,6 +666,9 @@ Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node& startNode, No
if (!node.renderer() && !isDisplayContents && !enclosingElementWithTag(firstPositionInOrBeforeNode(&node), selectTag))
return false;

if (m_ignoresUserSelectNone && Position::nodeIsUserSelectNone(&node))
return false;

++depth;
if (shouldEmit)
startAppendingNode(node);
Expand Down Expand Up @@ -877,7 +886,7 @@ static Node* highestAncestorToWrapMarkup(const Position& start, const Position&
return specialCommonAncestor;
}

static String serializePreservingVisualAppearanceInternal(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs resolveURLs, SerializeComposedTree serializeComposedTree,
static String serializePreservingVisualAppearanceInternal(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs resolveURLs, SerializeComposedTree serializeComposedTree, IgnoreUserSelectNone ignoreUserSelectNone,
AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, StandardFontFamilySerializationMode standardFontFamilySerializationMode, MSOListMode msoListMode)
{
static NeverDestroyed<const String> interchangeNewlineString(MAKE_STATIC_STRING_IMPL("<br class=\"" AppleInterchangeNewline "\">"));
Expand All @@ -904,7 +913,7 @@ static String serializePreservingVisualAppearanceInternal(const Position& start,

Node* specialCommonAncestor = highestAncestorToWrapMarkup(start, end, *commonAncestor, annotate);

StyledMarkupAccumulator accumulator(start, end, nodes, resolveURLs, serializeComposedTree, annotate, standardFontFamilySerializationMode, msoListMode, needsPositionStyleConversion, specialCommonAncestor);
StyledMarkupAccumulator accumulator(start, end, nodes, resolveURLs, serializeComposedTree, ignoreUserSelectNone, annotate, standardFontFamilySerializationMode, msoListMode, needsPositionStyleConversion, specialCommonAncestor);

Position startAdjustedForInterchangeNewline = start;
if (annotate == AnnotateForInterchange::Yes && needInterchangeNewlineAfter(visibleStart)) {
Expand Down Expand Up @@ -979,13 +988,13 @@ static String serializePreservingVisualAppearanceInternal(const Position& start,
String serializePreservingVisualAppearance(const SimpleRange& range, Vector<Node*>* nodes, AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, ResolveURLs resolveURLs)
{
return serializePreservingVisualAppearanceInternal(makeDeprecatedLegacyPosition(range.start), makeDeprecatedLegacyPosition(range.end),
nodes, resolveURLs, SerializeComposedTree::No,
nodes, resolveURLs, SerializeComposedTree::No, IgnoreUserSelectNone::No,
annotate, convertBlocksToInlines, StandardFontFamilySerializationMode::Keep, MSOListMode::DoNotPreserve);
}

String serializePreservingVisualAppearance(const VisibleSelection& selection, ResolveURLs resolveURLs, SerializeComposedTree serializeComposedTree, Vector<Node*>* nodes)
String serializePreservingVisualAppearance(const VisibleSelection& selection, ResolveURLs resolveURLs, SerializeComposedTree serializeComposedTree, IgnoreUserSelectNone ignoreUserSelectNone, Vector<Node*>* nodes)
{
return serializePreservingVisualAppearanceInternal(selection.start(), selection.end(), nodes, resolveURLs, serializeComposedTree,
return serializePreservingVisualAppearanceInternal(selection.start(), selection.end(), nodes, resolveURLs, serializeComposedTree, ignoreUserSelectNone,
AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, StandardFontFamilySerializationMode::Keep, MSOListMode::DoNotPreserve);
}

Expand All @@ -1012,7 +1021,7 @@ String sanitizedMarkupForFragmentInDocument(Ref<DocumentFragment>&& fragment, Do

// SerializeComposedTree::No because there can't be a shadow tree in the pasted fragment.
auto result = serializePreservingVisualAppearanceInternal(firstPositionInNode(bodyElement.get()), lastPositionInNode(bodyElement.get()), nullptr,
ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::No, AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, StandardFontFamilySerializationMode::Strip, msoListMode);
ResolveURLs::YesExcludingURLsForPrivacy, SerializeComposedTree::No, IgnoreUserSelectNone::No, AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, StandardFontFamilySerializationMode::Strip, msoListMode);

if (msoListMode != MSOListMode::Preserve)
return result;
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/editing/markup.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ ExceptionOr<void> replaceChildrenWithFragment(ContainerNode&, Ref<DocumentFragme

enum class ConvertBlocksToInlines : uint8_t { No, Yes };
enum class SerializeComposedTree : uint8_t { No, Yes };
enum class IgnoreUserSelectNone: uint8_t { No, Yes };
WEBCORE_EXPORT String serializePreservingVisualAppearance(const SimpleRange&, Vector<Node*>* = nullptr, AnnotateForInterchange = AnnotateForInterchange::No, ConvertBlocksToInlines = ConvertBlocksToInlines::No, ResolveURLs = ResolveURLs::No);
String serializePreservingVisualAppearance(const VisibleSelection&, ResolveURLs = ResolveURLs::No, SerializeComposedTree = SerializeComposedTree::No, Vector<Node*>* = nullptr);
String serializePreservingVisualAppearance(const VisibleSelection&, ResolveURLs = ResolveURLs::No, SerializeComposedTree = SerializeComposedTree::No,
IgnoreUserSelectNone = IgnoreUserSelectNone::Yes, Vector<Node*>* = nullptr);

enum class SerializedNodes : uint8_t { SubtreeIncludingNode, SubtreesOfChildren };
enum class SerializationSyntax : uint8_t { HTML, XML };
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ RefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
builder.append(documentTypeString(*document));

Vector<Node*> nodeList;
builder.append(serializePreservingVisualAppearance(frame->selection().selection(), ResolveURLs::No, SerializeComposedTree::Yes, &nodeList));
builder.append(serializePreservingVisualAppearance(frame->selection().selection(), ResolveURLs::No, SerializeComposedTree::Yes, IgnoreUserSelectNone::Yes, &nodeList));

auto archive = create(builder.toString(), *frame, nodeList, nullptr);
if (!archive)
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/page/DOMSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,10 @@ String DOMSelection::toString() const
return String();
if (frame->settings().liveRangeSelectionEnabled()) {
auto range = this->range();
return range ? plainText(*range) : emptyString();
return range ? plainText(*range, TextIteratorBehavior::IgnoresUserSelectNone) : emptyString();
}
auto range = frame->selection().selection().firstRange();
return range ? plainText(*range) : emptyString();
return range ? plainText(*range, TextIteratorBehavior::IgnoresUserSelectNone) : emptyString();
}

RefPtr<Node> DOMSelection::shadowAdjustedNode(const Position& position) const
Expand Down
7 changes: 7 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,11 @@ static void checkColor(PlatformColor *color, CGFloat red, CGFloat green, CGFloat

#endif // ENABLE(DATA_DETECTION)

TEST(CopyRTF, StripsUserSelectNone)
{
auto attributedString = copyAttributedStringFromHTML(@"hello <span style='-webkit-user-select: none'>world </span><span inert>foo </span>bar", false);

EXPECT_WK_STREQ([attributedString string].UTF8String, "hello bar");
}

#endif // PLATFORM(COCOA)

0 comments on commit 166aef8

Please sign in to comment.