- Revision
- 289839
- Author
- wenson_hs...@apple.com
- Date
- 2022-02-15 12:47:29 -0800 (Tue, 15 Feb 2022)
Log Message
Refactor logic for writing selected text in image overlays to the pasteboard
https://bugs.webkit.org/show_bug.cgi?id=236546
rdar://83173693
Reviewed by Megan Gardner.
Refactor logic for copying text in image overlays to use `Editor::writeSelection`, as opposed to extracting a
string from the current selection range and directly writing it to the pasteboard as plain text. See below for
more details.
* dom/ImageOverlay.cpp:
Add a couple of new ImageOverlay-namespaced helper functions.
(WebCore::ImageOverlay::characterRange):
Add a function to return the character range for the given selection inside an image overlay (or `std::nullopt`
if the selection is not inside of an image overlay).
(WebCore::ImageOverlay::isInsideOverlay):
Add a function to determine whether or not the given selection is inside of an image overlay.
* dom/ImageOverlay.h:
* editing/Editor.cpp:
(WebCore::Editor::performCutOrCopy):
Refactor this to use `writeSelectionToPasteboard()` when the current selection is inside an image overlay.
* editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::selectionInHTMLFormat):
Intentionally return the null string for image overlays.
(WebCore::selectionInImageOverlayAsAttributedString):
(WebCore::selectionAsAttributedString):
Add a special codepath when serializing selected text in image overlays such that we consult the image element's
TextRecognitionResult for the attributed string to write to the pasteboard, given the selected CharacterRange in
the image overlay.
(WebCore::Editor::selectionInWebArchiveFormat):
Intentionally return null for image overlays.
* page/Page.cpp:
(WebCore::Page::cachedTextRecognitionResult const):
Add a helper method to retrieve a cached TextRecognitionResult, given a host image element.
* page/Page.h:
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (289838 => 289839)
--- trunk/Source/WebCore/ChangeLog 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/ChangeLog 2022-02-15 20:47:29 UTC (rev 289839)
@@ -1,3 +1,57 @@
+2022-02-15 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Refactor logic for writing selected text in image overlays to the pasteboard
+ https://bugs.webkit.org/show_bug.cgi?id=236546
+ rdar://83173693
+
+ Reviewed by Megan Gardner.
+
+ Refactor logic for copying text in image overlays to use `Editor::writeSelection`, as opposed to extracting a
+ string from the current selection range and directly writing it to the pasteboard as plain text. See below for
+ more details.
+
+ * dom/ImageOverlay.cpp:
+
+ Add a couple of new ImageOverlay-namespaced helper functions.
+
+ (WebCore::ImageOverlay::characterRange):
+
+ Add a function to return the character range for the given selection inside an image overlay (or `std::nullopt`
+ if the selection is not inside of an image overlay).
+
+ (WebCore::ImageOverlay::isInsideOverlay):
+
+ Add a function to determine whether or not the given selection is inside of an image overlay.
+
+ * dom/ImageOverlay.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::performCutOrCopy):
+
+ Refactor this to use `writeSelectionToPasteboard()` when the current selection is inside an image overlay.
+
+ * editing/cocoa/EditorCocoa.mm:
+ (WebCore::Editor::selectionInHTMLFormat):
+
+ Intentionally return the null string for image overlays.
+
+ (WebCore::selectionInImageOverlayAsAttributedString):
+ (WebCore::selectionAsAttributedString):
+
+ Add a special codepath when serializing selected text in image overlays such that we consult the image element's
+ TextRecognitionResult for the attributed string to write to the pasteboard, given the selected CharacterRange in
+ the image overlay.
+
+ (WebCore::Editor::selectionInWebArchiveFormat):
+
+ Intentionally return null for image overlays.
+
+ * page/Page.cpp:
+ (WebCore::Page::cachedTextRecognitionResult const):
+
+ Add a helper method to retrieve a cached TextRecognitionResult, given a host image element.
+
+ * page/Page.h:
+
2022-02-15 Antti Koivisto <an...@apple.com>
[CSS Container Queries] Support all size features
Modified: trunk/Source/WebCore/dom/ImageOverlay.cpp (289838 => 289839)
--- trunk/Source/WebCore/dom/ImageOverlay.cpp 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/dom/ImageOverlay.cpp 2022-02-15 20:47:29 UTC (rev 289839)
@@ -26,6 +26,7 @@
#include "config.h"
#include "ImageOverlay.h"
+#include "CharacterRange.h"
#include "DOMTokenList.h"
#include "Document.h"
#include "ElementChildIterator.h"
@@ -46,8 +47,10 @@
#include "ShadowRoot.h"
#include "SimpleRange.h"
#include "Text.h"
+#include "TextIterator.h"
#include "TextRecognitionResult.h"
#include "UserAgentStyleSheets.h"
+#include "VisibleSelection.h"
#include <wtf/Range.h>
#include <wtf/WeakPtr.h>
#include <wtf/text/AtomString.h>
@@ -117,6 +120,35 @@
return imageOverlayHost(element) && element.hasClass() && element.classNames().contains(imageOverlayDataDetectorClass());
}
+std::optional<CharacterRange> characterRange(const VisibleSelection& selection)
+{
+ auto selectionRange = selection.range();
+ if (!selectionRange)
+ return std::nullopt;
+
+ if (!isInsideOverlay(selection))
+ return std::nullopt;
+
+ std::optional<SimpleRange> imageOverlayRange;
+ for (auto& ancestor : ancestorsOfType<HTMLDivElement>(*selection.start().containerNode())) {
+ if (ancestor.getIdAttribute() == imageOverlayElementIdentifier()) {
+ imageOverlayRange = makeRangeSelectingNodeContents(ancestor);
+ break;
+ }
+ }
+
+ if (!imageOverlayRange)
+ return std::nullopt;
+
+ return characterRange(resolveCharacterLocation(*imageOverlayRange, 0), *selectionRange);
+}
+
+bool isInsideOverlay(const VisibleSelection& selection)
+{
+ auto range = selection.range();
+ return range && isInsideOverlay(*range);
+}
+
bool isInsideOverlay(const SimpleRange& range)
{
RefPtr commonAncestor = commonInclusiveAncestor<ComposedTree>(range);
Modified: trunk/Source/WebCore/dom/ImageOverlay.h (289838 => 289839)
--- trunk/Source/WebCore/dom/ImageOverlay.h 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/dom/ImageOverlay.h 2022-02-15 20:47:29 UTC (rev 289839)
@@ -31,7 +31,9 @@
class HTMLElement;
class Node;
+class VisibleSelection;
+struct CharacterRange;
struct SimpleRange;
struct TextRecognitionResult;
@@ -45,6 +47,8 @@
WEBCORE_EXPORT bool isOverlayText(const Node*);
void removeOverlaySoonIfNeeded(HTMLElement&);
IntRect containerRect(HTMLElement&);
+std::optional<CharacterRange> characterRange(const VisibleSelection&);
+bool isInsideOverlay(const VisibleSelection&);
#if ENABLE(IMAGE_ANALYSIS)
enum class CacheTextRecognitionResults : bool { No, Yes };
Modified: trunk/Source/WebCore/editing/Editor.cpp (289838 => 289839)
--- trunk/Source/WebCore/editing/Editor.cpp 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/editing/Editor.cpp 2022-02-15 20:47:29 UTC (rev 289839)
@@ -1442,11 +1442,12 @@
updateMarkersForWordsAffectedByEditing(true);
}
- if (enclosingTextFormControl(m_document.selection().selection().start()) || (selection && ImageOverlay::isInsideOverlay(*selection)))
+ if (enclosingTextFormControl(m_document.selection().selection().start()))
Pasteboard::createForCopyAndPaste(PagePasteboardContext::create(m_document.pageID()))->writePlainText(selectedTextForDataTransfer(), canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace);
else {
RefPtr<HTMLImageElement> imageElement;
- if (action == CopyAction)
+ bool isSelectionInImageOverlay = selection && !selection->collapsed() && ImageOverlay::isInsideOverlay(*selection);
+ if (action == CopyAction && !isSelectionInImageOverlay)
imageElement = imageElementFromImageDocument(document());
if (imageElement) {
Modified: trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm (289838 => 289839)
--- trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm 2022-02-15 20:47:29 UTC (rev 289839)
@@ -45,8 +45,10 @@
#import "HTMLConverter.h"
#import "HTMLImageElement.h"
#import "HTMLSpanElement.h"
+#import "ImageOverlay.h"
#import "LegacyNSPasteboardTypes.h"
#import "LegacyWebArchive.h"
+#import "Page.h"
#import "PagePasteboardContext.h"
#import "Pasteboard.h"
#import "PasteboardStrategy.h"
@@ -75,6 +77,8 @@
String Editor::selectionInHTMLFormat()
{
+ if (ImageOverlay::isInsideOverlay(m_document.selection().selection()))
+ return { };
return serializePreservingVisualAppearance(m_document.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy, SerializeComposedTree::Yes);
}
@@ -100,9 +104,52 @@
#endif
+static RetainPtr<NSAttributedString> selectionInImageOverlayAsAttributedString(const VisibleSelection& selection)
+{
+#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
+ auto* page = selection.document()->page();
+ if (!page)
+ return nil;
+
+ RefPtr hostElement = dynamicDowncast<HTMLElement>(selection.start().containerNode()->shadowHost());
+ if (!hostElement) {
+ ASSERT_NOT_REACHED();
+ return nil;
+ }
+
+ auto cachedResult = page->cachedTextRecognitionResult(*hostElement);
+ if (!cachedResult)
+ return nil;
+
+ auto characterRange = valueOrDefault(ImageOverlay::characterRange(selection));
+ if (!characterRange.length)
+ return nil;
+
+ auto string = stringForRange(*cachedResult, characterRange);
+ __block bool hasAnyAttributes = false;
+ [string enumerateAttributesInRange:NSMakeRange(0, [string length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange, BOOL *stop) {
+ if (attributes.count) {
+ hasAnyAttributes = true;
+ *stop = YES;
+ }
+ }];
+
+ if (!hasAnyAttributes)
+ return nil;
+
+ return string;
+#else
+ UNUSED_PARAM(selection);
+ return nil;
+#endif
+}
+
static RetainPtr<NSAttributedString> selectionAsAttributedString(const Document& document)
{
- auto range = document.selection().selection().firstRange();
+ auto selection = document.selection().selection();
+ if (ImageOverlay::isInsideOverlay(selection))
+ return selectionInImageOverlayAsAttributedString(selection);
+ auto range = selection.firstRange();
return range ? attributedString(*range).string : adoptNS([[NSAttributedString alloc] init]);
}
@@ -146,6 +193,8 @@
RefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
{
+ if (ImageOverlay::isInsideOverlay(m_document.selection().selection()))
+ return nullptr;
auto archive = LegacyWebArchive::createFromSelection(m_document.frame());
if (!archive)
return nullptr;
Modified: trunk/Source/WebCore/page/Page.cpp (289838 => 289839)
--- trunk/Source/WebCore/page/Page.cpp 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/page/Page.cpp 2022-02-15 20:47:29 UTC (rev 289839)
@@ -3836,6 +3836,15 @@
return m_textRecognitionResults.contains(element);
}
+std::optional<TextRecognitionResult> Page::cachedTextRecognitionResult(const HTMLElement& element) const
+{
+ auto iterator = m_textRecognitionResults.find(element);
+ if (iterator == m_textRecognitionResults.end())
+ return std::nullopt;
+
+ return { iterator->value.first };
+}
+
void Page::cacheTextRecognitionResult(const HTMLElement& element, const IntRect& containerRect, const TextRecognitionResult& result)
{
m_textRecognitionResults.set(element, CachedTextRecognitionResult { result, containerRect });
Modified: trunk/Source/WebCore/page/Page.h (289838 => 289839)
--- trunk/Source/WebCore/page/Page.h 2022-02-15 19:14:03 UTC (rev 289838)
+++ trunk/Source/WebCore/page/Page.h 2022-02-15 20:47:29 UTC (rev 289839)
@@ -918,6 +918,7 @@
void setLoadSchedulingMode(LoadSchedulingMode);
#if ENABLE(IMAGE_ANALYSIS)
+ std::optional<TextRecognitionResult> cachedTextRecognitionResult(const HTMLElement&) const;
WEBCORE_EXPORT bool hasCachedTextRecognitionResult(const HTMLElement&) const;
void cacheTextRecognitionResult(const HTMLElement&, const IntRect& containerRect, const TextRecognitionResult&);
void resetTextRecognitionResult(const HTMLElement&);