Diff
Modified: branches/safari-608-branch/LayoutTests/ChangeLog (249697 => 249698)
--- branches/safari-608-branch/LayoutTests/ChangeLog 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/LayoutTests/ChangeLog 2019-09-10 03:20:02 UTC (rev 249698)
@@ -1,5 +1,133 @@
2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+ Cherry-pick r249605. rdar://problem/55182896
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore:
+
+ Editor::replaceSelectionWithFragment currently scrolls to reveal the selection after inserting the given
+ DocumentFragment. However, this scrolling occurs before any inserted images have loaded yet, which causes the
+ wrong caret rect to be revealed, since all image elements inserted during paste will be empty.
+
+ To fix this, we defer revealing the selection after inserting the fragment until after all images that have
+ been inserted are done loading. While waiting for images to load, if any layers which may be scrolled as a
+ result of revealing the selection are scrolled, we additionally cancel the deferred selection reveal. See
+ comments below for more detail.
+
+ Tests: editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html
+ editing/pasteboard/reveal-selection-after-pasting-images.html
+ PasteImage.RevealSelectionAfterPastingImage
+
+ * editing/Editing.cpp:
+ (WebCore::visibleImageElementsInRangeWithNonLoadedImages):
+
+ Add a new helper to iterate through a range and collect all image elements in that range, that contain cached
+ images that have not finished loading yet.
+
+ * editing/Editing.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::replaceSelectionWithFragment):
+
+ Instead of always immediately revealing the selection after applying the ReplaceSelectionCommand, collect the
+ image elements that were just inserted, and avoid immediately revealing the selection if any of these images
+ have non-null cached images, but are not loaded yet. Instead, hold on to these images in a set, remove them once
+ they finish loading using the new method below, and once all images are removed, reveal the selection.
+
+ (WebCore::Editor::revealSelectionIfNeededAfterLoadingImageForElement):
+ (WebCore::Editor::renderLayerDidScroll):
+
+ Called whenever a scrollable RenderLayer is scrolled (or in the case of FrameView, the root layer). In the case
+ where Editor is waiting to reveal the selection, we check to see if the scrolled layer is an ancestor of the
+ layer enclosing the start of the selection.
+
+ (WebCore::Editor::respondToChangedSelection):
+
+ If the selection changes between pasting and waiting for pasted images to load, just cancel waiting to reveal
+ the selection after pasting.
+
+ * editing/Editor.h:
+ * editing/ReplaceSelectionCommand.cpp:
+ (WebCore::ReplaceSelectionCommand::insertedContentRange const):
+
+ Add a helper method to grab the Range of content inserted after applying the command.
+
+ * editing/ReplaceSelectionCommand.h:
+ * page/FrameView.cpp:
+ (WebCore::FrameView::scrollPositionChanged):
+ * page/FrameView.h:
+ * page/Page.cpp:
+ (WebCore::Page::didFinishLoadingImageForElement):
+
+ Notify Editor after an image finishes loading.
+
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::scrollTo):
+
+ Source/WebKit:
+
+ Tweak some existing logic to use the new visibleImageElementsInRangeWithNonLoadedImages helper function. See
+ WebCore for more details.
+
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::didConcludeEditDrag):
+
+ Tools:
+
+ Add an API test to exercise the scenario where we scroll to reveal the selection after pasting an image that was
+ directly written to the pasteboard.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
+
+ LayoutTests:
+
+ Add a couple of new layout tests.
+
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt: Added.
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html: Added.
+
+ This test verifies that we don't try to scroll to reveal the caret after pasting, if the scroll position was
+ changed before the images finished loading.
+
+ * editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+ * editing/pasteboard/reveal-selection-after-pasting-images.html: Added.
+ * platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+
+ This test verifies that we reveal the caret after loading multiple pasted images in a selection, and dispatch a
+ scroll event in the process.
+
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249605 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2019-09-06 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Add a couple of new layout tests.
+
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt: Added.
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html: Added.
+
+ This test verifies that we don't try to scroll to reveal the caret after pasting, if the scroll position was
+ changed before the images finished loading.
+
+ * editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+ * editing/pasteboard/reveal-selection-after-pasting-images.html: Added.
+ * platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+
+ This test verifies that we reveal the caret after loading multiple pasted images in a selection, and dispatch a
+ scroll event in the process.
+
+2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+
Cherry-pick r249581. rdar://problem/55202922
REGRESSION (iOS 13): If an overflow:hidden with a non-zero scroll position is toggled to overflow:scroll, some other scroll causes its scroll position to get reset
Added: branches/safari-608-branch/LayoutTests/editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt (0 => 249698)
--- branches/safari-608-branch/LayoutTests/editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt (rev 0)
+++ branches/safari-608-branch/LayoutTests/editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt 2019-09-10 03:20:02 UTC (rev 249698)
@@ -0,0 +1,17 @@
+This test verifies that we do not scroll to reveal the selection after pasting images in an editable area, if the page programmatically scrolls the editor. To run the test manually, copy the selected images and paste into the red editable container above. The container's scroll position should remain at 0.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS allImagesAreDoneLoading() became true
+PASS editor.scrollTop is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Here's to the crazy ones, the misfits, the rebels, the trouble makers, the round pegs in the square holes, the ones who see things differently. There not fond of rules, and they have no respect for the status quo, you can quote then, disagree with them, glorify or vilify them, about the only thing you can't do is ignore them. Because they change things. They push the human race forward. And while some may see them as the crazy ones, we see genius. Because the people who are crazy enough to think they can change the world are the ones who do.
+
+
+
+
+
+
Added: branches/safari-608-branch/LayoutTests/editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html (0 => 249698)
--- branches/safari-608-branch/LayoutTests/editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html (rev 0)
+++ branches/safari-608-branch/LayoutTests/editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html 2019-09-10 03:20:02 UTC (rev 249698)
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src=""
+ <script src=""
+ <script>
+ jsTestIsAsync = true;
+ description("This test verifies that we do not scroll to reveal the selection after pasting images in an editable area, if the page programmatically scrolls the editor. To run the test manually, copy the selected images and paste into the red editable container above. The container's scroll position should remain at 0.");
+
+ function allImagesAreDoneLoading() {
+ for (const image of Array.from(document.body.querySelectorAll("img"))) {
+ if (!image.complete)
+ return false;
+ }
+ return true;
+ }
+
+ addEventListener("load", () => {
+ editor = document.getElementById("editor");
+ images = document.getElementById("images-to-copy");
+ lastLine = document.getElementById("lastLine");
+
+ editor.scrollTo(0, 200);
+ setSelectionCommand(images.firstChild, 0, images.lastChild, 1);
+
+ if (!window.testRunner) {
+ editor.addEventListener("paste", () => setTimeout(() => editor.scrollTo(0, 0)));
+ return;
+ }
+
+ execCopyCommand();
+ setSelectionCommand(lastLine, 0, lastLine, 0);
+ execPasteCommand();
+ editor.scrollTo(0, 0);
+
+ shouldBecomeEqual("allImagesAreDoneLoading()", "true", () => {
+ shouldBe("editor.scrollTop", "0");
+ finishJSTest();
+ });
+ });
+ </script>
+ <style>
+ div#editor {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ border: 1px solid tomato;
+ }
+ </style>
+</head>
+<body>
+ <div id="editor" contenteditable>
+ <span>Here's to the crazy ones, the misfits, the rebels, the trouble makers, the round pegs in the square holes, the ones who see things differently. There not fond of rules, and they have no respect for the status quo, you can quote then, disagree with them, glorify or vilify them, about the only thing you can't do is ignore them. Because they change things. They push the human race forward. And while some may see them as the crazy ones, we see genius. Because the people who are crazy enough to think they can change the world are the ones who do.</span>
+ <div><br id="lastLine"></div>
+ </div>
+ <div id="images-to-copy">
+ <div><img src=""
+ <div><img src=""
+ <div><img src=""
+ </div>
+</body>
+</html>
Added: branches/safari-608-branch/LayoutTests/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt (0 => 249698)
--- branches/safari-608-branch/LayoutTests/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt (rev 0)
+++ branches/safari-608-branch/LayoutTests/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt 2019-09-10 03:20:02 UTC (rev 249698)
@@ -0,0 +1,13 @@
+This test verifies that we scroll to reveal the selection after pasting images in an editable area. To run the test manually, copy the selected images and paste into the red editable container above. The container should scroll to reveal the caret after the last image.
+
+Scrolled after pasting:
+| <div>
+| <img>
+| src=""
+| <div>
+| <img>
+| src=""
+| <div>
+| <img>
+| src=""
+| <#selection-caret>
Added: branches/safari-608-branch/LayoutTests/editing/pasteboard/reveal-selection-after-pasting-images.html (0 => 249698)
--- branches/safari-608-branch/LayoutTests/editing/pasteboard/reveal-selection-after-pasting-images.html (rev 0)
+++ branches/safari-608-branch/LayoutTests/editing/pasteboard/reveal-selection-after-pasting-images.html 2019-09-10 03:20:02 UTC (rev 249698)
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src=""
+ <script src=""
+ <script>
+ Markup.waitUntilDone();
+ Markup.description("This test verifies that we scroll to reveal the selection after pasting images in an editable area. To run the test manually, copy the selected images and paste into the red editable container above. The container should scroll to reveal the caret after the last image.");
+
+ addEventListener("load", () => {
+ editor = document.getElementById("editor");
+ images = document.getElementById("images-to-copy");
+
+ editor.addEventListener("scroll", () => {
+ Markup.dump(editor, "Scrolled after pasting");
+ Markup.notifyDone();
+ }, { once: true });
+
+ setSelectionCommand(images.firstChild, 0, images.lastChild, 1);
+
+ if (!window.testRunner)
+ return;
+
+ execCopyCommand();
+ editor.focus();
+ execPasteCommand();
+ });
+ </script>
+ <style>
+ div#editor {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ border: 1px solid tomato;
+ }
+ </style>
+</head>
+<body>
+ <div id="editor" contenteditable></div>
+ <div id="images-to-copy">
+ <div><img src=""
+ <div><img src=""
+ <div><img src=""
+ </div>
+</body>
+</html>
Added: branches/safari-608-branch/LayoutTests/platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt (0 => 249698)
--- branches/safari-608-branch/LayoutTests/platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt (rev 0)
+++ branches/safari-608-branch/LayoutTests/platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt 2019-09-10 03:20:02 UTC (rev 249698)
@@ -0,0 +1,16 @@
+This test verifies that we scroll to reveal the selection after pasting images in an editable area. To run the test manually, copy the selected images and paste into the red editable container above. The container should scroll to reveal the caret after the last image.
+
+Scrolled after pasting:
+| <div>
+| style="-webkit-text-size-adjust: auto;"
+| <img>
+| src=""
+| <div>
+| style="-webkit-text-size-adjust: auto;"
+| <img>
+| src=""
+| <div>
+| style="-webkit-text-size-adjust: auto;"
+| <img>
+| src=""
+| <#selection-caret>
Modified: branches/safari-608-branch/Source/WebCore/ChangeLog (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/ChangeLog 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/ChangeLog 2019-09-10 03:20:02 UTC (rev 249698)
@@ -1,5 +1,176 @@
2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+ Cherry-pick r249605. rdar://problem/55182896
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore:
+
+ Editor::replaceSelectionWithFragment currently scrolls to reveal the selection after inserting the given
+ DocumentFragment. However, this scrolling occurs before any inserted images have loaded yet, which causes the
+ wrong caret rect to be revealed, since all image elements inserted during paste will be empty.
+
+ To fix this, we defer revealing the selection after inserting the fragment until after all images that have
+ been inserted are done loading. While waiting for images to load, if any layers which may be scrolled as a
+ result of revealing the selection are scrolled, we additionally cancel the deferred selection reveal. See
+ comments below for more detail.
+
+ Tests: editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html
+ editing/pasteboard/reveal-selection-after-pasting-images.html
+ PasteImage.RevealSelectionAfterPastingImage
+
+ * editing/Editing.cpp:
+ (WebCore::visibleImageElementsInRangeWithNonLoadedImages):
+
+ Add a new helper to iterate through a range and collect all image elements in that range, that contain cached
+ images that have not finished loading yet.
+
+ * editing/Editing.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::replaceSelectionWithFragment):
+
+ Instead of always immediately revealing the selection after applying the ReplaceSelectionCommand, collect the
+ image elements that were just inserted, and avoid immediately revealing the selection if any of these images
+ have non-null cached images, but are not loaded yet. Instead, hold on to these images in a set, remove them once
+ they finish loading using the new method below, and once all images are removed, reveal the selection.
+
+ (WebCore::Editor::revealSelectionIfNeededAfterLoadingImageForElement):
+ (WebCore::Editor::renderLayerDidScroll):
+
+ Called whenever a scrollable RenderLayer is scrolled (or in the case of FrameView, the root layer). In the case
+ where Editor is waiting to reveal the selection, we check to see if the scrolled layer is an ancestor of the
+ layer enclosing the start of the selection.
+
+ (WebCore::Editor::respondToChangedSelection):
+
+ If the selection changes between pasting and waiting for pasted images to load, just cancel waiting to reveal
+ the selection after pasting.
+
+ * editing/Editor.h:
+ * editing/ReplaceSelectionCommand.cpp:
+ (WebCore::ReplaceSelectionCommand::insertedContentRange const):
+
+ Add a helper method to grab the Range of content inserted after applying the command.
+
+ * editing/ReplaceSelectionCommand.h:
+ * page/FrameView.cpp:
+ (WebCore::FrameView::scrollPositionChanged):
+ * page/FrameView.h:
+ * page/Page.cpp:
+ (WebCore::Page::didFinishLoadingImageForElement):
+
+ Notify Editor after an image finishes loading.
+
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::scrollTo):
+
+ Source/WebKit:
+
+ Tweak some existing logic to use the new visibleImageElementsInRangeWithNonLoadedImages helper function. See
+ WebCore for more details.
+
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::didConcludeEditDrag):
+
+ Tools:
+
+ Add an API test to exercise the scenario where we scroll to reveal the selection after pasting an image that was
+ directly written to the pasteboard.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
+
+ LayoutTests:
+
+ Add a couple of new layout tests.
+
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt: Added.
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html: Added.
+
+ This test verifies that we don't try to scroll to reveal the caret after pasting, if the scroll position was
+ changed before the images finished loading.
+
+ * editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+ * editing/pasteboard/reveal-selection-after-pasting-images.html: Added.
+ * platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+
+ This test verifies that we reveal the caret after loading multiple pasted images in a selection, and dispatch a
+ scroll event in the process.
+
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249605 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2019-09-06 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Editor::replaceSelectionWithFragment currently scrolls to reveal the selection after inserting the given
+ DocumentFragment. However, this scrolling occurs before any inserted images have loaded yet, which causes the
+ wrong caret rect to be revealed, since all image elements inserted during paste will be empty.
+
+ To fix this, we defer revealing the selection after inserting the fragment until after all images that have
+ been inserted are done loading. While waiting for images to load, if any layers which may be scrolled as a
+ result of revealing the selection are scrolled, we additionally cancel the deferred selection reveal. See
+ comments below for more detail.
+
+ Tests: editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html
+ editing/pasteboard/reveal-selection-after-pasting-images.html
+ PasteImage.RevealSelectionAfterPastingImage
+
+ * editing/Editing.cpp:
+ (WebCore::visibleImageElementsInRangeWithNonLoadedImages):
+
+ Add a new helper to iterate through a range and collect all image elements in that range, that contain cached
+ images that have not finished loading yet.
+
+ * editing/Editing.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::replaceSelectionWithFragment):
+
+ Instead of always immediately revealing the selection after applying the ReplaceSelectionCommand, collect the
+ image elements that were just inserted, and avoid immediately revealing the selection if any of these images
+ have non-null cached images, but are not loaded yet. Instead, hold on to these images in a set, remove them once
+ they finish loading using the new method below, and once all images are removed, reveal the selection.
+
+ (WebCore::Editor::revealSelectionIfNeededAfterLoadingImageForElement):
+ (WebCore::Editor::renderLayerDidScroll):
+
+ Called whenever a scrollable RenderLayer is scrolled (or in the case of FrameView, the root layer). In the case
+ where Editor is waiting to reveal the selection, we check to see if the scrolled layer is an ancestor of the
+ layer enclosing the start of the selection.
+
+ (WebCore::Editor::respondToChangedSelection):
+
+ If the selection changes between pasting and waiting for pasted images to load, just cancel waiting to reveal
+ the selection after pasting.
+
+ * editing/Editor.h:
+ * editing/ReplaceSelectionCommand.cpp:
+ (WebCore::ReplaceSelectionCommand::insertedContentRange const):
+
+ Add a helper method to grab the Range of content inserted after applying the command.
+
+ * editing/ReplaceSelectionCommand.h:
+ * page/FrameView.cpp:
+ (WebCore::FrameView::scrollPositionChanged):
+ * page/FrameView.h:
+ * page/Page.cpp:
+ (WebCore::Page::didFinishLoadingImageForElement):
+
+ Notify Editor after an image finishes loading.
+
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::scrollTo):
+
+2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+
Cherry-pick r249581. rdar://problem/55202922
REGRESSION (iOS 13): If an overflow:hidden with a non-zero scroll position is toggled to overflow:scroll, some other scroll causes its scroll position to get reset
Modified: branches/safari-608-branch/Source/WebCore/editing/Editing.cpp (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/editing/Editing.cpp 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/editing/Editing.cpp 2019-09-10 03:20:02 UTC (rev 249698)
@@ -27,6 +27,7 @@
#include "Editing.h"
#include "AXObjectCache.h"
+#include "CachedImage.h"
#include "Document.h"
#include "Editor.h"
#include "Frame.h"
@@ -34,6 +35,7 @@
#include "HTMLDListElement.h"
#include "HTMLDivElement.h"
#include "HTMLElementFactory.h"
+#include "HTMLImageElement.h"
#include "HTMLInterchange.h"
#include "HTMLLIElement.h"
#include "HTMLNames.h"
@@ -1302,4 +1304,19 @@
return rendererForCaretPainting->localToAbsoluteQuad(FloatRect(localRect), UseTransforms, insideFixed).enclosingBoundingBox();
}
+HashSet<RefPtr<HTMLImageElement>> visibleImageElementsInRangeWithNonLoadedImages(const Range& range)
+{
+ HashSet<RefPtr<HTMLImageElement>> result;
+ for (TextIterator iterator(&range); !iterator.atEnd(); iterator.advance()) {
+ if (!is<HTMLImageElement>(iterator.node()))
+ continue;
+
+ auto& imageElement = downcast<HTMLImageElement>(*iterator.node());
+ auto* cachedImage = imageElement.cachedImage();
+ if (cachedImage && cachedImage->isLoading())
+ result.add(&imageElement);
+ }
+ return result;
+}
+
} // namespace WebCore
Modified: branches/safari-608-branch/Source/WebCore/editing/Editing.h (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/editing/Editing.h 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/editing/Editing.h 2019-09-10 03:20:02 UTC (rev 249698)
@@ -27,6 +27,7 @@
#include "Position.h"
#include <wtf/Forward.h>
+#include <wtf/HashSet.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
@@ -33,6 +34,7 @@
class Document;
class HTMLElement;
+class HTMLImageElement;
class HTMLSpanElement;
class HTMLTextFormControlElement;
class RenderBlock;
@@ -102,6 +104,8 @@
bool positionBeforeOrAfterNodeIsCandidate(Node&);
+WEBCORE_EXPORT HashSet<RefPtr<HTMLImageElement>> visibleImageElementsInRangeWithNonLoadedImages(const Range&);
+
// -------------------------------------------------------------------------
// Position
// -------------------------------------------------------------------------
Modified: branches/safari-608-branch/Source/WebCore/editing/Editor.cpp (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/editing/Editor.cpp 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/editing/Editor.cpp 2019-09-10 03:20:02 UTC (rev 249698)
@@ -86,6 +86,7 @@
#include "Range.h"
#include "RemoveFormatCommand.h"
#include "RenderBlock.h"
+#include "RenderLayer.h"
#include "RenderTextControl.h"
#include "RenderedDocumentMarker.h"
#include "RenderedPosition.h"
@@ -674,8 +675,14 @@
auto command = ReplaceSelectionCommand::create(document(), &fragment, options, editingAction);
command->apply();
- revealSelectionAfterEditingOperation();
+ m_imageElementsToLoadBeforeRevealingSelection.clear();
+ if (auto insertionRange = command->insertedContentRange())
+ m_imageElementsToLoadBeforeRevealingSelection = visibleImageElementsInRangeWithNonLoadedImages(*insertionRange);
+
+ if (m_imageElementsToLoadBeforeRevealingSelection.isEmpty())
+ revealSelectionAfterEditingOperation();
+
selection = m_frame.selection().selection();
if (selection.isInPasswordField())
return;
@@ -1571,6 +1578,44 @@
#endif
+void Editor::revealSelectionIfNeededAfterLoadingImageForElement(HTMLImageElement& element)
+{
+ if (m_imageElementsToLoadBeforeRevealingSelection.isEmpty())
+ return;
+
+ if (!m_imageElementsToLoadBeforeRevealingSelection.remove(&element))
+ return;
+
+ if (!m_imageElementsToLoadBeforeRevealingSelection.isEmpty())
+ return;
+
+ // FIXME: This should be queued as a task for the next rendering update.
+ document().updateLayout();
+ revealSelectionAfterEditingOperation();
+}
+
+void Editor::renderLayerDidScroll(const RenderLayer& layer)
+{
+ if (m_imageElementsToLoadBeforeRevealingSelection.isEmpty())
+ return;
+
+ auto startContainer = makeRefPtr(m_frame.selection().selection().start().containerNode());
+ if (!startContainer)
+ return;
+
+ auto* startContainerRenderer = startContainer->renderer();
+ if (!startContainerRenderer)
+ return;
+
+ // FIXME: Ideally, this would also cancel deferred selection revealing if the selection is inside a subframe and a parent frame is scrolled.
+ for (auto* enclosingLayer = startContainerRenderer->enclosingLayer(); enclosingLayer; enclosingLayer = enclosingLayer->parent()) {
+ if (enclosingLayer == &layer) {
+ m_imageElementsToLoadBeforeRevealingSelection.clear();
+ break;
+ }
+ }
+}
+
bool Editor::isContinuousSpellCheckingEnabled() const
{
return client() && client()->isContinuousSpellCheckingEnabled();
@@ -3575,6 +3620,7 @@
#endif
setStartNewKillRingSequence(true);
+ m_imageElementsToLoadBeforeRevealingSelection.clear();
if (m_editorUIUpdateTimer.isActive())
return;
Modified: branches/safari-608-branch/Source/WebCore/editing/Editor.h (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/editing/Editor.h 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/editing/Editor.h 2019-09-10 03:20:02 UTC (rev 249698)
@@ -72,6 +72,7 @@
class KillRing;
class Pasteboard;
class PasteboardWriterData;
+class RenderLayer;
class SharedBuffer;
class Font;
class SpellCheckRequest;
@@ -183,6 +184,9 @@
WEBCORE_EXPORT void copyImage(const HitTestResult&);
#endif
+ void renderLayerDidScroll(const RenderLayer&);
+ void revealSelectionIfNeededAfterLoadingImageForElement(HTMLImageElement&);
+
String readPlainTextFromPasteboard(Pasteboard&);
WEBCORE_EXPORT void indent();
@@ -628,6 +632,7 @@
#endif
bool m_isGettingDictionaryPopupInfo { false };
+ HashSet<RefPtr<HTMLImageElement>> m_imageElementsToLoadBeforeRevealingSelection;
};
inline void Editor::setStartNewKillRingSequence(bool flag)
Modified: branches/safari-608-branch/Source/WebCore/editing/ReplaceSelectionCommand.cpp (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/editing/ReplaceSelectionCommand.cpp 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/editing/ReplaceSelectionCommand.cpp 2019-09-10 03:20:02 UTC (rev 249698)
@@ -1675,4 +1675,12 @@
return true;
}
+RefPtr<Range> ReplaceSelectionCommand::insertedContentRange() const
+{
+ if (auto document = makeRefPtr(m_startOfInsertedContent.document()))
+ return Range::create(*document, m_startOfInsertedContent, m_endOfInsertedContent);
+
+ return nullptr;
+}
+
} // namespace WebCore
Modified: branches/safari-608-branch/Source/WebCore/editing/ReplaceSelectionCommand.h (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/editing/ReplaceSelectionCommand.h 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/editing/ReplaceSelectionCommand.h 2019-09-10 03:20:02 UTC (rev 249698)
@@ -31,6 +31,7 @@
namespace WebCore {
class DocumentFragment;
+class Range;
class ReplacementFragment;
class ReplaceSelectionCommand : public CompositeEditCommand {
@@ -52,6 +53,8 @@
VisibleSelection visibleSelectionForInsertedText() const { return m_visibleSelectionForInsertedText; }
+ RefPtr<Range> insertedContentRange() const;
+
private:
ReplaceSelectionCommand(Document&, RefPtr<DocumentFragment>&&, OptionSet<CommandOption>, EditAction);
Modified: branches/safari-608-branch/Source/WebCore/page/FrameView.cpp (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/page/FrameView.cpp 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/page/FrameView.cpp 2019-09-10 03:20:02 UTC (rev 249698)
@@ -40,6 +40,7 @@
#include "DeprecatedGlobalSettings.h"
#include "DocumentLoader.h"
#include "DocumentMarkerController.h"
+#include "Editor.h"
#include "EventHandler.h"
#include "EventNames.h"
#include "FloatRect.h"
@@ -2457,6 +2458,11 @@
LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " scrollPositionChanged from " << oldPosition << " to " << newPosition << " (scale " << frameScaleFactor() << " )");
updateLayoutViewport();
viewportContentsChanged();
+
+ if (auto* renderView = this->renderView()) {
+ if (auto* layer = renderView->layer())
+ frame().editor().renderLayerDidScroll(*layer);
+ }
}
void FrameView::applyRecursivelyWithVisibleRect(const WTF::Function<void (FrameView& frameView, const IntRect& visibleRect)>& apply)
Modified: branches/safari-608-branch/Source/WebCore/page/FrameView.h (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/page/FrameView.h 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/page/FrameView.h 2019-09-10 03:20:02 UTC (rev 249698)
@@ -664,6 +664,8 @@
GraphicsLayer* layerForHorizontalScrollbar() const final;
GraphicsLayer* layerForVerticalScrollbar() const final;
+ void renderLayerDidScroll(const RenderLayer&);
+
protected:
bool scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) final;
void scrollContentsSlowPath(const IntRect& updateRect) final;
Modified: branches/safari-608-branch/Source/WebCore/page/Page.cpp (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/page/Page.cpp 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/page/Page.cpp 2019-09-10 03:20:02 UTC (rev 249698)
@@ -3002,6 +3002,9 @@
void Page::didFinishLoadingImageForElement(HTMLImageElement& element)
{
+ auto protectedElement = makeRef(element);
+ if (auto frame = makeRefPtr(element.document().frame()))
+ frame->editor().revealSelectionIfNeededAfterLoadingImageForElement(element);
chrome().client().didFinishLoadingImageForElement(element);
}
Modified: branches/safari-608-branch/Source/WebCore/rendering/RenderLayer.cpp (249697 => 249698)
--- branches/safari-608-branch/Source/WebCore/rendering/RenderLayer.cpp 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebCore/rendering/RenderLayer.cpp 2019-09-10 03:20:02 UTC (rev 249698)
@@ -55,6 +55,7 @@
#include "DocumentEventQueue.h"
#include "DocumentMarkerController.h"
#include "DocumentTimeline.h"
+#include "Editor.h"
#include "Element.h"
#include "EventHandler.h"
#include "FEColorMatrix.h"
@@ -2570,6 +2571,7 @@
view.frameView().didChangeScrollOffset();
view.frameView().viewportContentsChanged();
+ frame.editor().renderLayerDidScroll(*this);
}
static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView& frameView)
Modified: branches/safari-608-branch/Source/WebKit/ChangeLog (249697 => 249698)
--- branches/safari-608-branch/Source/WebKit/ChangeLog 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebKit/ChangeLog 2019-09-10 03:20:02 UTC (rev 249698)
@@ -1,5 +1,124 @@
2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+ Cherry-pick r249605. rdar://problem/55182896
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore:
+
+ Editor::replaceSelectionWithFragment currently scrolls to reveal the selection after inserting the given
+ DocumentFragment. However, this scrolling occurs before any inserted images have loaded yet, which causes the
+ wrong caret rect to be revealed, since all image elements inserted during paste will be empty.
+
+ To fix this, we defer revealing the selection after inserting the fragment until after all images that have
+ been inserted are done loading. While waiting for images to load, if any layers which may be scrolled as a
+ result of revealing the selection are scrolled, we additionally cancel the deferred selection reveal. See
+ comments below for more detail.
+
+ Tests: editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html
+ editing/pasteboard/reveal-selection-after-pasting-images.html
+ PasteImage.RevealSelectionAfterPastingImage
+
+ * editing/Editing.cpp:
+ (WebCore::visibleImageElementsInRangeWithNonLoadedImages):
+
+ Add a new helper to iterate through a range and collect all image elements in that range, that contain cached
+ images that have not finished loading yet.
+
+ * editing/Editing.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::replaceSelectionWithFragment):
+
+ Instead of always immediately revealing the selection after applying the ReplaceSelectionCommand, collect the
+ image elements that were just inserted, and avoid immediately revealing the selection if any of these images
+ have non-null cached images, but are not loaded yet. Instead, hold on to these images in a set, remove them once
+ they finish loading using the new method below, and once all images are removed, reveal the selection.
+
+ (WebCore::Editor::revealSelectionIfNeededAfterLoadingImageForElement):
+ (WebCore::Editor::renderLayerDidScroll):
+
+ Called whenever a scrollable RenderLayer is scrolled (or in the case of FrameView, the root layer). In the case
+ where Editor is waiting to reveal the selection, we check to see if the scrolled layer is an ancestor of the
+ layer enclosing the start of the selection.
+
+ (WebCore::Editor::respondToChangedSelection):
+
+ If the selection changes between pasting and waiting for pasted images to load, just cancel waiting to reveal
+ the selection after pasting.
+
+ * editing/Editor.h:
+ * editing/ReplaceSelectionCommand.cpp:
+ (WebCore::ReplaceSelectionCommand::insertedContentRange const):
+
+ Add a helper method to grab the Range of content inserted after applying the command.
+
+ * editing/ReplaceSelectionCommand.h:
+ * page/FrameView.cpp:
+ (WebCore::FrameView::scrollPositionChanged):
+ * page/FrameView.h:
+ * page/Page.cpp:
+ (WebCore::Page::didFinishLoadingImageForElement):
+
+ Notify Editor after an image finishes loading.
+
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::scrollTo):
+
+ Source/WebKit:
+
+ Tweak some existing logic to use the new visibleImageElementsInRangeWithNonLoadedImages helper function. See
+ WebCore for more details.
+
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::didConcludeEditDrag):
+
+ Tools:
+
+ Add an API test to exercise the scenario where we scroll to reveal the selection after pasting an image that was
+ directly written to the pasteboard.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
+
+ LayoutTests:
+
+ Add a couple of new layout tests.
+
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt: Added.
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html: Added.
+
+ This test verifies that we don't try to scroll to reveal the caret after pasting, if the scroll position was
+ changed before the images finished loading.
+
+ * editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+ * editing/pasteboard/reveal-selection-after-pasting-images.html: Added.
+ * platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+
+ This test verifies that we reveal the caret after loading multiple pasted images in a selection, and dispatch a
+ scroll event in the process.
+
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249605 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2019-09-06 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Tweak some existing logic to use the new visibleImageElementsInRangeWithNonLoadedImages helper function. See
+ WebCore for more details.
+
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::didConcludeEditDrag):
+
+2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+
Cherry-pick r249584. rdar://problem/55202935
Marking up a note on iOS results in a PDF with no contents
Modified: branches/safari-608-branch/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (249697 => 249698)
--- branches/safari-608-branch/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-09-10 03:20:02 UTC (rev 249698)
@@ -910,21 +910,9 @@
m_pendingImageElementsForDropSnapshot.clear();
- bool waitingForAnyImageToLoad = false;
auto frame = makeRef(m_page->focusController().focusedOrMainFrame());
if (auto selectionRange = frame->selection().selection().toNormalizedRange()) {
- for (TextIterator iterator(selectionRange.get()); !iterator.atEnd(); iterator.advance()) {
- auto* node = iterator.node();
- if (!is<HTMLImageElement>(node))
- continue;
-
- auto& imageElement = downcast<HTMLImageElement>(*node);
- auto* cachedImage = imageElement.cachedImage();
- if (cachedImage && cachedImage->image() && cachedImage->image()->isNull()) {
- m_pendingImageElementsForDropSnapshot.add(&imageElement);
- waitingForAnyImageToLoad = true;
- }
- }
+ m_pendingImageElementsForDropSnapshot = visibleImageElementsInRangeWithNonLoadedImages(*selectionRange);
auto collapsedRange = Range::create(selectionRange->ownerDocument(), selectionRange->endPosition(), selectionRange->endPosition());
frame->selection().setSelectedRange(collapsedRange.ptr(), DOWNSTREAM, FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
@@ -931,7 +919,7 @@
m_rangeForDropSnapshot = WTFMove(selectionRange);
}
- if (!waitingForAnyImageToLoad)
+ if (m_pendingImageElementsForDropSnapshot.isEmpty())
computeAndSendEditDragSnapshot();
}
Modified: branches/safari-608-branch/Tools/ChangeLog (249697 => 249698)
--- branches/safari-608-branch/Tools/ChangeLog 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Tools/ChangeLog 2019-09-10 03:20:02 UTC (rev 249698)
@@ -1,5 +1,123 @@
2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+ Cherry-pick r249605. rdar://problem/55182896
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore:
+
+ Editor::replaceSelectionWithFragment currently scrolls to reveal the selection after inserting the given
+ DocumentFragment. However, this scrolling occurs before any inserted images have loaded yet, which causes the
+ wrong caret rect to be revealed, since all image elements inserted during paste will be empty.
+
+ To fix this, we defer revealing the selection after inserting the fragment until after all images that have
+ been inserted are done loading. While waiting for images to load, if any layers which may be scrolled as a
+ result of revealing the selection are scrolled, we additionally cancel the deferred selection reveal. See
+ comments below for more detail.
+
+ Tests: editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html
+ editing/pasteboard/reveal-selection-after-pasting-images.html
+ PasteImage.RevealSelectionAfterPastingImage
+
+ * editing/Editing.cpp:
+ (WebCore::visibleImageElementsInRangeWithNonLoadedImages):
+
+ Add a new helper to iterate through a range and collect all image elements in that range, that contain cached
+ images that have not finished loading yet.
+
+ * editing/Editing.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::replaceSelectionWithFragment):
+
+ Instead of always immediately revealing the selection after applying the ReplaceSelectionCommand, collect the
+ image elements that were just inserted, and avoid immediately revealing the selection if any of these images
+ have non-null cached images, but are not loaded yet. Instead, hold on to these images in a set, remove them once
+ they finish loading using the new method below, and once all images are removed, reveal the selection.
+
+ (WebCore::Editor::revealSelectionIfNeededAfterLoadingImageForElement):
+ (WebCore::Editor::renderLayerDidScroll):
+
+ Called whenever a scrollable RenderLayer is scrolled (or in the case of FrameView, the root layer). In the case
+ where Editor is waiting to reveal the selection, we check to see if the scrolled layer is an ancestor of the
+ layer enclosing the start of the selection.
+
+ (WebCore::Editor::respondToChangedSelection):
+
+ If the selection changes between pasting and waiting for pasted images to load, just cancel waiting to reveal
+ the selection after pasting.
+
+ * editing/Editor.h:
+ * editing/ReplaceSelectionCommand.cpp:
+ (WebCore::ReplaceSelectionCommand::insertedContentRange const):
+
+ Add a helper method to grab the Range of content inserted after applying the command.
+
+ * editing/ReplaceSelectionCommand.h:
+ * page/FrameView.cpp:
+ (WebCore::FrameView::scrollPositionChanged):
+ * page/FrameView.h:
+ * page/Page.cpp:
+ (WebCore::Page::didFinishLoadingImageForElement):
+
+ Notify Editor after an image finishes loading.
+
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::scrollTo):
+
+ Source/WebKit:
+
+ Tweak some existing logic to use the new visibleImageElementsInRangeWithNonLoadedImages helper function. See
+ WebCore for more details.
+
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::didConcludeEditDrag):
+
+ Tools:
+
+ Add an API test to exercise the scenario where we scroll to reveal the selection after pasting an image that was
+ directly written to the pasteboard.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
+
+ LayoutTests:
+
+ Add a couple of new layout tests.
+
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll-expected.txt: Added.
+ * editing/pasteboard/do-not-reveal-selection-after-programmatic-scroll.html: Added.
+
+ This test verifies that we don't try to scroll to reveal the caret after pasting, if the scroll position was
+ changed before the images finished loading.
+
+ * editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+ * editing/pasteboard/reveal-selection-after-pasting-images.html: Added.
+ * platform/ios/editing/pasteboard/reveal-selection-after-pasting-images-expected.txt: Added.
+
+ This test verifies that we reveal the caret after loading multiple pasted images in a selection, and dispatch a
+ scroll event in the process.
+
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249605 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2019-09-06 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Incorrect selection rect revealed after pasting images in a contenteditable element
+ https://bugs.webkit.org/show_bug.cgi?id=201549
+ <rdar://problem/50956429>
+
+ Reviewed by Simon Fraser.
+
+ Add an API test to exercise the scenario where we scroll to reveal the selection after pasting an image that was
+ directly written to the pasteboard.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
+
+2019-09-09 Kocsen Chung <kocsen_ch...@apple.com>
+
Cherry-pick r249584. rdar://problem/55202935
Marking up a note on iOS results in a PDF with no contents
Modified: branches/safari-608-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm (249697 => 249698)
--- branches/safari-608-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm 2019-09-10 03:19:56 UTC (rev 249697)
+++ branches/safari-608-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm 2019-09-10 03:20:02 UTC (rev 249698)
@@ -33,7 +33,7 @@
#import <wtf/text/WTFString.h>
#if PLATFORM(IOS_FAMILY)
-#include <MobileCoreServices/MobileCoreServices.h>
+#import <MobileCoreServices/MobileCoreServices.h>
#endif
#if PLATFORM(MAC)
@@ -137,6 +137,21 @@
EXPECT_WK_STREQ("200", [webView stringByEvaluatingJavaScript:@"imageElement.width"]);
}
+TEST(PasteImage, RevealSelectionAfterPastingImage)
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
+ [webView synchronouslyLoadHTMLString:@"<meta name='viewport' content='width=device-width, initial-scale=1'><body contenteditable>Hello world</body>"];
+ [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
+ [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello world"];
+ [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
+
+ writeImageDataToPasteboard((__bridge NSString *)kUTTypeJPEG, [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sunset-in-cupertino-600px" ofType:@"jpg" inDirectory:@"TestWebKitAPI.resources"]]);
+ [webView paste:nil];
+
+ while ([[webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop"] doubleValue] <= 0)
+ [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
+}
+
#if PLATFORM(MAC)
void writeBundleFileToPasteboard(id object)
{