fed6c04bec8c5da26e6519356c6200e23bee0350
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / ios / WebPageIOS.mm
1 /*
2  * Copyright (C) 2012-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebPage.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "AccessibilityIOS.h"
32 #import "DataReference.h"
33 #import "DocumentEditingContext.h"
34 #import "DrawingArea.h"
35 #import "EditingRange.h"
36 #import "EditorState.h"
37 #import "InteractionInformationAtPosition.h"
38 #import "Logging.h"
39 #import "NativeWebKeyboardEvent.h"
40 #import "PluginView.h"
41 #import "PrintInfo.h"
42 #import "RemoteLayerTreeDrawingArea.h"
43 #import "SandboxUtilities.h"
44 #import "SharedMemory.h"
45 #import "SyntheticEditingCommandType.h"
46 #import "TextCheckingControllerProxy.h"
47 #import "UIKitSPI.h"
48 #import "UserData.h"
49 #import "ViewGestureGeometryCollector.h"
50 #import "VisibleContentRectUpdateInfo.h"
51 #import "WKAccessibilityWebPageObjectIOS.h"
52 #import "WebAutocorrectionContext.h"
53 #import "WebAutocorrectionData.h"
54 #import "WebChromeClient.h"
55 #import "WebCoreArgumentCoders.h"
56 #import "WebFrame.h"
57 #import "WebImage.h"
58 #import "WebPageMessages.h"
59 #import "WebPageProxyMessages.h"
60 #import "WebPreviewLoaderClient.h"
61 #import "WebProcess.h"
62 #import <CoreText/CTFont.h>
63 #import <WebCore/Autofill.h>
64 #import <WebCore/AutofillElements.h>
65 #import <WebCore/Chrome.h>
66 #import <WebCore/ContentChangeObserver.h>
67 #import <WebCore/DOMTimerHoldingTank.h>
68 #import <WebCore/DataDetection.h>
69 #import <WebCore/DiagnosticLoggingClient.h>
70 #import <WebCore/DiagnosticLoggingKeys.h>
71 #import <WebCore/DocumentLoader.h>
72 #import <WebCore/DragController.h>
73 #import <WebCore/Editing.h>
74 #import <WebCore/Editor.h>
75 #import <WebCore/EditorClient.h>
76 #import <WebCore/Element.h>
77 #import <WebCore/ElementAncestorIterator.h>
78 #import <WebCore/EventHandler.h>
79 #import <WebCore/File.h>
80 #import <WebCore/FloatQuad.h>
81 #import <WebCore/FocusController.h>
82 #import <WebCore/Frame.h>
83 #import <WebCore/FrameLoaderClient.h>
84 #import <WebCore/FrameView.h>
85 #import <WebCore/GeometryUtilities.h>
86 #import <WebCore/HTMLAreaElement.h>
87 #import <WebCore/HTMLAttachmentElement.h>
88 #import <WebCore/HTMLBodyElement.h>
89 #import <WebCore/HTMLElement.h>
90 #import <WebCore/HTMLElementTypeHelpers.h>
91 #import <WebCore/HTMLFormElement.h>
92 #import <WebCore/HTMLIFrameElement.h>
93 #import <WebCore/HTMLImageElement.h>
94 #import <WebCore/HTMLInputElement.h>
95 #import <WebCore/HTMLLabelElement.h>
96 #import <WebCore/HTMLOptGroupElement.h>
97 #import <WebCore/HTMLOptionElement.h>
98 #import <WebCore/HTMLParserIdioms.h>
99 #import <WebCore/HTMLSelectElement.h>
100 #import <WebCore/HTMLSummaryElement.h>
101 #import <WebCore/HTMLTextAreaElement.h>
102 #import <WebCore/HTMLTextFormControlElement.h>
103 #import <WebCore/HistoryItem.h>
104 #import <WebCore/HitTestResult.h>
105 #import <WebCore/InputMode.h>
106 #import <WebCore/KeyboardEvent.h>
107 #import <WebCore/LibWebRTCProvider.h>
108 #import <WebCore/MediaSessionManagerIOS.h>
109 #import <WebCore/Node.h>
110 #import <WebCore/NodeList.h>
111 #import <WebCore/NotImplemented.h>
112 #import <WebCore/Page.h>
113 #import <WebCore/Pasteboard.h>
114 #import <WebCore/PlatformKeyboardEvent.h>
115 #import <WebCore/PlatformMouseEvent.h>
116 #import <WebCore/PointerCaptureController.h>
117 #import <WebCore/Quirks.h>
118 #import <WebCore/RenderBlock.h>
119 #import <WebCore/RenderImage.h>
120 #import <WebCore/RenderLayer.h>
121 #import <WebCore/RenderThemeIOS.h>
122 #import <WebCore/RenderView.h>
123 #import <WebCore/RuntimeApplicationChecks.h>
124 #import <WebCore/Settings.h>
125 #import <WebCore/ShadowRoot.h>
126 #import <WebCore/SharedBuffer.h>
127 #import <WebCore/SimpleRange.h>
128 #import <WebCore/StyleProperties.h>
129 #import <WebCore/TextIndicator.h>
130 #import <WebCore/TextIterator.h>
131 #import <WebCore/TextPlaceholderElement.h>
132 #import <WebCore/UserAgent.h>
133 #import <WebCore/VisibleUnits.h>
134 #import <WebCore/WebEvent.h>
135 #import <wtf/MathExtras.h>
136 #import <wtf/MemoryPressureHandler.h>
137 #import <wtf/SetForScope.h>
138 #import <wtf/SoftLinking.h>
139 #import <wtf/cocoa/Entitlements.h>
140 #import <wtf/text/TextStream.h>
141
142 #define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), channel, "%p - WebPage::" fmt, this, ##__VA_ARGS__)
143 #define RELEASE_LOG_ERROR_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), channel, "%p - WebPage::" fmt, this, ##__VA_ARGS__)
144
145 namespace WebKit {
146 using namespace WebCore;
147
148 void WebPage::platformInitialize()
149 {
150     platformInitializeAccessibility();
151 }
152
153 void WebPage::platformDetach()
154 {
155     [m_mockAccessibilityElement setWebPage:nullptr];
156 }
157     
158 void WebPage::platformInitializeAccessibility()
159 {
160     m_mockAccessibilityElement = adoptNS([[WKAccessibilityWebPageObject alloc] init]);
161     [m_mockAccessibilityElement setWebPage:this];
162
163     accessibilityTransferRemoteToken(accessibilityRemoteTokenData());
164 }
165
166 void WebPage::platformReinitialize()
167 {
168     accessibilityTransferRemoteToken(accessibilityRemoteTokenData());
169 }
170
171 RetainPtr<NSData> WebPage::accessibilityRemoteTokenData() const
172 {
173     return newAccessibilityRemoteToken([NSUUID UUID]);
174 }
175
176 static void computeEditableRootHasContentAndPlainText(const VisibleSelection& selection, EditorState::PostLayoutData& data)
177 {
178     data.hasContent = false;
179     data.hasPlainText = false;
180     if (!selection.isContentEditable())
181         return;
182
183     if (data.selectedTextLength || data.characterAfterSelection || data.characterBeforeSelection || data.twoCharacterBeforeSelection) {
184         // If any of these variables have been previously set, the editable root must have plain text content, so we can bail from the remainder of the check.
185         data.hasContent = true;
186         data.hasPlainText = true;
187         return;
188     }
189
190     auto* root = selection.rootEditableElement();
191     if (!root)
192         return;
193
194     auto startInEditableRoot = firstPositionInNode(root);
195     data.hasContent = root->hasChildNodes() && !isEndOfEditableOrNonEditableContent(startInEditableRoot);
196     data.hasPlainText = data.hasContent && hasAnyPlainText(Range::create(root->document(), VisiblePosition { startInEditableRoot }, VisiblePosition { lastPositionInNode(root) }));
197 }
198
199 bool WebPage::isTransparentOrFullyClipped(const Element& element) const
200 {
201     auto* renderer = element.renderer();
202     if (!renderer)
203         return false;
204
205     auto* enclosingLayer = renderer->enclosingLayer();
206     if (enclosingLayer && enclosingLayer->isTransparentRespectingParentFrames())
207         return true;
208
209     return renderer->hasNonEmptyVisibleRectRespectingParentFrames();
210 }
211
212 void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
213 {
214     FrameView* view = frame.view();
215     if (!view) {
216         result.isMissingPostLayoutData = true;
217         return;
218     }
219
220     if (frame.editor().hasComposition()) {
221         RefPtr<Range> compositionRange = frame.editor().compositionRange();
222         Vector<WebCore::SelectionRect> compositionRects;
223         if (compositionRange) {
224             compositionRange->collectSelectionRects(compositionRects);
225             if (compositionRects.size())
226                 result.firstMarkedRect = view->contentsToRootView(compositionRects[0].rect());
227             if (compositionRects.size() > 1)
228                 result.lastMarkedRect = view->contentsToRootView(compositionRects.last().rect());
229             else
230                 result.lastMarkedRect = result.firstMarkedRect;
231             result.markedText = plainTextReplacingNoBreakSpace(compositionRange.get());
232         }
233     }
234
235     // We only set the remaining EditorState entries if layout is done as a performance optimization
236     // to avoid the need to force a synchronous layout here to compute these entries. If we
237     // have a composition or are using a hardware keyboard then we send the full editor state
238     // immediately so that the UIProcess can update UI, including the position of the caret.
239     bool needsLayout = view->needsLayout();
240     bool requiresPostLayoutData = frame.editor().hasComposition();
241 #if !PLATFORM(MACCATALYST)
242     requiresPostLayoutData |= m_keyboardIsAttached;
243 #endif
244     if ((shouldIncludePostLayoutData == IncludePostLayoutDataHint::No || needsLayout) && !requiresPostLayoutData) {
245         result.isMissingPostLayoutData = true;
246         return;
247     }
248
249     auto& postLayoutData = result.postLayoutData();
250     
251     const VisibleSelection& selection = frame.selection().selection();
252     postLayoutData.isStableStateUpdate = m_isInStableState;
253     bool startNodeIsInsideFixedPosition = false;
254     bool endNodeIsInsideFixedPosition = false;
255     if (selection.isCaret()) {
256         postLayoutData.caretRectAtStart = view->contentsToRootView(frame.selection().absoluteCaretBounds(&startNodeIsInsideFixedPosition));
257         endNodeIsInsideFixedPosition = startNodeIsInsideFixedPosition;
258         postLayoutData.caretRectAtEnd = postLayoutData.caretRectAtStart;
259         // FIXME: The following check should take into account writing direction.
260         postLayoutData.isReplaceAllowed = result.isContentEditable && atBoundaryOfGranularity(selection.start(), WordGranularity, DirectionForward);
261         postLayoutData.wordAtSelection = plainTextReplacingNoBreakSpace(wordRangeFromPosition(selection.start()).get());
262         if (selection.isContentEditable())
263             charactersAroundPosition(selection.start(), postLayoutData.characterAfterSelection, postLayoutData.characterBeforeSelection, postLayoutData.twoCharacterBeforeSelection);
264     } else if (selection.isRange()) {
265         postLayoutData.caretRectAtStart = view->contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds(&startNodeIsInsideFixedPosition));
266         postLayoutData.caretRectAtEnd = view->contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds(&endNodeIsInsideFixedPosition));
267         RefPtr<Range> selectedRange = selection.toNormalizedRange();
268         String selectedText;
269         if (selectedRange) {
270             selectedRange->collectSelectionRects(postLayoutData.selectionRects);
271             convertSelectionRectsToRootView(view, postLayoutData.selectionRects);
272             selectedText = plainTextReplacingNoBreakSpace(selectedRange.get(), TextIteratorDefaultBehavior, true);
273             postLayoutData.selectedTextLength = selectedText.length();
274             const int maxSelectedTextLength = 200;
275             postLayoutData.wordAtSelection = selectedText.left(maxSelectedTextLength);
276         }
277         // FIXME: We should disallow replace when the string contains only CJ characters.
278         postLayoutData.isReplaceAllowed = result.isContentEditable && !result.isInPasswordField && !selectedText.isAllSpecialCharacters<isHTMLSpace>();
279     }
280     postLayoutData.atStartOfSentence = frame.selection().selectionAtSentenceStart();
281     postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
282     if (!selection.isNone()) {
283         if (m_focusedElement && m_focusedElement->renderer()) {
284             auto& renderer = *m_focusedElement->renderer();
285             postLayoutData.focusedElementRect = rootViewInteractionBoundsForElement(*m_focusedElement);
286             postLayoutData.caretColor = CaretBase::computeCaretColor(renderer.style(), renderer.element());
287         }
288         if (result.isContentEditable) {
289             if (auto editableRootOrFormControl = makeRefPtr(selection.rootEditableElement())) {
290                 if (is<HTMLTextFormControlElement>(editableRootOrFormControl->shadowHost()))
291                     editableRootOrFormControl = editableRootOrFormControl->shadowHost();
292                 postLayoutData.editableRootIsTransparentOrFullyClipped = isTransparentOrFullyClipped(*editableRootOrFormControl);
293             }
294         }
295         computeEditableRootHasContentAndPlainText(selection, postLayoutData);
296         postLayoutData.selectionStartIsAtParagraphBoundary = atBoundaryOfGranularity(selection.visibleStart(), TextGranularity::ParagraphGranularity, SelectionDirection::DirectionBackward);
297         postLayoutData.selectionEndIsAtParagraphBoundary = atBoundaryOfGranularity(selection.visibleEnd(), TextGranularity::ParagraphGranularity, SelectionDirection::DirectionForward);
298     }
299 }
300
301 void WebPage::platformWillPerformEditingCommand()
302 {
303     auto& frame = m_page->focusController().focusedOrMainFrame();
304     if (auto* document = frame.document()) {
305         if (auto* holdingTank = document->domTimerHoldingTankIfExists())
306             holdingTank->removeAll();
307     }
308 }
309
310 FloatSize WebPage::screenSize() const
311 {
312     return m_screenSize;
313 }
314
315 FloatSize WebPage::availableScreenSize() const
316 {
317     return m_availableScreenSize;
318 }
319
320 FloatSize WebPage::overrideScreenSize() const
321 {
322     return m_overrideScreenSize;
323 }
324
325 void WebPage::didReceiveMobileDocType(bool isMobileDoctype)
326 {
327     resetViewportDefaultConfiguration(m_mainFrame.get(), isMobileDoctype);
328 }
329
330 void WebPage::savePageState(HistoryItem& historyItem)
331 {
332     historyItem.setScaleIsInitial(!m_userHasChangedPageScaleFactor);
333     historyItem.setMinimumLayoutSizeInScrollViewCoordinates(m_viewportConfiguration.minimumLayoutSize());
334     historyItem.setContentSize(m_viewportConfiguration.contentsSize());
335 }
336
337 static double scaleAfterViewportWidthChange(double currentScale, bool userHasChangedPageScaleFactor, const ViewportConfiguration& viewportConfiguration, float unobscuredWidthInScrollViewCoordinates, const IntSize& newContentSize, const IntSize& oldContentSize, float visibleHorizontalFraction)
338 {
339     double scale;
340     if (!userHasChangedPageScaleFactor)
341         scale = viewportConfiguration.initialScale();
342     else
343         scale = std::max(std::min(currentScale, viewportConfiguration.maximumScale()), viewportConfiguration.minimumScale());
344
345     LOG(VisibleRects, "scaleAfterViewportWidthChange getting scale %.2f", scale);
346
347     if (userHasChangedPageScaleFactor) {
348         // When the content size changes, we keep the same relative horizontal content width in view, otherwise we would
349         // end up zoomed too far in landscape->portrait, and too close in portrait->landscape.
350         double widthToKeepInView = visibleHorizontalFraction * newContentSize.width();
351         double newScale = unobscuredWidthInScrollViewCoordinates / widthToKeepInView;
352         scale = std::max(std::min(newScale, viewportConfiguration.maximumScale()), viewportConfiguration.minimumScale());
353     }
354     return scale;
355 }
356
357 static FloatPoint relativeCenterAfterContentSizeChange(const FloatRect& originalContentRect, IntSize oldContentSize, IntSize newContentSize)
358 {
359     // If the content size has changed, keep the same relative position.
360     FloatPoint oldContentCenter = originalContentRect.center();
361     float relativeHorizontalPosition = oldContentCenter.x() / oldContentSize.width();
362     float relativeVerticalPosition =  oldContentCenter.y() / oldContentSize.height();
363     return FloatPoint(relativeHorizontalPosition * newContentSize.width(), relativeVerticalPosition * newContentSize.height());
364 }
365
366 static inline FloatRect adjustExposedRectForNewScale(const FloatRect& exposedRect, double exposedRectScale, double newScale)
367 {
368     if (exposedRectScale == newScale)
369         return exposedRect;
370
371     float horizontalChange = exposedRect.width() * exposedRectScale / newScale - exposedRect.width();
372     float verticalChange = exposedRect.height() * exposedRectScale / newScale - exposedRect.height();
373
374     auto adjustedRect = exposedRect;
375     adjustedRect.inflate({ horizontalChange / 2, verticalChange / 2 });
376     return adjustedRect;
377 }
378
379 void WebPage::restorePageState(const HistoryItem& historyItem)
380 {
381     // When a HistoryItem is cleared, its scale factor and scroll point are set to zero. We should not try to restore the other
382     // parameters in those conditions.
383     if (!historyItem.pageScaleFactor()) {
384         send(Messages::WebPageProxy::CouldNotRestorePageState());
385         return;
386     }
387
388     // We can restore the exposed rect and scale, but we cannot touch the scroll position since the obscured insets
389     // may be changing in the UIProcess. The UIProcess can update the position from the information we send and will then
390     // scroll to the correct position through a regular VisibleContentRectUpdate.
391
392     m_userHasChangedPageScaleFactor = !historyItem.scaleIsInitial();
393
394     FrameView& frameView = *m_page->mainFrame().view();
395
396     FloatSize currentMinimumLayoutSizeInScrollViewCoordinates = m_viewportConfiguration.minimumLayoutSize();
397     if (historyItem.minimumLayoutSizeInScrollViewCoordinates() == currentMinimumLayoutSizeInScrollViewCoordinates) {
398         float boundedScale = historyItem.scaleIsInitial() ? m_viewportConfiguration.initialScale() : historyItem.pageScaleFactor();
399         boundedScale = std::min<float>(m_viewportConfiguration.maximumScale(), std::max<float>(m_viewportConfiguration.minimumScale(), boundedScale));
400         scalePage(boundedScale, IntPoint());
401
402         Optional<FloatPoint> scrollPosition;
403         if (historyItem.shouldRestoreScrollPosition()) {
404             m_drawingArea->setExposedContentRect(historyItem.exposedContentRect());
405             m_hasRestoredExposedContentRectAfterDidCommitLoad = true;
406             scrollPosition = FloatPoint(historyItem.scrollPosition());
407         }
408         send(Messages::WebPageProxy::RestorePageState(scrollPosition, frameView.scrollOrigin(), historyItem.obscuredInsets(), boundedScale));
409     } else {
410         IntSize oldContentSize = historyItem.contentSize();
411         IntSize newContentSize = frameView.contentsSize();
412         double visibleHorizontalFraction = static_cast<float>(historyItem.unobscuredContentRect().width()) / oldContentSize.width();
413
414         double newScale = scaleAfterViewportWidthChange(historyItem.pageScaleFactor(), !historyItem.scaleIsInitial(), m_viewportConfiguration, currentMinimumLayoutSizeInScrollViewCoordinates.width(), newContentSize, oldContentSize, visibleHorizontalFraction);
415
416         Optional<FloatPoint> newCenter;
417         if (historyItem.shouldRestoreScrollPosition()) {
418             if (!oldContentSize.isEmpty() && !newContentSize.isEmpty() && newContentSize != oldContentSize)
419                 newCenter = relativeCenterAfterContentSizeChange(historyItem.unobscuredContentRect(), oldContentSize, newContentSize);
420             else
421                 newCenter = FloatRect(historyItem.unobscuredContentRect()).center();
422         }
423
424         scalePage(newScale, IntPoint());
425         send(Messages::WebPageProxy::RestorePageCenterAndScale(newCenter, newScale));
426     }
427 }
428
429 double WebPage::minimumPageScaleFactor() const
430 {
431     if (!m_viewportConfiguration.allowsUserScaling())
432         return m_page->pageScaleFactor();
433     return m_viewportConfiguration.minimumScale();
434 }
435
436 double WebPage::maximumPageScaleFactor() const
437 {
438     if (!m_viewportConfiguration.allowsUserScaling())
439         return m_page->pageScaleFactor();
440     return m_viewportConfiguration.maximumScale();
441 }
442
443 double WebPage::maximumPageScaleFactorIgnoringAlwaysScalable() const
444 {
445     if (!m_viewportConfiguration.allowsUserScalingIgnoringAlwaysScalable())
446         return m_page->pageScaleFactor();
447     return m_viewportConfiguration.maximumScaleIgnoringAlwaysScalable();
448 }
449
450 bool WebPage::allowsUserScaling() const
451 {
452     return m_viewportConfiguration.allowsUserScaling();
453 }
454
455 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent& event)
456 {
457     auto* platformEvent = event.underlyingPlatformEvent();
458     if (!platformEvent)
459         return false;
460     
461     // Don't send synthetic events to the UIProcess. They are only
462     // used for interacting with JavaScript.
463     if (platformEvent->isSyntheticEvent())
464         return false;
465
466     // FIXME: Interpret the event immediately upon receiving it in UI process, without sending to WebProcess first.
467     bool eventWasHandled = false;
468     bool sendResult = WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(editorState(), platformEvent->type() == PlatformKeyboardEvent::Char),
469         Messages::WebPageProxy::InterpretKeyEvent::Reply(eventWasHandled), m_identifier);
470     return sendResult && eventWasHandled;
471 }
472
473 bool WebPage::parentProcessHasServiceWorkerEntitlement() const
474 {
475     static bool hasEntitlement = WTF::hasEntitlement(WebProcess::singleton().parentProcessConnection()->xpcConnection(), "com.apple.developer.WebKit.ServiceWorkers");
476     return hasEntitlement;
477 }
478
479 void WebPage::sendComplexTextInputToPlugin(uint64_t, const String&)
480 {
481     notImplemented();
482 }
483
484 bool WebPage::performNonEditingBehaviorForSelector(const String&, WebCore::KeyboardEvent*)
485 {
486     notImplemented();
487     return false;
488 }
489
490 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
491 {
492     notImplemented();
493     return false;
494 }
495
496 void WebPage::getSelectionContext(CallbackID callbackID)
497 {
498     Frame& frame = m_page->focusController().focusedOrMainFrame();
499     if (!frame.selection().isRange()) {
500         send(Messages::WebPageProxy::SelectionContextCallback(String(), String(), String(), callbackID));
501         return;
502     }
503     const int selectionExtendedContextLength = 350;
504     
505     String selectedText = plainTextReplacingNoBreakSpace(frame.selection().selection().toNormalizedRange().get());
506     String textBefore = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(frame.selection().selection().start(), selectionExtendedContextLength, DirectionBackward).get(), TextIteratorDefaultBehavior, true);
507     String textAfter = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(frame.selection().selection().end(), selectionExtendedContextLength, DirectionForward).get(), TextIteratorDefaultBehavior, true);
508
509     send(Messages::WebPageProxy::SelectionContextCallback(selectedText, textBefore, textAfter, callbackID));
510 }
511
512 NSObject *WebPage::accessibilityObjectForMainFramePlugin()
513 {
514     if (!m_page)
515         return nil;
516     
517     if (auto* pluginView = pluginViewForFrame(&m_page->mainFrame()))
518         return pluginView->accessibilityObject();
519     
520     return nil;
521 }
522     
523 void WebPage::registerUIProcessAccessibilityTokens(const IPC::DataReference& elementToken, const IPC::DataReference&)
524 {
525     NSData *elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
526     [m_mockAccessibilityElement setRemoteTokenData:elementTokenData];
527 }
528
529 void WebPage::readSelectionFromPasteboard(const String&, CompletionHandler<void(bool&&)>&& completionHandler)
530 {
531     notImplemented();
532     completionHandler(false);
533 }
534
535 void WebPage::getStringSelectionForPasteboard(CompletionHandler<void(String&&)>&& completionHandler)
536 {
537     notImplemented();
538     completionHandler({ });
539 }
540
541 void WebPage::getDataSelectionForPasteboard(const String, CompletionHandler<void(SharedMemory::Handle&&, uint64_t)>&& completionHandler)
542 {
543     notImplemented();
544     completionHandler({ }, 0);
545 }
546
547 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
548 {
549     notImplemented();
550     return 0;
551 }
552
553 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest&)
554 {
555     notImplemented();
556     return false;
557 }
558
559 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent&, CompletionHandler<void(bool)>&& completionHandler)
560 {
561     notImplemented();
562     completionHandler(false);
563 }
564
565 void WebPage::acceptsFirstMouse(int, const WebKit::WebMouseEvent&, CompletionHandler<void(bool)>&& completionHandler)
566 {
567     notImplemented();
568     completionHandler(false);
569 }
570
571 void WebPage::computePagesForPrintingPDFDocument(WebCore::FrameIdentifier, const PrintInfo&, Vector<IntRect>&)
572 {
573     notImplemented();
574 }
575
576 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef, PDFDocument *, const PrintInfo&, uint32_t, uint32_t)
577 {
578     notImplemented();
579 }
580
581 void WebPage::advanceToNextMisspelling(bool)
582 {
583     notImplemented();
584 }
585
586 IntRect WebPage::rectForElementAtInteractionLocation() const
587 {
588     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::AllowVisibleChildFrameContentOnly };
589     HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(m_lastInteractionLocation, hitType);
590     Node* hitNode = result.innerNode();
591     if (!hitNode || !hitNode->renderer())
592         return IntRect();
593     return result.innerNodeFrame()->view()->contentsToRootView(hitNode->renderer()->absoluteBoundingBoxRect(true));
594 }
595
596 void WebPage::updateSelectionAppearance()
597 {
598     auto& frame = m_page->focusController().focusedOrMainFrame();
599     auto& editor = frame.editor();
600     if (editor.ignoreSelectionChanges())
601         return;
602
603     if (editor.client() && !editor.client()->shouldRevealCurrentSelectionAfterInsertion())
604         return;
605
606     if (!editor.hasComposition() && frame.selection().selection().isNone())
607         return;
608
609     didChangeSelection();
610 }
611
612 static void dispatchSyntheticMouseMove(Frame& mainFrame, const WebCore::FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers, WebCore::PointerID pointerId = WebCore::mousePointerID)
613 {
614     IntPoint roundedAdjustedPoint = roundedIntPoint(location);
615     auto shiftKey = modifiers.contains(WebEvent::Modifier::ShiftKey);
616     auto ctrlKey = modifiers.contains(WebEvent::Modifier::ControlKey);
617     auto altKey = modifiers.contains(WebEvent::Modifier::AltKey);
618     auto metaKey = modifiers.contains(WebEvent::Modifier::MetaKey);
619     auto mouseEvent = PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), WebCore::ForceAtClick, WebCore::NoTap, pointerId);
620     // FIXME: Pass caps lock state.
621     mainFrame.eventHandler().dispatchSyntheticMouseMove(mouseEvent);
622 }
623
624 void WebPage::generateSyntheticEditingCommand(SyntheticEditingCommandType command)
625 {
626     PlatformKeyboardEvent keyEvent;
627     auto& frame = m_page->focusController().focusedOrMainFrame();
628     
629     OptionSet<PlatformEvent::Modifier> modifiers;
630     modifiers.add(PlatformEvent::Modifier::MetaKey);
631     
632     switch (command) {
633     case SyntheticEditingCommandType::Undo:
634         keyEvent = PlatformKeyboardEvent(PlatformEvent::KeyDown, "z", "z",
635         "z", "KeyZ"_s,
636         @"U+005A", 90, false, false, false, modifiers, WallTime::now());
637         break;
638     case SyntheticEditingCommandType::Redo:
639         keyEvent = PlatformKeyboardEvent(PlatformEvent::KeyDown, "y", "y",
640         "y", "KeyY"_s,
641         @"U+0059", 89, false, false, false, modifiers, WallTime::now());
642         break;
643     case SyntheticEditingCommandType::ToggleBoldface:
644         keyEvent = PlatformKeyboardEvent(PlatformEvent::KeyDown, "b", "b",
645         "b", "KeyB"_s,
646         @"U+0042", 66, false, false, false, modifiers, WallTime::now());
647         break;
648     case SyntheticEditingCommandType::ToggleItalic:
649         keyEvent = PlatformKeyboardEvent(PlatformEvent::KeyDown, "i", "i",
650         "i", "KeyI"_s,
651         @"U+0049", 73, false, false, false, modifiers, WallTime::now());
652         break;
653     case SyntheticEditingCommandType::ToggleUnderline:
654         keyEvent = PlatformKeyboardEvent(PlatformEvent::KeyDown, "u", "u",
655         "u", "KeyU"_s,
656         @"U+0055", 85, false, false, false, modifiers, WallTime::now());
657         break;
658     default:
659         break;
660     }
661
662     keyEvent.setIsSyntheticEvent();
663     
664     PlatformKeyboardEvent::setCurrentModifierState(modifiers);
665     
666     frame.eventHandler().keyEvent(keyEvent);
667 }
668
669 void WebPage::handleSyntheticClick(Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers, WebCore::PointerID pointerId)
670 {
671     if (!nodeRespondingToClick.document().settings().contentChangeObserverEnabled()) {
672         completeSyntheticClick(nodeRespondingToClick, location, modifiers, WebCore::OneFingerTap, pointerId);
673         return;
674     }
675
676     auto& respondingDocument = nodeRespondingToClick.document();
677     auto& contentChangeObserver = respondingDocument.contentChangeObserver();
678     auto targetNodeWentFromHiddenToVisible = contentChangeObserver.hiddenTouchTarget() == &nodeRespondingToClick && ContentChangeObserver::isConsideredVisible(nodeRespondingToClick);
679     {
680         LOG_WITH_STREAM(ContentObservation, stream << "handleSyntheticClick: node(" << &nodeRespondingToClick << ") " << location);
681         ContentChangeObserver::MouseMovedScope observingScope(respondingDocument);
682         auto& mainFrame = m_page->mainFrame();
683         dispatchSyntheticMouseMove(mainFrame, location, modifiers, pointerId);
684         mainFrame.document()->updateStyleIfNeeded();
685         if (m_isClosed)
686             return;
687     }
688
689     if (targetNodeWentFromHiddenToVisible) {
690         LOG(ContentObservation, "handleSyntheticClick: target node was hidden and now is visible -> hover.");
691         return;
692     }
693
694     auto nodeTriggersFastPath = [&](auto& targetNode) {
695         if (!is<Element>(targetNode))
696             return false;
697         if (is<HTMLFormControlElement>(targetNode))
698             return true;
699         if (targetNode.document().quirks().shouldIgnoreAriaForFastPathContentObservationCheck())
700             return false;
701         auto ariaRole = AccessibilityObject::ariaRoleToWebCoreRole(downcast<Element>(targetNode).getAttribute(HTMLNames::roleAttr));
702         return AccessibilityObject::isARIAControl(ariaRole) || AccessibilityObject::isARIAInput(ariaRole);
703     };
704     auto targetNodeTriggersFastPath = nodeTriggersFastPath(nodeRespondingToClick);
705
706     auto observedContentChange = contentChangeObserver.observedContentChange();
707     auto continueContentObservation = !(observedContentChange == WKContentVisibilityChange || targetNodeTriggersFastPath);
708     if (continueContentObservation) {
709         // Wait for callback to completePendingSyntheticClickForContentChangeObserver() to decide whether to send the click event.
710         const Seconds observationDuration = 32_ms;
711         contentChangeObserver.startContentObservationForDuration(observationDuration);
712         LOG(ContentObservation, "handleSyntheticClick: Can't decide it yet -> wait.");
713         m_pendingSyntheticClickNode = &nodeRespondingToClick;
714         m_pendingSyntheticClickLocation = location;
715         m_pendingSyntheticClickModifiers = modifiers;
716         m_pendingSyntheticClickPointerId = pointerId;
717         return;
718     }
719     contentChangeObserver.stopContentObservation();
720     callOnMainThread([protectedThis = makeRefPtr(this), targetNode = Ref<Node>(nodeRespondingToClick), location, modifiers, observedContentChange, pointerId] {
721         if (protectedThis->m_isClosed || !protectedThis->corePage())
722             return;
723
724         auto shouldStayAtHoverState = observedContentChange == WKContentVisibilityChange;
725         if (shouldStayAtHoverState) {
726             // The move event caused new contents to appear. Don't send synthetic click event, but just ensure that the mouse is on the most recent content.
727             dispatchSyntheticMouseMove(protectedThis->corePage()->mainFrame(), location, modifiers, pointerId);
728             LOG(ContentObservation, "handleSyntheticClick: Observed meaningful visible change -> hover.");
729             return;
730         }
731         LOG(ContentObservation, "handleSyntheticClick: calling completeSyntheticClick -> click.");
732         protectedThis->completeSyntheticClick(targetNode, location, modifiers, WebCore::OneFingerTap, pointerId);
733     });
734 }
735
736 void WebPage::didFinishContentChangeObserving(WKContentChange observedContentChange)
737 {
738     LOG_WITH_STREAM(ContentObservation, stream << "didFinishContentChangeObserving: pending target node(" << m_pendingSyntheticClickNode << ")");
739     if (!m_pendingSyntheticClickNode)
740         return;
741     callOnMainThread([protectedThis = makeRefPtr(this), targetNode = Ref<Node>(*m_pendingSyntheticClickNode), originalDocument = makeWeakPtr(m_pendingSyntheticClickNode->document()), observedContentChange, location = m_pendingSyntheticClickLocation, modifiers = m_pendingSyntheticClickModifiers, pointerId = m_pendingSyntheticClickPointerId] {
742         if (protectedThis->m_isClosed || !protectedThis->corePage())
743             return;
744         if (!originalDocument || &targetNode->document() != originalDocument)
745             return;
746
747         // Only dispatch the click if the document didn't get changed by any timers started by the move event.
748         if (observedContentChange == WKContentNoChange) {
749             LOG(ContentObservation, "No chage was observed -> click.");
750             protectedThis->completeSyntheticClick(targetNode, location, modifiers, WebCore::OneFingerTap, pointerId);
751             return;
752         }
753         // Ensure that the mouse is on the most recent content.
754         LOG(ContentObservation, "Observed meaningful visible change -> hover.");
755         dispatchSyntheticMouseMove(protectedThis->corePage()->mainFrame(), location, modifiers, pointerId);
756     });
757     m_pendingSyntheticClickNode = nullptr;
758     m_pendingSyntheticClickLocation = { };
759     m_pendingSyntheticClickModifiers = { };
760     m_pendingSyntheticClickPointerId = 0;
761 }
762
763 void WebPage::completeSyntheticClick(Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers, SyntheticClickType syntheticClickType, WebCore::PointerID pointerId)
764 {
765     IntPoint roundedAdjustedPoint = roundedIntPoint(location);
766     Frame& mainframe = m_page->mainFrame();
767
768     RefPtr<Frame> oldFocusedFrame = m_page->focusController().focusedFrame();
769     RefPtr<Element> oldFocusedElement = oldFocusedFrame ? oldFocusedFrame->document()->focusedElement() : nullptr;
770
771     SetForScope<bool> userIsInteractingChange { m_userIsInteracting, true };
772
773     bool tapWasHandled = false;
774     m_lastInteractionLocation = roundedAdjustedPoint;
775
776     // FIXME: Pass caps lock state.
777     bool shiftKey = modifiers.contains(WebEvent::Modifier::ShiftKey);
778     bool ctrlKey = modifiers.contains(WebEvent::Modifier::ControlKey);
779     bool altKey = modifiers.contains(WebEvent::Modifier::AltKey);
780     bool metaKey = modifiers.contains(WebEvent::Modifier::MetaKey);
781
782     tapWasHandled |= mainframe.eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), WebCore::ForceAtClick, syntheticClickType, pointerId));
783     if (m_isClosed)
784         return;
785
786     tapWasHandled |= mainframe.eventHandler().handleMouseReleaseEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), WebCore::ForceAtClick, syntheticClickType, pointerId));
787     if (m_isClosed)
788         return;
789
790     RefPtr<Frame> newFocusedFrame = m_page->focusController().focusedFrame();
791     RefPtr<Element> newFocusedElement = newFocusedFrame ? newFocusedFrame->document()->focusedElement() : nullptr;
792
793     // If the focus has not changed, we need to notify the client anyway, since it might be
794     // necessary to start assisting the node.
795     // If the node has been focused by JavaScript without user interaction, the
796     // keyboard is not on screen.
797     if (newFocusedElement && newFocusedElement == oldFocusedElement)
798         elementDidRefocus(*newFocusedElement);
799
800     if (nodeRespondingToClick.document().settings().contentChangeObserverEnabled()) {
801         auto& document = nodeRespondingToClick.document();
802         // Dispatch mouseOut to dismiss tooltip content when tapping on the control bar buttons (cc, settings).
803         if (document.quirks().needsYouTubeMouseOutQuirk()) {
804             if (auto* frame = document.frame())
805                 frame->eventHandler().dispatchSyntheticMouseOut(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::NoType, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, WebCore::NoTap, pointerId));
806         }
807     }
808
809     if (m_isClosed)
810         return;
811
812     if (!tapWasHandled || !nodeRespondingToClick.isElementNode())
813         send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(location)));
814     
815     send(Messages::WebPageProxy::DidCompleteSyntheticClick());
816 }
817
818 void WebPage::handleTap(const IntPoint& point, OptionSet<WebEvent::Modifier> modifiers, TransactionID lastLayerTreeTransactionId)
819 {
820     FloatPoint adjustedPoint;
821     Node* nodeRespondingToClick = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
822     Frame* frameRespondingToClick = nodeRespondingToClick ? nodeRespondingToClick->document().frame() : nullptr;
823     IntPoint adjustedIntPoint = roundedIntPoint(adjustedPoint);
824
825     if (!frameRespondingToClick || lastLayerTreeTransactionId < WebFrame::fromCoreFrame(*frameRespondingToClick)->firstLayerTreeTransactionIDAfterDidCommitLoad())
826         send(Messages::WebPageProxy::DidNotHandleTapAsClick(adjustedIntPoint));
827 #if ENABLE(DATA_DETECTION)
828     else if (is<Element>(*nodeRespondingToClick) && DataDetection::shouldCancelDefaultAction(downcast<Element>(*nodeRespondingToClick))) {
829         InteractionInformationRequest request(adjustedIntPoint);
830         requestPositionInformation(request);
831         send(Messages::WebPageProxy::DidNotHandleTapAsClick(adjustedIntPoint));
832     }
833 #endif
834     else
835         handleSyntheticClick(*nodeRespondingToClick, adjustedPoint, modifiers);
836 }
837
838 void WebPage::handleDoubleTapForDoubleClickAtPoint(const IntPoint& point, OptionSet<WebEvent::Modifier> modifiers, TransactionID lastLayerTreeTransactionId)
839 {
840     FloatPoint adjustedPoint;
841     auto* nodeRespondingToDoubleClick = m_page->mainFrame().nodeRespondingToDoubleClickEvent(point, adjustedPoint);
842     if (!nodeRespondingToDoubleClick)
843         return;
844
845     auto* frameRespondingToDoubleClick = nodeRespondingToDoubleClick->document().frame();
846     if (!frameRespondingToDoubleClick || lastLayerTreeTransactionId < WebFrame::fromCoreFrame(*frameRespondingToDoubleClick)->firstLayerTreeTransactionIDAfterDidCommitLoad())
847         return;
848
849     bool shiftKey = modifiers.contains(WebEvent::Modifier::ShiftKey);
850     bool ctrlKey = modifiers.contains(WebEvent::Modifier::ControlKey);
851     bool altKey = modifiers.contains(WebEvent::Modifier::AltKey);
852     bool metaKey = modifiers.contains(WebEvent::Modifier::MetaKey);
853     auto roundedAdjustedPoint = roundedIntPoint(adjustedPoint);
854     nodeRespondingToDoubleClick->document().frame()->eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MousePressed, 2, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, WebCore::OneFingerTap));
855     if (m_isClosed)
856         return;
857     nodeRespondingToDoubleClick->document().frame()->eventHandler().handleMouseReleaseEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MouseReleased, 2, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, WebCore::OneFingerTap));
858 }
859
860 void WebPage::requestFocusedElementInformation(WebKit::CallbackID callbackID)
861 {
862     FocusedElementInformation info;
863     if (m_focusedElement)
864         getFocusedElementInformation(info);
865
866     send(Messages::WebPageProxy::FocusedElementInformationCallback(info, callbackID));
867 }
868
869 #if ENABLE(DATA_INTERACTION)
870 void WebPage::requestDragStart(const IntPoint& clientPosition, const IntPoint& globalPosition, uint64_t allowedActions)
871 {
872     SetForScope<WebCore::DragSourceAction> allowedActionsForScope(m_allowedDragSourceActions, static_cast<WebCore::DragSourceAction>(allowedActions));
873     bool didStart = m_page->mainFrame().eventHandler().tryToBeginDragAtPoint(clientPosition, globalPosition);
874     send(Messages::WebPageProxy::DidHandleDragStartRequest(didStart));
875 }
876
877 void WebPage::requestAdditionalItemsForDragSession(const IntPoint& clientPosition, const IntPoint& globalPosition, uint64_t allowedActions)
878 {
879     SetForScope<WebCore::DragSourceAction> allowedActionsForScope(m_allowedDragSourceActions, static_cast<WebCore::DragSourceAction>(allowedActions));
880     // To augment the platform drag session with additional items, end the current drag session and begin a new drag session with the new drag item.
881     // This process is opaque to the UI process, which still maintains the old drag item in its drag session. Similarly, this persistent drag session
882     // is opaque to the web process, which only sees that the current drag has ended, and that a new one is beginning.
883     PlatformMouseEvent event(clientPosition, globalPosition, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), 0, NoTap);
884     m_page->dragController().dragEnded();
885     m_page->mainFrame().eventHandler().dragSourceEndedAt(event, DragOperationNone, MayExtendDragSession::Yes);
886
887     bool didHandleDrag = m_page->mainFrame().eventHandler().tryToBeginDragAtPoint(clientPosition, globalPosition);
888     send(Messages::WebPageProxy::DidHandleAdditionalDragItemsRequest(didHandleDrag));
889 }
890
891 void WebPage::insertDroppedImagePlaceholders(const Vector<IntSize>& imageSizes, CompletionHandler<void(const Vector<IntRect>&, Optional<WebCore::TextIndicatorData>)>&& reply)
892 {
893     m_page->dragController().insertDroppedImagePlaceholdersAtCaret(imageSizes);
894     auto placeholderRects = m_page->dragController().droppedImagePlaceholders().map([&] (auto& element) {
895         return rootViewBoundsForElement(element);
896     });
897
898     auto imagePlaceholderRange = m_page->dragController().droppedImagePlaceholderRange();
899     if (placeholderRects.size() != imageSizes.size()) {
900         RELEASE_LOG(DragAndDrop, "Failed to insert dropped image placeholders: placeholder rect count (%tu) does not match image size count (%tu).", placeholderRects.size(), imageSizes.size());
901         reply({ }, WTF::nullopt);
902         return;
903     }
904
905     if (!imagePlaceholderRange) {
906         RELEASE_LOG(DragAndDrop, "Failed to insert dropped image placeholders: no image placeholder range.");
907         reply({ }, WTF::nullopt);
908         return;
909     }
910
911     Optional<TextIndicatorData> textIndicatorData;
912     OptionSet<TextIndicatorOption> textIndicatorOptions = {
913         TextIndicatorOptionIncludeSnapshotOfAllVisibleContentWithoutSelection,
914         TextIndicatorOptionExpandClipBeyondVisibleRect,
915         TextIndicatorOptionPaintAllContent,
916         TextIndicatorOptionUseSelectionRectForSizing
917     };
918
919     if (auto textIndicator = TextIndicator::createWithRange(*imagePlaceholderRange, textIndicatorOptions.toRaw(), TextIndicatorPresentationTransition::None, { }))
920         textIndicatorData = textIndicator->data();
921
922     reply(WTFMove(placeholderRects), WTFMove(textIndicatorData));
923 }
924
925 void WebPage::didConcludeDrop()
926 {
927     m_rangeForDropSnapshot = nullptr;
928     m_pendingImageElementsForDropSnapshot.clear();
929 }
930
931 void WebPage::didConcludeEditDrag()
932 {
933     send(Messages::WebPageProxy::WillReceiveEditDragSnapshot());
934
935     layoutIfNeeded();
936
937     m_pendingImageElementsForDropSnapshot.clear();
938
939     auto frame = makeRef(m_page->focusController().focusedOrMainFrame());
940     if (auto selectionRange = frame->selection().selection().toNormalizedRange()) {
941         m_pendingImageElementsForDropSnapshot = visibleImageElementsInRangeWithNonLoadedImages(*selectionRange);
942         auto collapsedRange = Range::create(selectionRange->ownerDocument(), selectionRange->endPosition(), selectionRange->endPosition());
943         frame->selection().setSelectedRange(collapsedRange.ptr(), DOWNSTREAM, FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
944
945         m_rangeForDropSnapshot = WTFMove(selectionRange);
946     }
947
948     if (m_pendingImageElementsForDropSnapshot.isEmpty())
949         computeAndSendEditDragSnapshot();
950 }
951
952 void WebPage::didFinishLoadingImageForElement(WebCore::HTMLImageElement& element)
953 {
954     if (element.isDroppedImagePlaceholder())
955         m_page->dragController().finalizeDroppedImagePlaceholder(element);
956
957     if (m_pendingImageElementsForDropSnapshot.isEmpty())
958         return;
959
960     m_pendingImageElementsForDropSnapshot.remove(&element);
961
962     if (m_pendingImageElementsForDropSnapshot.isEmpty())
963         computeAndSendEditDragSnapshot();
964 }
965
966 void WebPage::computeAndSendEditDragSnapshot()
967 {
968     Optional<TextIndicatorData> textIndicatorData;
969     static auto defaultTextIndicatorOptionsForEditDrag = TextIndicatorOptionIncludeSnapshotOfAllVisibleContentWithoutSelection | TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionPaintAllContent | TextIndicatorOptionIncludeMarginIfRangeMatchesSelection | TextIndicatorOptionPaintBackgrounds | TextIndicatorOptionComputeEstimatedBackgroundColor | TextIndicatorOptionUseSelectionRectForSizing | TextIndicatorOptionIncludeSnapshotWithSelectionHighlight;
970     if (auto range = std::exchange(m_rangeForDropSnapshot, nullptr)) {
971         if (auto textIndicator = TextIndicator::createWithRange(*range, defaultTextIndicatorOptionsForEditDrag, TextIndicatorPresentationTransition::None, { }))
972             textIndicatorData = textIndicator->data();
973     }
974     send(Messages::WebPageProxy::DidReceiveEditDragSnapshot(WTFMove(textIndicatorData)));
975 }
976
977 #endif
978
979 void WebPage::sendTapHighlightForNodeIfNecessary(uint64_t requestID, Node* node)
980 {
981 #if ENABLE(TOUCH_EVENTS)
982     if (!node)
983         return;
984
985     if (m_page->isEditable() && node == m_page->mainFrame().document()->body())
986         return;
987
988     if (is<Element>(*node)) {
989         ASSERT(m_page);
990         m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host().toString());
991     }
992
993     if (is<HTMLAreaElement>(node)) {
994         node = downcast<HTMLAreaElement>(node)->imageElement();
995         if (!node)
996             return;
997     }
998
999     Vector<FloatQuad> quads;
1000     if (RenderObject *renderer = node->renderer()) {
1001         renderer->absoluteQuads(quads);
1002         Color highlightColor = renderer->style().tapHighlightColor();
1003         if (!node->document().frame()->isMainFrame()) {
1004             FrameView* view = node->document().frame()->view();
1005             for (size_t i = 0; i < quads.size(); ++i) {
1006                 FloatQuad& currentQuad = quads[i];
1007                 currentQuad.setP1(view->contentsToRootView(roundedIntPoint(currentQuad.p1())));
1008                 currentQuad.setP2(view->contentsToRootView(roundedIntPoint(currentQuad.p2())));
1009                 currentQuad.setP3(view->contentsToRootView(roundedIntPoint(currentQuad.p3())));
1010                 currentQuad.setP4(view->contentsToRootView(roundedIntPoint(currentQuad.p4())));
1011             }
1012         }
1013
1014         RoundedRect::Radii borderRadii;
1015         if (is<RenderBox>(*renderer))
1016             borderRadii = downcast<RenderBox>(*renderer).borderRadii();
1017
1018         bool nodeHasBuiltInClickHandling = is<HTMLFormControlElement>(*node) || is<HTMLAnchorElement>(*node) || is<HTMLLabelElement>(*node) || is<HTMLSummaryElement>(*node) || node->isLink();
1019         send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, highlightColor, quads, roundedIntSize(borderRadii.topLeft()), roundedIntSize(borderRadii.topRight()), roundedIntSize(borderRadii.bottomLeft()), roundedIntSize(borderRadii.bottomRight()), nodeHasBuiltInClickHandling));
1020     }
1021 #else
1022     UNUSED_PARAM(requestID);
1023     UNUSED_PARAM(node);
1024 #endif
1025 }
1026
1027 void WebPage::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t requestID)
1028 {
1029     FloatPoint adjustedPoint;
1030     Node* nodeRespondingToClick = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
1031     if (!nodeRespondingToClick || !nodeRespondingToClick->renderer()) {
1032         send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(adjustedPoint)));
1033         return;
1034     }
1035     sendTapHighlightForNodeIfNecessary(requestID, nodeRespondingToClick);
1036 #if ENABLE(DATA_DETECTION)
1037     if (is<Element>(*nodeRespondingToClick) && DataDetection::shouldCancelDefaultAction(downcast<Element>(*nodeRespondingToClick))) {
1038         InteractionInformationRequest request(roundedIntPoint(adjustedPoint));
1039         requestPositionInformation(request);
1040         send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(adjustedPoint)));
1041     } else
1042 #endif
1043         completeSyntheticClick(*nodeRespondingToClick, adjustedPoint, modifiers, WebCore::TwoFingerTap);
1044 }
1045
1046 void WebPage::handleStylusSingleTapAtPoint(const WebCore::IntPoint& point, uint64_t requestID)
1047 {
1048     SetForScope<bool> userIsInteractingChange { m_userIsInteracting, true };
1049
1050     auto& frame = m_page->focusController().focusedOrMainFrame();
1051
1052     auto pointInDocument = frame.view()->rootViewToContents(point);
1053     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::AllowVisibleChildFrameContentOnly };
1054     HitTestResult hitTest = frame.eventHandler().hitTestResultAtPoint(pointInDocument, hitType);
1055
1056     Node* node = hitTest.innerNonSharedNode();
1057     if (!node)
1058         return;
1059     auto renderer = node->renderer();
1060     if (!renderer)
1061         return;
1062
1063     if (renderer->isReplaced())
1064         return;
1065
1066     VisiblePosition position = renderer->positionForPoint(hitTest.localPoint(), nullptr);
1067     if (position.isNull())
1068         position = firstPositionInOrBeforeNode(node);
1069
1070     if (position.isNull())
1071         return;
1072
1073     auto range = Range::create(*frame.document(), position, position);
1074     frame.selection().setSelectedRange(range.ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1075     auto image = frame.editor().insertEditableImage();
1076     frame.document()->setFocusedElement(image.get());
1077 }
1078
1079 void WebPage::potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint& position, bool shouldRequestMagnificationInformation)
1080 {
1081     m_potentialTapNode = m_page->mainFrame().nodeRespondingToClickEvents(position, m_potentialTapLocation, m_potentialTapSecurityOrigin.get());
1082
1083     if (shouldRequestMagnificationInformation && m_potentialTapNode && m_viewGestureGeometryCollector) {
1084         // FIXME: Could this be combined into tap highlight?
1085         FloatPoint origin = position;
1086         FloatRect renderRect;
1087         bool fitEntireRect;
1088         double viewportMinimumScale;
1089         double viewportMaximumScale;
1090
1091         m_viewGestureGeometryCollector->computeZoomInformationForNode(*m_potentialTapNode, origin, renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale);
1092
1093         bool nodeIsRootLevel = is<WebCore::Document>(*m_potentialTapNode) || is<WebCore::HTMLBodyElement>(*m_potentialTapNode);
1094         send(Messages::WebPageProxy::HandleSmartMagnificationInformationForPotentialTap(requestID, renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale, nodeIsRootLevel));
1095     }
1096
1097     sendTapHighlightForNodeIfNecessary(requestID, m_potentialTapNode.get());
1098 #if ENABLE(TOUCH_EVENTS)
1099     if (m_potentialTapNode && !m_potentialTapNode->allowsDoubleTapGesture())
1100         send(Messages::WebPageProxy::DisableDoubleTapGesturesDuringTapIfNecessary(requestID));
1101 #endif
1102 }
1103
1104 void WebPage::commitPotentialTap(OptionSet<WebEvent::Modifier> modifiers, TransactionID lastLayerTreeTransactionId, WebCore::PointerID pointerId)
1105 {
1106     auto invalidTargetForSingleClick = !m_potentialTapNode;
1107     if (!invalidTargetForSingleClick) {
1108         bool targetRenders = m_potentialTapNode->renderer();
1109         if (!targetRenders && is<Element>(m_potentialTapNode.get()))
1110             targetRenders = downcast<Element>(*m_potentialTapNode).renderOrDisplayContentsStyle();
1111         invalidTargetForSingleClick = !targetRenders && !is<HTMLAreaElement>(m_potentialTapNode.get());
1112     }
1113     if (invalidTargetForSingleClick) {
1114         commitPotentialTapFailed();
1115         return;
1116     }
1117
1118     FloatPoint adjustedPoint;
1119     Node* nodeRespondingToClick = m_page->mainFrame().nodeRespondingToClickEvents(m_potentialTapLocation, adjustedPoint, m_potentialTapSecurityOrigin.get());
1120     Frame* frameRespondingToClick = nodeRespondingToClick ? nodeRespondingToClick->document().frame() : nullptr;
1121
1122     if (!frameRespondingToClick || lastLayerTreeTransactionId < WebFrame::fromCoreFrame(*frameRespondingToClick)->firstLayerTreeTransactionIDAfterDidCommitLoad()) {
1123         commitPotentialTapFailed();
1124         return;
1125     }
1126
1127     if (m_potentialTapNode == nodeRespondingToClick) {
1128 #if ENABLE(DATA_DETECTION)
1129         if (is<Element>(*nodeRespondingToClick) && DataDetection::shouldCancelDefaultAction(downcast<Element>(*nodeRespondingToClick))) {
1130             InteractionInformationRequest request(roundedIntPoint(m_potentialTapLocation));
1131             requestPositionInformation(request);
1132             commitPotentialTapFailed();
1133         } else
1134 #endif
1135             handleSyntheticClick(*nodeRespondingToClick, adjustedPoint, modifiers, pointerId);
1136     } else
1137         commitPotentialTapFailed();
1138
1139     m_potentialTapNode = nullptr;
1140     m_potentialTapLocation = FloatPoint();
1141     m_potentialTapSecurityOrigin = nullptr;
1142 }
1143
1144 void WebPage::commitPotentialTapFailed()
1145 {
1146     ContentChangeObserver::didCancelPotentialTap(m_page->mainFrame());
1147     if (!m_page->focusController().focusedOrMainFrame().selection().selection().isContentEditable())
1148         clearSelection();
1149
1150     send(Messages::WebPageProxy::CommitPotentialTapFailed());
1151     send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(m_potentialTapLocation)));
1152 }
1153
1154 void WebPage::cancelPotentialTap()
1155 {
1156     ContentChangeObserver::didCancelPotentialTap(m_page->mainFrame());
1157     cancelPotentialTapInFrame(*m_mainFrame);
1158 }
1159
1160 void WebPage::cancelPotentialTapInFrame(WebFrame& frame)
1161 {
1162     if (m_potentialTapNode) {
1163         auto* potentialTapFrame = m_potentialTapNode->document().frame();
1164         if (potentialTapFrame && !potentialTapFrame->tree().isDescendantOf(frame.coreFrame()))
1165             return;
1166     }
1167
1168     m_potentialTapNode = nullptr;
1169     m_potentialTapLocation = FloatPoint();
1170     m_potentialTapSecurityOrigin = nullptr;
1171 }
1172
1173 void WebPage::didRecognizeLongPress()
1174 {
1175     ContentChangeObserver::didRecognizeLongPress(m_page->mainFrame());
1176 }
1177
1178 void WebPage::tapHighlightAtPosition(uint64_t requestID, const FloatPoint& position)
1179 {
1180     Frame& mainframe = m_page->mainFrame();
1181     FloatPoint adjustedPoint;
1182     sendTapHighlightForNodeIfNecessary(requestID, mainframe.nodeRespondingToClickEvents(position, adjustedPoint));
1183 }
1184
1185 void WebPage::inspectorNodeSearchMovedToPosition(const FloatPoint& position)
1186 {
1187     IntPoint adjustedPoint = roundedIntPoint(position);
1188     Frame& mainframe = m_page->mainFrame();
1189
1190     mainframe.eventHandler().mouseMoved(PlatformMouseEvent(adjustedPoint, adjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, false, false, false, false, { }, 0, WebCore::NoTap));
1191     mainframe.document()->updateStyleIfNeeded();
1192 }
1193
1194 void WebPage::inspectorNodeSearchEndedAtPosition(const FloatPoint& position)
1195 {
1196     if (Node* node = m_page->mainFrame().deepestNodeAtLocation(position))
1197         node->inspect();
1198 }
1199
1200 void WebPage::updateInputContextAfterBlurringAndRefocusingElementIfNeeded(Element& element)
1201 {
1202     if (m_recentlyBlurredElement != &element || !m_isShowingInputViewForFocusedElement)
1203         return;
1204
1205     m_hasPendingInputContextUpdateAfterBlurringAndRefocusingElement = true;
1206     callOnMainThread([this, protectedThis = makeRefPtr(this)] {
1207         if (m_hasPendingInputContextUpdateAfterBlurringAndRefocusingElement)
1208             send(Messages::WebPageProxy::UpdateInputContextAfterBlurringAndRefocusingElement());
1209         m_hasPendingInputContextUpdateAfterBlurringAndRefocusingElement = false;
1210     });
1211 }
1212
1213 void WebPage::blurFocusedElement()
1214 {
1215     if (!m_focusedElement)
1216         return;
1217
1218     m_focusedElement->blur();
1219 }
1220
1221 void WebPage::setIsShowingInputViewForFocusedElement(bool showingInputView)
1222 {
1223     m_isShowingInputViewForFocusedElement = showingInputView;
1224 }
1225
1226 void WebPage::setFocusedElementValue(const String& value)
1227 {
1228     // FIXME: should also handle the case of HTMLSelectElement.
1229     if (is<HTMLInputElement>(m_focusedElement.get()))
1230         downcast<HTMLInputElement>(*m_focusedElement).setValue(value, DispatchInputAndChangeEvent);
1231 }
1232
1233 void WebPage::setFocusedElementValueAsNumber(double value)
1234 {
1235     if (is<HTMLInputElement>(m_focusedElement.get()))
1236         downcast<HTMLInputElement>(*m_focusedElement).setValueAsNumber(value, DispatchInputAndChangeEvent);
1237 }
1238
1239 void WebPage::setFocusedElementSelectedIndex(uint32_t index, bool allowMultipleSelection)
1240 {
1241     if (is<HTMLSelectElement>(m_focusedElement.get()))
1242         downcast<HTMLSelectElement>(*m_focusedElement).optionSelectedByUser(index, true, allowMultipleSelection);
1243 }
1244
1245 void WebPage::showInspectorHighlight(const WebCore::Highlight& highlight)
1246 {
1247     send(Messages::WebPageProxy::ShowInspectorHighlight(highlight));
1248 }
1249
1250 void WebPage::hideInspectorHighlight()
1251 {
1252     send(Messages::WebPageProxy::HideInspectorHighlight());
1253 }
1254
1255 void WebPage::showInspectorIndication()
1256 {
1257     send(Messages::WebPageProxy::ShowInspectorIndication());
1258 }
1259
1260 void WebPage::hideInspectorIndication()
1261 {
1262     send(Messages::WebPageProxy::HideInspectorIndication());
1263 }
1264
1265 void WebPage::enableInspectorNodeSearch()
1266 {
1267     send(Messages::WebPageProxy::EnableInspectorNodeSearch());
1268 }
1269
1270 void WebPage::disableInspectorNodeSearch()
1271 {
1272     send(Messages::WebPageProxy::DisableInspectorNodeSearch());
1273 }
1274
1275 void WebPage::setForceAlwaysUserScalable(bool userScalable)
1276 {
1277     m_forceAlwaysUserScalable = userScalable;
1278     m_viewportConfiguration.setForceAlwaysUserScalable(userScalable);
1279 }
1280
1281 static IntRect elementBoundsInFrame(const Frame& frame, const Element& focusedElement)
1282 {
1283     frame.document()->updateLayoutIgnorePendingStylesheets();
1284     
1285     if (focusedElement.hasTagName(HTMLNames::textareaTag) || focusedElement.hasTagName(HTMLNames::inputTag) || focusedElement.hasTagName(HTMLNames::selectTag))
1286         return WebPage::absoluteInteractionBoundsForElement(focusedElement);
1287
1288     if (auto* rootEditableElement = focusedElement.rootEditableElement())
1289         return WebPage::absoluteInteractionBoundsForElement(*rootEditableElement);
1290
1291     return { };
1292 }
1293
1294 static IntPoint constrainPoint(const IntPoint& point, const Frame& frame, const Element& focusedElement)
1295 {
1296     ASSERT(&focusedElement.document() == frame.document());
1297     const int DEFAULT_CONSTRAIN_INSET = 2;
1298     IntRect innerFrame = elementBoundsInFrame(frame, focusedElement);
1299     IntPoint constrainedPoint = point;
1300
1301     int minX = innerFrame.x() + DEFAULT_CONSTRAIN_INSET;
1302     int maxX = innerFrame.maxX() - DEFAULT_CONSTRAIN_INSET;
1303     int minY = innerFrame.y() + DEFAULT_CONSTRAIN_INSET;
1304     int maxY = innerFrame.maxY() - DEFAULT_CONSTRAIN_INSET;
1305
1306     if (point.x() < minX)
1307         constrainedPoint.setX(minX);
1308     else if (point.x() > maxX)
1309         constrainedPoint.setX(maxX);
1310
1311     if (point.y() < minY)
1312         constrainedPoint.setY(minY);
1313     else if (point.y() >= maxY)
1314         constrainedPoint.setY(maxY);
1315                     
1316     return constrainedPoint;
1317 }
1318
1319 void WebPage::selectWithGesture(const IntPoint& point, uint32_t granularity, uint32_t gestureType, uint32_t gestureState, bool isInteractingWithFocusedElement, CallbackID callbackID)
1320 {
1321     if (static_cast<GestureRecognizerState>(gestureState) == GestureRecognizerState::Began)
1322         setFocusedFrameBeforeSelectingTextAtLocation(point);
1323
1324     auto& frame = m_page->focusController().focusedOrMainFrame();
1325     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
1326
1327     if (position.isNull()) {
1328         send(Messages::WebPageProxy::GestureCallback(point, gestureType, gestureState, 0, callbackID));
1329         return;
1330     }
1331     RefPtr<Range> range;
1332     SelectionFlags flags = None;
1333     GestureRecognizerState wkGestureState = static_cast<GestureRecognizerState>(gestureState);
1334     switch (static_cast<GestureType>(gestureType)) {
1335     case GestureType::PhraseBoundary:
1336     {
1337         if (!frame.editor().hasComposition())
1338             break;
1339         RefPtr<Range> markedRange = frame.editor().compositionRange();
1340         if (position < markedRange->startPosition())
1341             position = markedRange->startPosition();
1342         if (position > markedRange->endPosition())
1343             position = markedRange->endPosition();
1344         if (wkGestureState != GestureRecognizerState::Began)
1345             flags = distanceBetweenPositions(markedRange->startPosition(), frame.selection().selection().start()) != distanceBetweenPositions(markedRange->startPosition(), position) ? PhraseBoundaryChanged : None;
1346         else
1347             flags = PhraseBoundaryChanged;
1348         range = Range::create(*frame.document(), position, position);
1349     }
1350         break;
1351
1352     case GestureType::OneFingerTap:
1353     {
1354         VisiblePosition result;
1355         // move the position at the end of the word
1356         if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
1357             // Don't cross line boundaries.
1358             result = position;
1359         } else if (withinTextUnitOfGranularity(position, WordGranularity, DirectionForward)) {
1360             // The position lies within a word.
1361             RefPtr<Range> wordRange = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionForward);
1362             if (wordRange) {
1363                 result = wordRange->startPosition();
1364                 if (distanceBetweenPositions(position, result) > 1)
1365                     result = wordRange->endPosition();
1366             }
1367             flags = WordIsNearTap;
1368         } else if (atBoundaryOfGranularity(position, WordGranularity, DirectionBackward)) {
1369             // The position is at the end of a word.
1370             result = position;
1371         } else {
1372             // The position is not within a word.
1373             // Go to the next boundary.
1374             result = positionOfNextBoundaryOfGranularity(position, WordGranularity, DirectionForward);
1375
1376             // If there is no such boundary we go to the end of the element.
1377             if (result.isNull())
1378                 result = endOfEditableContent(position);
1379         }
1380         if (result.isNotNull())
1381             range = Range::create(*frame.document(), result, result);
1382     }
1383         break;
1384
1385     case GestureType::Loupe:
1386         if (position.rootEditableElement())
1387             range = Range::create(*frame.document(), position, position);
1388         else
1389 #if !PLATFORM(MACCATALYST)
1390             range = wordRangeFromPosition(position);
1391 #else
1392             switch (wkGestureState) {
1393             case GestureRecognizerState::Began:
1394                 m_startingGestureRange = Range::create(*frame.document(), position, position);
1395                 break;
1396             case GestureRecognizerState::Changed:
1397                 if (m_startingGestureRange) {
1398                     if (m_startingGestureRange->startPosition() < position)
1399                         range = Range::create(*frame.document(), m_startingGestureRange->startPosition(), position);
1400                     else
1401                         range = Range::create(*frame.document(), position, m_startingGestureRange->startPosition());
1402                 }
1403                 break;
1404             case GestureRecognizerState::Ended:
1405             case GestureRecognizerState::Cancelled:
1406                 m_startingGestureRange = nullptr;
1407                 break;
1408             case GestureRecognizerState::Failed:
1409             case GestureRecognizerState::Possible:
1410                 ASSERT_NOT_REACHED();
1411                 break;
1412             }
1413 #endif
1414         break;
1415
1416     case GestureType::TapAndAHalf:
1417         switch (wkGestureState) {
1418         case GestureRecognizerState::Began:
1419             range = wordRangeFromPosition(position);
1420             m_currentWordRange = range ? RefPtr<Range>(Range::create(*frame.document(), range->startPosition(), range->endPosition())) : nullptr;
1421             break;
1422         case GestureRecognizerState::Changed:
1423             if (!m_currentWordRange)
1424                 break;
1425             range = Range::create(*frame.document(), m_currentWordRange->startPosition(), m_currentWordRange->endPosition());
1426             if (position < range->startPosition())
1427                 range->setStart(position.deepEquivalent());
1428             if (position > range->endPosition())
1429                 range->setEnd(position.deepEquivalent());
1430             break;
1431         case GestureRecognizerState::Ended:
1432         case GestureRecognizerState::Cancelled:
1433             m_currentWordRange = nullptr;
1434             break;
1435         case GestureRecognizerState::Failed:
1436         case GestureRecognizerState::Possible:
1437             ASSERT_NOT_REACHED();
1438         }
1439         break;
1440
1441     case GestureType::OneFingerDoubleTap:
1442         if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
1443             // Double-tap at end of line only places insertion point there.
1444             // This helps to get the callout for pasting at ends of lines,
1445             // paragraphs, and documents.
1446             range = Range::create(*frame.document(), position, position);
1447          } else
1448             range = wordRangeFromPosition(position);
1449         break;
1450
1451     case GestureType::TwoFingerSingleTap:
1452         // Single tap with two fingers selects the entire paragraph.
1453         range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
1454         break;
1455
1456     case GestureType::OneFingerTripleTap:
1457         if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
1458             // Triple-tap at end of line only places insertion point there.
1459             // This helps to get the callout for pasting at ends of lines,
1460             // paragraphs, and documents.
1461             range = Range::create(*frame.document(), position, position);
1462         } else
1463             range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
1464         break;
1465
1466     default:
1467         break;
1468     }
1469     if (range)
1470         frame.selection().setSelectedRange(range.get(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1471
1472     send(Messages::WebPageProxy::GestureCallback(point, gestureType, gestureState, static_cast<uint32_t>(flags), callbackID));
1473 }
1474
1475 static RefPtr<Range> rangeForPointInRootViewCoordinates(Frame& frame, const IntPoint& pointInRootViewCoordinates, bool baseIsStart)
1476 {
1477     VisibleSelection existingSelection = frame.selection().selection();
1478     VisiblePosition selectionStart = existingSelection.visibleStart();
1479     VisiblePosition selectionEnd = existingSelection.visibleEnd();
1480
1481     auto pointInDocument = frame.view()->rootViewToContents(pointInRootViewCoordinates);
1482
1483     if (baseIsStart) {
1484         int startY = selectionStart.absoluteCaretBounds().center().y();
1485         if (pointInDocument.y() < startY)
1486             pointInDocument.setY(startY);
1487     } else {
1488         int endY = selectionEnd.absoluteCaretBounds().center().y();
1489         if (pointInDocument.y() > endY)
1490             pointInDocument.setY(endY);
1491     }
1492     
1493     VisiblePosition result;
1494     RefPtr<Range> range;
1495
1496     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::AllowVisibleChildFrameContentOnly };
1497     HitTestResult hitTest = frame.eventHandler().hitTestResultAtPoint(pointInDocument, hitType);
1498     if (hitTest.targetNode())
1499         result = frame.eventHandler().selectionExtentRespectingEditingBoundary(frame.selection().selection(), hitTest.localPoint(), hitTest.targetNode()).deepEquivalent();
1500     else
1501         result = frame.visiblePositionForPoint(pointInDocument).deepEquivalent();
1502     
1503     if (baseIsStart) {
1504         if (comparePositions(result, selectionStart) <= 0)
1505             result = selectionStart.next();
1506         else if (&selectionStart.deepEquivalent().anchorNode()->treeScope() != &hitTest.targetNode()->treeScope())
1507             result = VisibleSelection::adjustPositionForEnd(result.deepEquivalent(), selectionStart.deepEquivalent().containerNode());
1508         
1509         if (result.isNotNull())
1510             range = Range::create(*frame.document(), selectionStart, result);
1511     } else {
1512         if (comparePositions(selectionEnd, result) <= 0)
1513             result = selectionEnd.previous();
1514         else if (&hitTest.targetNode()->treeScope() != &selectionEnd.deepEquivalent().anchorNode()->treeScope())
1515             result = VisibleSelection::adjustPositionForStart(result.deepEquivalent(), selectionEnd.deepEquivalent().containerNode());
1516         
1517         if (result.isNotNull())
1518             range = Range::create(*frame.document(), result.deepEquivalent(), selectionEnd);
1519     }
1520     
1521     return range;
1522 }
1523
1524 static RefPtr<Range> rangeAtWordBoundaryForPosition(Frame* frame, const VisiblePosition& position, bool baseIsStart, SelectionDirection direction)
1525 {
1526     SelectionDirection sameDirection = baseIsStart ? DirectionForward : DirectionBackward;
1527     SelectionDirection oppositeDirection = baseIsStart ? DirectionBackward : DirectionForward;
1528     VisiblePosition base = baseIsStart ? frame->selection().selection().visibleStart() : frame->selection().selection().visibleEnd();
1529     VisiblePosition extent = baseIsStart ? frame->selection().selection().visibleEnd() : frame->selection().selection().visibleStart();
1530     VisiblePosition initialExtent = position;
1531
1532     if (atBoundaryOfGranularity(extent, WordGranularity, sameDirection)) {
1533         // This is a word boundary. Leave selection where it is.
1534         return nullptr;
1535     }
1536
1537     if (atBoundaryOfGranularity(extent, WordGranularity, oppositeDirection)) {
1538         // This is a word boundary in the wrong direction. Nudge the selection to a character before proceeding.
1539         extent = baseIsStart ? extent.previous() : extent.next();
1540     }
1541
1542     // Extend to the boundary of the word.
1543
1544     VisiblePosition wordBoundary = positionOfNextBoundaryOfGranularity(extent, WordGranularity, sameDirection);
1545     if (wordBoundary.isNotNull()
1546         && atBoundaryOfGranularity(wordBoundary, WordGranularity, sameDirection)
1547         && initialExtent != wordBoundary) {
1548         extent = wordBoundary;
1549         return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base);
1550     }
1551     // Conversely, if the initial extent equals the current word boundary, then
1552     // run the rest of this function to see if the selection should extend
1553     // the other direction to the other word.
1554
1555     // If this is where the extent was initially, then iterate in the other direction in the document until we hit the next word.
1556     while (extent.isNotNull()
1557         && !atBoundaryOfGranularity(extent, WordGranularity, sameDirection)
1558         && extent != base
1559         && !atBoundaryOfGranularity(extent, LineGranularity, sameDirection)
1560         && !atBoundaryOfGranularity(extent, LineGranularity, oppositeDirection)) {
1561         extent = baseIsStart ? extent.next() : extent.previous();
1562     }
1563
1564     // Don't let the smart extension make the extent equal the base.
1565     // Expand out to word boundary.
1566     if (extent.isNull() || extent == base)
1567         extent = wordBoundary;
1568     if (extent.isNull())
1569         return nullptr;
1570
1571     return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base);
1572 }
1573
1574 IntRect WebPage::rootViewBoundsForElement(const Element& element)
1575 {
1576     auto* frame = element.document().frame();
1577     if (!frame)
1578         return { };
1579
1580     auto* view = frame->view();
1581     if (!view)
1582         return { };
1583
1584     auto* renderer = element.renderer();
1585     if (!renderer)
1586         return { };
1587
1588     return view->contentsToRootView(renderer->absoluteBoundingBoxRect());
1589 }
1590
1591 IntRect WebPage::absoluteInteractionBoundsForElement(const Element& element)
1592 {
1593     auto* frame = element.document().frame();
1594     if (!frame)
1595         return { };
1596
1597     auto* view = frame->view();
1598     if (!view)
1599         return { };
1600
1601     auto* renderer = element.renderer();
1602     if (!renderer)
1603         return { };
1604
1605     if (is<RenderBox>(*renderer)) {
1606         auto& box = downcast<RenderBox>(*renderer);
1607
1608         FloatRect rect;
1609         // FIXME: want borders or not?
1610         if (box.style().isOverflowVisible())
1611             rect = box.layoutOverflowRect();
1612         else
1613             rect = box.clientBoxRect();
1614         return box.localToAbsoluteQuad(rect).enclosingBoundingBox();
1615     }
1616
1617     auto& style = renderer->style();
1618     FloatRect boundingBox = renderer->absoluteBoundingBoxRect(true /* use transforms*/);
1619     // This is wrong. It's subtracting borders after converting to absolute coords on something that probably doesn't represent a rectangular element.
1620     boundingBox.move(style.borderLeftWidth(), style.borderTopWidth());
1621     boundingBox.setWidth(boundingBox.width() - style.borderLeftWidth() - style.borderRightWidth());
1622     boundingBox.setHeight(boundingBox.height() - style.borderBottomWidth() - style.borderTopWidth());
1623     return enclosingIntRect(boundingBox);
1624 }
1625
1626 IntRect WebPage::rootViewInteractionBoundsForElement(const Element& element)
1627 {
1628     auto* frame = element.document().frame();
1629     if (!frame)
1630         return { };
1631
1632     auto* view = frame->view();
1633     if (!view)
1634         return { };
1635
1636     return view->contentsToRootView(absoluteInteractionBoundsForElement(element));
1637 }
1638
1639 void WebPage::clearSelection()
1640 {
1641     m_startingGestureRange = nullptr;
1642     m_page->focusController().focusedOrMainFrame().selection().clear();
1643 }
1644
1645 void WebPage::dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch touch, const IntPoint& point)
1646 {
1647     auto frame = makeRef(m_page->focusController().focusedOrMainFrame());
1648     if (!frame->selection().selection().isContentEditable())
1649         return;
1650
1651     IntRect focusedElementRect;
1652     if (m_focusedElement)
1653         focusedElementRect = rootViewInteractionBoundsForElement(*m_focusedElement);
1654
1655     if (focusedElementRect.isEmpty())
1656         return;
1657
1658     auto adjustedPoint = point.constrainedBetween(focusedElementRect.minXMinYCorner(), focusedElementRect.maxXMaxYCorner());
1659     auto& eventHandler = m_page->mainFrame().eventHandler();
1660     switch (touch) {
1661     case SelectionTouch::Started:
1662         eventHandler.handleMousePressEvent({ adjustedPoint, adjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, NoTap });
1663         break;
1664     case SelectionTouch::Moved:
1665         eventHandler.dispatchSyntheticMouseMove({ adjustedPoint, adjustedPoint, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, NoTap });
1666         break;
1667     case SelectionTouch::Ended:
1668     case SelectionTouch::EndedMovingForward:
1669     case SelectionTouch::EndedMovingBackward:
1670     case SelectionTouch::EndedNotMoving:
1671         eventHandler.handleMouseReleaseEvent({ adjustedPoint, adjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, NoTap });
1672         break;
1673     }
1674 }
1675
1676 void WebPage::updateSelectionWithTouches(const IntPoint& point, uint32_t touches, bool baseIsStart, CallbackID callbackID)
1677 {
1678     Frame& frame = m_page->focusController().focusedOrMainFrame();
1679     IntPoint pointInDocument = frame.view()->rootViewToContents(point);
1680     VisiblePosition position = frame.visiblePositionForPoint(pointInDocument);
1681     if (position.isNull()) {
1682         send(Messages::WebPageProxy::TouchesCallback(point, touches, 0, callbackID));
1683         return;
1684     }
1685
1686     RefPtr<Range> range;
1687     VisiblePosition result;
1688     SelectionFlags flags = None;
1689
1690     auto selectionTouch = static_cast<SelectionTouch>(touches);
1691     if (shouldDispatchSyntheticMouseEventsWhenModifyingSelection())
1692         dispatchSyntheticMouseEventsForSelectionGesture(selectionTouch, point);
1693
1694     switch (selectionTouch) {
1695     case SelectionTouch::Started:
1696     case SelectionTouch::EndedNotMoving:
1697         break;
1698     
1699     case SelectionTouch::Ended:
1700         if (frame.selection().selection().isContentEditable()) {
1701             result = closestWordBoundaryForPosition(position);
1702             if (result.isNotNull())
1703                 range = Range::create(*frame.document(), result, result);
1704         } else
1705             range = rangeForPointInRootViewCoordinates(frame, point, baseIsStart);
1706         break;
1707
1708     case SelectionTouch::EndedMovingForward:
1709         range = rangeAtWordBoundaryForPosition(&frame, position, baseIsStart, DirectionForward);
1710         break;
1711         
1712     case SelectionTouch::EndedMovingBackward:
1713         range = rangeAtWordBoundaryForPosition(&frame, position, baseIsStart, DirectionBackward);
1714         break;
1715
1716     case SelectionTouch::Moved:
1717         range = rangeForPointInRootViewCoordinates(frame, point, baseIsStart);
1718         break;
1719     }
1720     if (range)
1721         frame.selection().setSelectedRange(range.get(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1722
1723     send(Messages::WebPageProxy::TouchesCallback(point, touches, flags, callbackID));
1724 }
1725
1726 void WebPage::selectWithTwoTouches(const WebCore::IntPoint& from, const WebCore::IntPoint& to, uint32_t gestureType, uint32_t gestureState, CallbackID callbackID)
1727 {
1728     Frame& frame = m_page->focusController().focusedOrMainFrame();
1729     VisiblePosition fromPosition = frame.visiblePositionForPoint(frame.view()->rootViewToContents(from));
1730     VisiblePosition toPosition = frame.visiblePositionForPoint(frame.view()->rootViewToContents(to));
1731     RefPtr<Range> range;
1732     if (fromPosition.isNotNull() && toPosition.isNotNull()) {
1733         if (fromPosition < toPosition)
1734             range = Range::create(*frame.document(), fromPosition, toPosition);
1735         else
1736             range = Range::create(*frame.document(), toPosition, fromPosition);
1737         frame.selection().setSelectedRange(range.get(), fromPosition.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1738     }
1739
1740     // We can use the same callback for the gestures with one point.
1741     send(Messages::WebPageProxy::GestureCallback(from, gestureType, gestureState, 0, callbackID));
1742 }
1743
1744 void WebPage::extendSelection(uint32_t granularity)
1745 {
1746     Frame& frame = m_page->focusController().focusedOrMainFrame();
1747     // For the moment we handle only WordGranularity.
1748     if (granularity != WordGranularity || !frame.selection().isCaret())
1749         return;
1750
1751     VisiblePosition position = frame.selection().selection().start();
1752     auto wordRange = wordRangeFromPosition(position);
1753     if (!wordRange)
1754         return;
1755
1756     IntPoint endLocationForSyntheticMouseEvents;
1757     bool shouldDispatchMouseEvents = shouldDispatchSyntheticMouseEventsWhenModifyingSelection();
1758     if (shouldDispatchMouseEvents) {
1759         auto startLocationForSyntheticMouseEvents = frame.view()->contentsToRootView(VisiblePosition(wordRange->startPosition()).absoluteCaretBounds()).center();
1760         endLocationForSyntheticMouseEvents = frame.view()->contentsToRootView(VisiblePosition(wordRange->endPosition()).absoluteCaretBounds()).center();
1761         dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Started, startLocationForSyntheticMouseEvents);
1762         dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Moved, endLocationForSyntheticMouseEvents);
1763     }
1764
1765     frame.selection().setSelectedRange(wordRange.get(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1766
1767     if (shouldDispatchMouseEvents)
1768         dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Ended, endLocationForSyntheticMouseEvents);
1769 }
1770
1771 void WebPage::platformDidSelectAll()
1772 {
1773     if (!shouldDispatchSyntheticMouseEventsWhenModifyingSelection())
1774         return;
1775
1776     auto frame = makeRef(m_page->focusController().focusedOrMainFrame());
1777     auto startCaretRect = frame->view()->contentsToRootView(VisiblePosition(frame->selection().selection().start()).absoluteCaretBounds());
1778     auto endCaretRect = frame->view()->contentsToRootView(VisiblePosition(frame->selection().selection().end()).absoluteCaretBounds());
1779     dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Started, startCaretRect.center());
1780     dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Moved, endCaretRect.center());
1781     dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Ended, endCaretRect.center());
1782 }
1783
1784 void WebPage::selectWordBackward()
1785 {
1786     Frame& frame = m_page->focusController().focusedOrMainFrame();
1787     if (!frame.selection().isCaret())
1788         return;
1789
1790     VisiblePosition position = frame.selection().selection().start();
1791     VisiblePosition startPosition = positionOfNextBoundaryOfGranularity(position, WordGranularity, DirectionBackward);
1792     if (startPosition.isNotNull() && startPosition != position)
1793         frame.selection().setSelectedRange(Range::create(*frame.document(), startPosition, position).ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1794 }
1795
1796 void WebPage::moveSelectionByOffset(int32_t offset, CallbackID callbackID)
1797 {
1798     Frame& frame = m_page->focusController().focusedOrMainFrame();
1799     
1800     VisiblePosition startPosition = frame.selection().selection().end();
1801     if (startPosition.isNull())
1802         return;
1803     SelectionDirection direction = offset < 0 ? DirectionBackward : DirectionForward;
1804     VisiblePosition position = startPosition;
1805     for (int i = 0; i < abs(offset); ++i) {
1806         position = positionOfNextBoundaryOfGranularity(position, CharacterGranularity, direction);
1807         if (position.isNull())
1808             break;
1809     }
1810     if (position.isNotNull() && startPosition != position)
1811         frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
1812     send(Messages::WebPageProxy::VoidCallback(callbackID));
1813 }
1814     
1815 void WebPage::startAutoscrollAtPosition(const WebCore::FloatPoint& positionInWindow)
1816 {
1817     if (m_focusedElement && m_focusedElement->renderer()) {
1818         m_page->mainFrame().eventHandler().startSelectionAutoscroll(m_focusedElement->renderer(), positionInWindow);
1819         return;
1820     }
1821     
1822     Frame& frame = m_page->focusController().focusedOrMainFrame();
1823     VisibleSelection selection = frame.selection().selection();
1824     if (!selection.isRange())
1825         return;
1826     RefPtr<Range> range = frame.selection().toNormalizedRange();
1827     if (!range)
1828         return;
1829     auto* renderer = range->startContainer().renderer();
1830     if (!renderer)
1831         return;
1832
1833     m_page->mainFrame().eventHandler().startSelectionAutoscroll(renderer, positionInWindow);
1834 }
1835     
1836 void WebPage::cancelAutoscroll()
1837 {
1838     m_page->mainFrame().eventHandler().cancelSelectionAutoscroll();
1839 }
1840
1841 void WebPage::requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<FloatRect>&)>&& reply)
1842 {
1843     auto& frame = m_page->focusController().focusedOrMainFrame();
1844     auto frameView = makeRefPtr(frame.view());
1845     if (!frameView) {
1846         reply({ });
1847         return;
1848     }
1849
1850     auto& selection = frame.selection().selection();
1851     if (selection.isNone()) {
1852         reply({ });
1853         return;
1854     }
1855
1856     auto selectedRange = selection.toNormalizedRange();
1857     if (!selectedRange) {
1858         reply({ });
1859         return;
1860     }
1861
1862     if (!m_focusedElement || !m_focusedElement->renderer() || isTransparentOrFullyClipped(*m_focusedElement)) {
1863         reply({ });
1864         return;
1865     }
1866
1867     float scaleFactor = pageScaleFactor();
1868     const double factorOfContentArea = 0.5;
1869     auto unobscuredContentArea = m_page->mainFrame().view()->unobscuredContentRect().area();
1870     if (unobscuredContentArea.hasOverflowed()) {
1871         reply({ });
1872         return;
1873     }
1874
1875     double contextMenuAreaLimit = factorOfContentArea * scaleFactor * unobscuredContentArea.unsafeGet();
1876
1877     FloatRect selectionBoundsInRootViewCoordinates;
1878     if (selection.isRange())
1879         selectionBoundsInRootViewCoordinates = frameView->contentsToRootView(selectedRange->absoluteBoundingBox());
1880     else
1881         selectionBoundsInRootViewCoordinates = frameView->contentsToRootView(frame.selection().absoluteCaretBounds());
1882
1883     auto centerOfTargetBounds = selectionBoundsInRootViewCoordinates.center();
1884     FloatPoint centerTopInRootViewCoordinates { centerOfTargetBounds.x(), selectionBoundsInRootViewCoordinates.y() };
1885
1886     auto clickableNonEditableNode = [&] (const FloatPoint& locationInRootViewCoordinates) -> Node* {
1887         FloatPoint adjustedPoint;
1888         auto* hitNode = m_page->mainFrame().nodeRespondingToClickEvents(locationInRootViewCoordinates, adjustedPoint);
1889         if (!hitNode || is<HTMLBodyElement>(hitNode) || is<Document>(hitNode) || hitNode->hasEditableStyle())
1890             return nullptr;
1891
1892         return hitNode;
1893     };
1894
1895     // This heuristic attempts to find a list of rects to avoid when showing the callout menu on iOS.
1896     // First, hit-test several points above the bounds of the selection rect in search of clickable nodes that are not editable.
1897     // Secondly, hit-test several points around the edges of the selection rect and exclude any nodes found in the first round of
1898     // hit-testing if these nodes are also reachable by moving outwards from the left, right, or bottom edges of the selection.
1899     // Additionally, exclude any hit-tested nodes that are either very large relative to the size of the root view, or completely
1900     // encompass the selection bounds. The resulting rects are the bounds of these hit-tested nodes in root view coordinates.
1901     HashSet<Ref<Node>> hitTestedNodes;
1902     Vector<FloatRect> rectsToAvoidInRootViewCoordinates;
1903     const Vector<FloatPoint, 5> offsetsForHitTesting {{ -30, -50 }, { 30, -50 }, { -60, -35 }, { 60, -35 }, { 0, -20 }};
1904     for (auto offset : offsetsForHitTesting) {
1905         offset.scale(1 / scaleFactor);
1906         if (auto* hitNode = clickableNonEditableNode(centerTopInRootViewCoordinates + offset))
1907             hitTestedNodes.add(*hitNode);
1908     }
1909
1910     const float marginForHitTestingSurroundingNodes = 80 / scaleFactor;
1911     Vector<FloatPoint, 3> exclusionHitTestLocations {
1912         { selectionBoundsInRootViewCoordinates.x() - marginForHitTestingSurroundingNodes, centerOfTargetBounds.y() },
1913         { centerOfTargetBounds.x(), selectionBoundsInRootViewCoordinates.maxY() + marginForHitTestingSurroundingNodes },
1914         { selectionBoundsInRootViewCoordinates.maxX() + marginForHitTestingSurroundingNodes, centerOfTargetBounds.y() }
1915     };
1916
1917     for (auto& location : exclusionHitTestLocations) {
1918         if (auto* nodeToExclude = clickableNonEditableNode(location))
1919             hitTestedNodes.remove(*nodeToExclude);
1920     }
1921
1922     for (auto& node : hitTestedNodes) {
1923         auto frameView = makeRefPtr(node->document().view());
1924         auto* renderer = node->renderer();
1925         if (!renderer || !frameView)
1926             continue;
1927
1928         auto bounds = frameView->contentsToRootView(renderer->absoluteBoundingBoxRect());
1929         auto area = bounds.area();
1930         if (area.hasOverflowed() || area.unsafeGet() > contextMenuAreaLimit)
1931             continue;
1932
1933         if (bounds.contains(enclosingIntRect(selectionBoundsInRootViewCoordinates)))
1934             continue;
1935
1936         rectsToAvoidInRootViewCoordinates.append(WTFMove(bounds));
1937     }
1938
1939     reply(WTFMove(rectsToAvoidInRootViewCoordinates));
1940 }
1941
1942 void WebPage::getRectsForGranularityWithSelectionOffset(uint32_t granularity, int32_t offset, CallbackID callbackID)
1943 {
1944     Frame& frame = m_page->focusController().focusedOrMainFrame();
1945     VisibleSelection selection = m_storedSelectionForAccessibility.isNone() ? frame.selection().selection() : m_storedSelectionForAccessibility;
1946     VisiblePosition selectionStart = selection.visibleStart();
1947
1948     if (selectionStart.isNull()) {
1949         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1950         return;
1951     }
1952
1953     auto position = visiblePositionForPositionWithOffset(selectionStart, offset);
1954     SelectionDirection direction = offset < 0 ? DirectionBackward : DirectionForward;
1955
1956     auto range = enclosingTextUnitOfGranularity(position, static_cast<WebCore::TextGranularity>(granularity), direction);
1957     if (!range || range->collapsed()) {
1958         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1959         return;
1960     }
1961
1962     Vector<WebCore::SelectionRect> selectionRects;
1963     range->collectSelectionRectsWithoutUnionInteriorLines(selectionRects);
1964     convertSelectionRectsToRootView(frame.view(), selectionRects);
1965     send(Messages::WebPageProxy::SelectionRectsCallback(selectionRects, callbackID));
1966 }
1967
1968 void WebPage::storeSelectionForAccessibility(bool shouldStore)
1969 {
1970     if (!shouldStore)
1971         m_storedSelectionForAccessibility = VisibleSelection();
1972     else {
1973         Frame& frame = m_page->focusController().focusedOrMainFrame();
1974         m_storedSelectionForAccessibility = frame.selection().selection();
1975     }
1976 }
1977
1978 static Optional<SimpleRange> rangeNearPositionMatchesText(const VisiblePosition& position, const String& matchText, RefPtr<Range> selectionRange)
1979 {
1980     if (!selectionRange)
1981         return WTF::nullopt;
1982     auto range = Range::create(selectionRange->ownerDocument(), selectionRange->startPosition(), position.deepEquivalent().parentAnchoredEquivalent());
1983     unsigned targetOffset = TextIterator::rangeLength(range.ptr(), true);
1984     return findClosestPlainText(*selectionRange, matchText, { }, targetOffset);
1985 }
1986
1987 void WebPage::getRectsAtSelectionOffsetWithText(int32_t offset, const String& text, CallbackID callbackID)
1988 {
1989     Frame& frame = m_page->focusController().focusedOrMainFrame();
1990     uint32_t length = text.length();
1991     VisibleSelection selection = m_storedSelectionForAccessibility.isNone() ? frame.selection().selection() : m_storedSelectionForAccessibility;
1992     VisiblePosition selectionStart = selection.visibleStart();
1993     VisiblePosition selectionEnd = selection.visibleEnd();
1994
1995     if (selectionStart.isNull() || selectionEnd.isNull()) {
1996         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1997         return;
1998     }
1999
2000     auto startPosition = visiblePositionForPositionWithOffset(selectionStart, offset);
2001     auto endPosition = visiblePositionForPositionWithOffset(startPosition, length);
2002     auto range = Range::create(*frame.document(), startPosition, endPosition);
2003
2004     if (range->collapsed()) {
2005         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
2006         return;
2007     }
2008
2009     if (plainTextReplacingNoBreakSpace(range.ptr(), TextIteratorDefaultBehavior, true) != text) {
2010         // Try to search for a range which is the closest to the position within the selection range that matches the passed in text.
2011         if (auto wordRange = rangeNearPositionMatchesText(startPosition, text, selection.toNormalizedRange())) {
2012             if (!wordRange->collapsed())
2013                 range = createLiveRange(*wordRange);
2014         }
2015     }
2016
2017     Vector<WebCore::SelectionRect> selectionRects;
2018     range->collectSelectionRectsWithoutUnionInteriorLines(selectionRects);
2019     convertSelectionRectsToRootView(frame.view(), selectionRects);
2020     send(Messages::WebPageProxy::SelectionRectsCallback(selectionRects, callbackID));
2021 }
2022
2023 VisiblePosition WebPage::visiblePositionInFocusedNodeForPoint(const Frame& frame, const IntPoint& point, bool isInteractingWithFocusedElement)
2024 {
2025     IntPoint adjustedPoint(frame.view()->rootViewToContents(point));
2026     IntPoint constrainedPoint = m_focusedElement && isInteractingWithFocusedElement ? constrainPoint(adjustedPoint, frame, *m_focusedElement) : adjustedPoint;
2027     return frame.visiblePositionForPoint(constrainedPoint);
2028 }
2029
2030 void WebPage::selectPositionAtPoint(const WebCore::IntPoint& point, bool isInteractingWithFocusedElement, CallbackID callbackID)
2031 {
2032     SetForScope<bool> userIsInteractingChange { m_userIsInteracting, true };
2033
2034     auto& frame = m_page->focusController().focusedOrMainFrame();
2035     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2036     
2037     if (position.isNotNull())
2038         frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
2039     send(Messages::WebPageProxy::VoidCallback(callbackID));
2040 }
2041
2042 void WebPage::selectPositionAtBoundaryWithDirection(const WebCore::IntPoint& point, uint32_t granularity, uint32_t direction, bool isInteractingWithFocusedElement, CallbackID callbackID)
2043 {
2044     auto& frame = m_page->focusController().focusedOrMainFrame();
2045     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2046
2047     if (position.isNotNull()) {
2048         position = positionOfNextBoundaryOfGranularity(position, static_cast<WebCore::TextGranularity>(granularity), static_cast<SelectionDirection>(direction));
2049         if (position.isNotNull())
2050             frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), UPSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
2051     }
2052     send(Messages::WebPageProxy::VoidCallback(callbackID));
2053 }
2054
2055 void WebPage::moveSelectionAtBoundaryWithDirection(uint32_t granularity, uint32_t direction, CallbackID callbackID)
2056 {
2057     Frame& frame = m_page->focusController().focusedOrMainFrame();
2058     
2059     if (!frame.selection().selection().isNone()) {
2060         bool isForward = (direction == DirectionForward || direction == DirectionRight);
2061         VisiblePosition position = (isForward) ? frame.selection().selection().visibleEnd() : frame.selection().selection().visibleStart();
2062         position = positionOfNextBoundaryOfGranularity(position, static_cast<WebCore::TextGranularity>(granularity), static_cast<SelectionDirection>(direction));
2063         if (position.isNotNull())
2064             frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), isForward? UPSTREAM : DOWNSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
2065     }
2066     send(Messages::WebPageProxy::VoidCallback(callbackID));
2067 }
2068
2069 RefPtr<Range> WebPage::rangeForGranularityAtPoint(Frame& frame, const WebCore::IntPoint& point, uint32_t granularity, bool isInteractingWithFocusedElement)
2070 {
2071     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2072
2073     RefPtr<Range> range;
2074     switch (static_cast<WebCore::TextGranularity>(granularity)) {
2075     case CharacterGranularity:
2076         range = makeRange(position, position);
2077         break;
2078     case WordGranularity:
2079         range = wordRangeFromPosition(position);
2080         break;
2081     case SentenceGranularity:
2082         range = enclosingTextUnitOfGranularity(position, SentenceGranularity, DirectionForward);
2083         break;
2084     case ParagraphGranularity:
2085         range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
2086         break;
2087     case DocumentGranularity:
2088         // FIXME: It's not clear why this mutates the current selection and returns null.
2089         frame.selection().selectAll();
2090         break;
2091     default:
2092         break;
2093     }
2094     return range;
2095 }
2096
2097 static inline bool rectIsTooBigForSelection(const IntRect& blockRect, const Frame& frame)
2098 {
2099     const float factor = 0.97;
2100     return blockRect.height() > frame.view()->unobscuredContentRect().height() * factor;
2101 }
2102
2103 void WebPage::setFocusedFrameBeforeSelectingTextAtLocation(const IntPoint& point)
2104 {
2105     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::DisallowUserAgentShadowContent, HitTestRequest::AllowVisibleChildFrameContentOnly };
2106     auto result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(point, hitType);
2107     auto* hitNode = result.innerNode();
2108     if (hitNode && hitNode->renderer())
2109         m_page->focusController().setFocusedFrame(result.innerNodeFrame());
2110 }
2111
2112 void WebPage::selectTextWithGranularityAtPoint(const WebCore::IntPoint& point, uint32_t granularity, bool isInteractingWithFocusedElement, CallbackID callbackID)
2113 {
2114     setFocusedFrameBeforeSelectingTextAtLocation(point);
2115
2116     auto& frame = m_page->focusController().focusedOrMainFrame();
2117     auto range = rangeForGranularityAtPoint(frame, point, granularity, isInteractingWithFocusedElement);
2118     if (range)
2119         frame.selection().setSelectedRange(range.get(), UPSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
2120     m_initialSelection = range;
2121     send(Messages::WebPageProxy::VoidCallback(callbackID));
2122 }
2123
2124 void WebPage::beginSelectionInDirection(uint32_t direction, CallbackID callbackID)
2125 {
2126     m_selectionAnchor = (static_cast<SelectionDirection>(direction) == DirectionLeft) ? Start : End;
2127     send(Messages::WebPageProxy::UnsignedCallback(m_selectionAnchor == Start, callbackID));
2128 }
2129
2130 void WebPage::updateSelectionWithExtentPointAndBoundary(const WebCore::IntPoint& point, uint32_t granularity, bool isInteractingWithFocusedElement, CallbackID callbackID)
2131 {
2132     auto& frame = m_page->focusController().focusedOrMainFrame();
2133     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2134     RefPtr<Range> newRange = rangeForGranularityAtPoint(frame, point, granularity, isInteractingWithFocusedElement);
2135     
2136     if (position.isNull() || !m_initialSelection || !newRange) {
2137         send(Messages::WebPageProxy::UnsignedCallback(false, callbackID));
2138         return;
2139     }
2140     
2141     RefPtr<Range> range;
2142     VisiblePosition selectionStart = m_initialSelection->startPosition();
2143     VisiblePosition selectionEnd = m_initialSelection->endPosition();
2144
2145     if (position > m_initialSelection->endPosition())
2146         selectionEnd = newRange->endPosition();
2147     else if (position < m_initialSelection->startPosition())
2148         selectionStart = newRange->startPosition();
2149     
2150     if (selectionStart.isNotNull() && selectionEnd.isNotNull())
2151         range = Range::create(*frame.document(), selectionStart, selectionEnd);
2152     
2153     if (range)
2154         frame.selection().setSelectedRange(range.get(), UPSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
2155     
2156     send(Messages::WebPageProxy::UnsignedCallback(selectionStart == m_initialSelection->startPosition(), callbackID));
2157 }
2158
2159 void WebPage::updateSelectionWithExtentPoint(const WebCore::IntPoint& point, bool isInteractingWithFocusedElement, RespectSelectionAnchor respectSelectionAnchor, CallbackID callbackID)
2160 {
2161     auto& frame = m_page->focusController().focusedOrMainFrame();
2162     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2163
2164     if (position.isNull()) {
2165         send(Messages::WebPageProxy::UnsignedCallback(false, callbackID));
2166         return;
2167     }
2168
2169     RefPtr<Range> range;
2170     VisiblePosition selectionStart;
2171     VisiblePosition selectionEnd;
2172     
2173     if (respectSelectionAnchor == RespectSelectionAnchor::Yes) {
2174         if (m_selectionAnchor == Start) {
2175             selectionStart = frame.selection().selection().visibleStart();
2176             selectionEnd = position;
2177             if (position <= selectionStart) {
2178                 selectionStart = selectionStart.previous();
2179                 selectionEnd = frame.selection().selection().visibleEnd();
2180                 m_selectionAnchor = End;
2181             }
2182         } else {
2183             selectionStart = position;
2184             selectionEnd = frame.selection().selection().visibleEnd();
2185             if (position >= selectionEnd) {
2186                 selectionStart = frame.selection().selection().visibleStart();
2187                 selectionEnd = selectionEnd.next();
2188                 m_selectionAnchor = Start;
2189             }
2190         }
2191     } else {
2192         auto currentStart = frame.selection().selection().visibleStart();
2193         auto currentEnd = frame.selection().selection().visibleEnd();
2194         if (position <= currentStart) {
2195             selectionStart = position;
2196             selectionEnd = currentEnd;
2197         } else if (position >= currentEnd) {
2198             selectionStart = currentStart;
2199             selectionEnd = position;
2200         }
2201     }
2202     
2203     if (selectionStart.isNotNull() && selectionEnd.isNotNull())
2204         range = Range::create(*frame.document(), selectionStart, selectionEnd);
2205
2206     if (range)
2207         frame.selection().setSelectedRange(range.get(), UPSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
2208
2209     send(Messages::WebPageProxy::UnsignedCallback(m_selectionAnchor == Start, callbackID));
2210 }
2211
2212 void WebPage::convertSelectionRectsToRootView(FrameView* view, Vector<SelectionRect>& selectionRects)
2213 {
2214     for (size_t i = 0; i < selectionRects.size(); ++i) {
2215         SelectionRect& currentRect = selectionRects[i];
2216         currentRect.setRect(view->contentsToRootView(currentRect.rect()));
2217     }
2218 }
2219
2220 void WebPage::requestDictationContext(CallbackID callbackID)
2221 {
2222     Frame& frame = m_page->focusController().focusedOrMainFrame();
2223     VisiblePosition startPosition = frame.selection().selection().start();
2224     VisiblePosition endPosition = frame.selection().selection().end();
2225     const unsigned dictationContextWordCount = 5;
2226
2227     String selectedText;
2228     if (frame.selection().isRange())
2229         selectedText = plainTextReplacingNoBreakSpace(frame.selection().selection().toNormalizedRange().get());
2230
2231     String contextBefore;
2232     if (startPosition != startOfEditableContent(startPosition)) {
2233         VisiblePosition currentPosition = startPosition;
2234         VisiblePosition lastPosition = startPosition;
2235         for (unsigned i = 0; i < dictationContextWordCount; ++i) {
2236             currentPosition = startOfWord(positionOfNextBoundaryOfGranularity(lastPosition, WordGranularity, DirectionBackward));
2237             if (currentPosition.isNull())
2238                 break;
2239             lastPosition = currentPosition;
2240         }
2241         if (lastPosition.isNotNull() && lastPosition != startPosition)
2242             contextBefore = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), lastPosition, startPosition).ptr());
2243     }
2244
2245     String contextAfter;
2246     if (endPosition != endOfEditableContent(endPosition)) {
2247         VisiblePosition currentPosition = endPosition;
2248         VisiblePosition lastPosition = endPosition;
2249         for (unsigned i = 0; i < dictationContextWordCount; ++i) {
2250             currentPosition = endOfWord(positionOfNextBoundaryOfGranularity(lastPosition, WordGranularity, DirectionForward));
2251             if (currentPosition.isNull())
2252                 break;
2253             lastPosition = currentPosition;
2254         }
2255         if (lastPosition.isNotNull() && lastPosition != endPosition)
2256             contextAfter = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), endPosition, lastPosition).ptr());
2257     }
2258
2259     send(Messages::WebPageProxy::SelectionContextCallback(selectedText, contextBefore, contextAfter, callbackID));
2260 }
2261
2262 void WebPage::replaceSelectedText(const String& oldText, const String& newText)
2263 {
2264     Frame& frame = m_page->focusController().focusedOrMainFrame();
2265     RefPtr<Range> wordRange = frame.selection().isCaret() ? wordRangeFromPosition(frame.selection().selection().start()) : frame.selection().toNormalizedRange();
2266     if (plainTextReplacingNoBreakSpace(wordRange.get()) != oldText)
2267         return;
2268     
2269     frame.editor().setIgnoreSelectionChanges(true);
2270     frame.selection().setSelectedRange(wordRange.get(), UPSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes);
2271     frame.editor().insertText(newText, 0);
2272     frame.editor().setIgnoreSelectionChanges(false);
2273 }
2274
2275 void WebPage::replaceDictatedText(const String& oldText, const String& newText)
2276 {
2277     Frame& frame = m_page->focusController().focusedOrMainFrame();
2278     if (frame.selection().isNone())
2279         return;
2280     
2281     if (frame.selection().isRange()) {
2282         frame.editor().deleteSelectionWithSmartDelete(false);
2283         return;
2284     }
2285     VisiblePosition position = frame.selection().selection().start();
2286     for (size_t i = 0; i < oldText.length(); ++i)
2287         position = position.previous();
2288     if (position.isNull())
2289         position = startOfDocument(static_cast<Node*>(frame.document()->documentElement()));
2290     auto range = Range::create(*frame.document(), position, frame.selection().selection().start());
2291
2292     if (plainTextReplacingNoBreakSpace(range.ptr()) != oldText)
2293         return;
2294
2295     // We don't want to notify the client that the selection has changed until we are done inserting the new text.
2296     frame.editor().setIgnoreSelectionChanges(true);
2297     frame.selection().setSelectedRange(range.ptr(), UPSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes);
2298     frame.editor().insertText(newText, 0);
2299     frame.editor().setIgnoreSelectionChanges(false);
2300 }
2301
2302 void WebPage::requestAutocorrectionData(const String& textForAutocorrection, CompletionHandler<void(WebAutocorrectionData)>&& reply)
2303 {
2304     auto& frame = m_page->focusController().focusedOrMainFrame();
2305     if (!frame.selection().isCaret()) {
2306         reply({ });
2307         return;
2308     }
2309
2310     VisiblePosition position = frame.selection().selection().start();
2311     auto range = wordRangeFromPosition(position);
2312     if (!range) {
2313         reply({ });
2314         return;
2315     }
2316
2317     auto textForRange = plainTextReplacingNoBreakSpace(range.get());
2318     const unsigned maxSearchAttempts = 5;
2319     for (size_t i = 0;  i < maxSearchAttempts && textForRange != textForAutocorrection; ++i)
2320     {
2321         position = range->startPosition().previous();
2322         if (position.isNull() || position == range->startPosition())
2323             break;
2324         range = Range::create(*frame.document(), wordRangeFromPosition(position)->startPosition(), range->endPosition());
2325         textForRange = plainTextReplacingNoBreakSpace(range.get());
2326     }
2327
2328     Vector<SelectionRect> selectionRects;
2329     if (textForRange == textForAutocorrection)
2330         range->collectSelectionRects(selectionRects);
2331
2332     Vector<FloatRect> rectsForText;
2333     rectsForText.grow(selectionRects.size());
2334
2335     convertSelectionRectsToRootView(frame.view(), selectionRects);
2336     for (size_t i = 0; i < selectionRects.size(); i++)
2337         rectsForText[i] = selectionRects[i].rect();
2338
2339     bool multipleFonts = false;
2340     CTFontRef font = nil;
2341     if (auto* coreFont = frame.editor().fontForSelection(multipleFonts))
2342         font = coreFont->getCTFont();
2343
2344     reply({ WTFMove(rectsForText), (__bridge UIFont *)font });
2345 }
2346
2347 void WebPage::applyAutocorrection(const String& correction, const String& originalText, CallbackID callbackID)
2348 {
2349     send(Messages::WebPageProxy::StringCallback(applyAutocorrectionInternal(correction, originalText) ? correction : String(), callbackID));
2350 }
2351
2352 Seconds WebPage::eventThrottlingDelay() const
2353 {
2354     auto behaviorOverride = m_page->eventThrottlingBehaviorOverride();
2355     if (behaviorOverride) {
2356         switch (behaviorOverride.value()) {
2357         case EventThrottlingBehavior::Responsive:
2358             return 0_s;
2359         case EventThrottlingBehavior::Unresponsive:
2360             return 1_s;
2361         }
2362     }
2363
2364     if (m_isInStableState || m_estimatedLatency <= Seconds(1.0 / 60))
2365         return 0_s;
2366
2367     return std::min(m_estimatedLatency * 2, 1_s);
2368 }
2369
2370 void WebPage::syncApplyAutocorrection(const String& correction, const String& originalText, CompletionHandler<void(bool)>&& reply)
2371 {
2372     reply(applyAutocorrectionInternal(correction, originalText));
2373 }
2374
2375 bool WebPage::applyAutocorrectionInternal(const String& correction, const String& originalText)
2376 {
2377     auto& frame = m_page->focusController().focusedOrMainFrame();
2378     if (!frame.selection().isCaretOrRange())
2379         return false;
2380
2381     RefPtr<Range> range;
2382     String textForRange;
2383     auto originalTextWithFoldedQuoteMarks = foldQuoteMarks(originalText);
2384
2385     if (frame.selection().isCaret()) {
2386         VisiblePosition position = frame.selection().selection().start();
2387         range = wordRangeFromPosition(position);
2388         textForRange = plainTextReplacingNoBreakSpace(range.get());
2389         
2390         // If 'originalText' is not the same as 'textForRange' we need to move 'range'
2391         // forward such that it matches the original selection as much as possible.
2392         if (foldQuoteMarks(textForRange) != originalTextWithFoldedQuoteMarks) {
2393             // Search for the original text before the selection caret.
2394             for (size_t i = 0; i < originalText.length(); ++i)
2395                 position = position.previous();
2396             if (position.isNull())
2397                 position = startOfDocument(static_cast<Node*>(frame.document()->documentElement()));
2398             range = Range::create(*frame.document(), position, frame.selection().selection().start());
2399             textForRange = plainTextReplacingNoBreakSpace(range.get());
2400             unsigned loopCount = 0;
2401             const unsigned maxPositionsAttempts = 10;
2402             while (textForRange.length() && textForRange.length() > originalText.length() && loopCount < maxPositionsAttempts) {
2403                 position = position.next();
2404                 if (position.isNotNull() && position >= frame.selection().selection().start())
2405                     range = nullptr;
2406                 else
2407                     range = Range::create(*frame.document(), position, frame.selection().selection().start());
2408                 textForRange = plainTextReplacingNoBreakSpace(range.get());
2409                 loopCount++;
2410             }
2411         } else if (textForRange.isEmpty() && range && !range->collapsed()) {
2412             // If 'range' does not include any text but it is not collapsed, we need to set
2413             // 'range' to match the selection. Otherwise non-text nodes will be removed.
2414             range = Range::create(*frame.document(), position, position);
2415             if (!range)
2416                 return false;
2417         }
2418     } else {
2419         // Range selection.
2420         range = frame.selection().toNormalizedRange();
2421         if (!range)
2422             return false;
2423
2424         textForRange = plainTextReplacingNoBreakSpace(range.get());
2425     }
2426
2427     if (foldQuoteMarks(textForRange) != originalTextWithFoldedQuoteMarks)
2428         return false;
2429     
2430     // Correctly determine affinity, using logic currently only present in VisiblePosition
2431     EAffinity affinity = DOWNSTREAM;
2432     if (range && range->collapsed())
2433         affinity = VisiblePosition(range->startPosition(), UPSTREAM).affinity();
2434     
2435     frame.selection().setSelectedRange(range.get(), affinity, WebCore::FrameSelection::ShouldCloseTyping::Yes);
2436     if (correction.length())
2437         frame.editor().insertText(correction, 0, originalText.isEmpty() ? TextEventInputKeyboard : TextEventInputAutocompletion);
2438     else if (originalText.length())
2439         frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, false, true);
2440     return true;
2441 }
2442
2443 WebAutocorrectionContext WebPage::autocorrectionContext()
2444 {
2445     String contextBefore;
2446     String markedText;
2447     String selectedText;
2448     String contextAfter;
2449     EditingRange markedTextRange;
2450
2451     auto& frame = m_page->focusController().focusedOrMainFrame();
2452     RefPtr<Range> range;
2453     VisiblePosition startPosition = frame.selection().selection().start();
2454     VisiblePosition endPosition = frame.selection().selection().end();
2455     const unsigned minContextWordCount = 3;
2456     const unsigned minContextLenght = 12;
2457     const unsigned maxContextLength = 30;
2458
2459     if (frame.selection().isRange())
2460         selectedText = plainTextReplacingNoBreakSpace(frame.selection().selection().toNormalizedRange().get());
2461
2462     if (auto compositionRange = frame.editor().compositionRange()) {
2463         range = Range::create(*frame.document(), compositionRange->startPosition(), startPosition);
2464         String markedTextBefore;
2465         if (range)
2466             markedTextBefore = plainTextReplacingNoBreakSpace(range.get());
2467         range = Range::create(*frame.document(), endPosition, compositionRange->endPosition());
2468         String markedTextAfter;
2469         if (range)
2470             markedTextAfter = plainTextReplacingNoBreakSpace(range.get());
2471         markedText = markedTextBefore + selectedText + markedTextAfter;
2472         if (!markedText.isEmpty()) {
2473             markedTextRange.location = markedTextBefore.length();
2474             markedTextRange.length = selectedText.length();
2475         }
2476     } else {
2477         if (startPosition != startOfEditableContent(startPosition)) {
2478             VisiblePosition currentPosition = startPosition;
2479             VisiblePosition previousPosition;
2480             unsigned totalContextLength = 0;
2481             for (unsigned i = 0; i < minContextWordCount; ++i) {
2482                 if (contextBefore.length() >= minContextLenght)
2483                     break;
2484                 previousPosition = startOfWord(positionOfNextBoundaryOfGranularity(currentPosition, WordGranularity, DirectionBackward));
2485                 if (previousPosition.isNull())
2486                     break;
2487                 String currentWord = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), previousPosition, currentPosition).ptr());
2488                 totalContextLength += currentWord.length();
2489                 if (totalContextLength >= maxContextLength)
2490                     break;
2491                 currentPosition = previousPosition;
2492             }
2493             if (currentPosition.isNotNull() && currentPosition != startPosition) {
2494                 contextBefore = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), currentPosition, startPosition).ptr());
2495                 if (atBoundaryOfGranularity(currentPosition, ParagraphGranularity, DirectionBackward))
2496                     contextBefore = makeString("\n "_s, contextBefore);
2497             }
2498         }
2499
2500         if (endPosition != endOfEditableContent(endPosition)) {
2501             VisiblePosition nextPosition;
2502             if (!atBoundaryOfGranularity(endPosition, WordGranularity, DirectionForward) && withinTextUnitOfGranularity(endPosition, WordGranularity, DirectionForward))
2503                 nextPosition = positionOfNextBoundaryOfGranularity(endPosition, WordGranularity, DirectionForward);
2504             if (nextPosition.isNotNull())
2505                 contextAfter = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), endPosition, nextPosition).ptr());
2506         }
2507     }
2508
2509     WebAutocorrectionContext correction;
2510     correction.contextBefore = WTFMove(contextBefore);
2511     correction.markedText = WTFMove(markedText);
2512     correction.selectedText = WTFMove(selectedText);
2513     correction.contextAfter = WTFMove(contextAfter);
2514     correction.markedTextRange = WTFMove(markedTextRange);
2515     return correction;
2516 }
2517
2518 void WebPage::requestAutocorrectionContext()
2519 {
2520     send(Messages::WebPageProxy::HandleAutocorrectionContext(autocorrectionContext()));
2521 }
2522
2523 static HTMLAnchorElement* containingLinkAnchorElement(Element& element)
2524 {
2525     // FIXME: There is code in the drag controller that supports any link, even if it's not an HTMLAnchorElement. Why is this different?
2526     for (auto& currentElement : lineageOfType<HTMLAnchorElement>(element)) {
2527         if (currentElement.isLink())
2528             return &currentElement;
2529     }
2530     return nullptr;
2531 }
2532
2533 static inline bool isAssistableElement(Element& element)
2534 {
2535     if (is<HTMLSelectElement>(element))
2536         return true;
2537     if (is<HTMLTextAreaElement>(element))
2538         return true;
2539     if (is<HTMLImageElement>(element) && downcast<HTMLImageElement>(element).hasEditableImageAttribute())
2540         return true;
2541     if (is<HTMLInputElement>(element)) {
2542         HTMLInputElement& inputElement = downcast<HTMLInputElement>(element);
2543         // FIXME: This laundry list of types is not a good way to factor this. Need a suitable function on HTMLInputElement itself.
2544 #if ENABLE(INPUT_TYPE_COLOR)
2545         if (inputElement.isColorControl())
2546             return true;
2547 #endif
2548         return inputElement.isTextField() || inputElement.isDateField() || inputElement.isDateTimeLocalField() || inputElement.isMonthField() || inputElement.isTimeField();
2549     }
2550     if (is<HTMLIFrameElement>(element))
2551         return false;
2552     return element.isContentEditable();
2553 }
2554
2555 void WebPage::getPositionInformation(const InteractionInformationRequest& request, CompletionHandler<void(InteractionInformationAtPosition&&)>&& reply)
2556 {
2557     // Avoid UIProcess hangs when the WebContent process is stuck on a sync IPC.
2558     if (IPC::UnboundedSynchronousIPCScope::hasOngoingUnboundedSyncIPC()) {
2559         RELEASE_LOG_ERROR_IF_ALLOWED(Process, "getPositionInformation - Not processing because the process is stuck on unbounded sync IPC");
2560         return reply({ });
2561     }
2562
2563     m_pendingSynchronousPositionInformationReply = WTFMove(reply);
2564
2565     auto information = positionInformation(request);
2566
2567     if (auto reply = WTFMove(m_pendingSynchronousPositionInformationReply))
2568         reply(WTFMove(information));
2569 }
2570     
2571 static void focusedElementPositionInformation(WebPage& page, Element& focusedElement, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2572 {
2573     const Frame& frame = page.corePage()->focusController().focusedOrMainFrame();
2574     if (!frame.editor().hasComposition())
2575         return;
2576
2577     const uint32_t kHitAreaWidth = 66;
2578     const uint32_t kHitAreaHeight = 66;
2579     FrameView& view = *frame.view();
2580     IntPoint adjustedPoint(view.rootViewToContents(request.point));
2581     IntPoint constrainedPoint = constrainPoint(adjustedPoint, frame, focusedElement);
2582     VisiblePosition position = frame.visiblePositionForPoint(constrainedPoint);
2583
2584     RefPtr<Range> compositionRange = frame.editor().compositionRange();
2585     if (!compositionRange)
2586         return;
2587
2588     if (position < compositionRange->startPosition())
2589         position = compositionRange->startPosition();
2590     else if (position > compositionRange->endPosition())
2591         position = compositionRange->endPosition();
2592     IntRect caretRect = view.contentsToRootView(position.absoluteCaretBounds());
2593     float deltaX = abs(caretRect.x() + (caretRect.width() / 2) - request.point.x());
2594     float deltaYFromTheTop = abs(caretRect.y() - request.point.y());
2595     float deltaYFromTheBottom = abs(caretRect.y() + caretRect.height() - request.point.y());
2596
2597     info.isNearMarkedText = !(deltaX > kHitAreaWidth || deltaYFromTheTop > kHitAreaHeight || deltaYFromTheBottom > kHitAreaHeight);
2598 }
2599
2600 static void linkIndicatorPositionInformation(WebPage& page, Element& linkElement, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2601 {
2602     if (!request.includeLinkIndicator)
2603         return;
2604
2605     auto linkRange = rangeOfContents(linkElement);
2606     float deviceScaleFactor = page.corePage()->deviceScaleFactor();
2607     const float marginInPoints = request.linkIndicatorShouldHaveLegacyMargins ? 4 : 0;
2608
2609     auto textIndicator = TextIndicator::createWithRange(linkRange.get(),
2610         TextIndicatorOptionTightlyFitContent | TextIndicatorOptionRespectTextColor | TextIndicatorOptionPaintBackgrounds |
2611         TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges | TextIndicatorOptionIncludeMarginIfRangeMatchesSelection | TextIndicatorOptionComputeEstimatedBackgroundColor,
2612         TextIndicatorPresentationTransition::None, FloatSize(marginInPoints * deviceScaleFactor, marginInPoints * deviceScaleFactor));
2613         
2614     if (textIndicator)
2615         info.linkIndicator = textIndicator->data();
2616 }
2617     
2618 #if ENABLE(DATA_DETECTION)
2619 static void dataDetectorLinkPositionInformation(Element& element, InteractionInformationAtPosition& info)
2620 {
2621     if (!DataDetection::isDataDetectorLink(element))
2622         return;
2623     
2624     info.isDataDetectorLink = true;
2625     const int dataDetectionExtendedContextLength = 350;
2626     info.dataDetectorIdentifier = DataDetection::dataDetectorIdentifier(element);
2627     info.dataDetectorResults = element.document().frame()->dataDetectionResults();
2628
2629     if (!DataDetection::requiresExtendedContext(element))
2630         return;
2631     
2632     auto linkRange = Range::create(element.document());
2633     linkRange->selectNodeContents(element);
2634     info.textBefore = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(linkRange->startPosition(),
2635         dataDetectionExtendedContextLength, DirectionBackward).get(), TextIteratorDefaultBehavior, true);
2636     info.textAfter = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(linkRange->endPosition(),
2637         dataDetectionExtendedContextLength, DirectionForward).get(), TextIteratorDefaultBehavior, true);
2638 }
2639 #endif
2640
2641 static void imagePositionInformation(WebPage& page, Element& element, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2642 {
2643     auto& renderImage = downcast<RenderImage>(*(element.renderer()));
2644     if (!renderImage.cachedImage() || renderImage.cachedImage()->errorOccurred())
2645         return;
2646
2647     auto* image = renderImage.cachedImage()->imageForRenderer(&renderImage);
2648     if (!image || image->width() <= 1 || image->height() <= 1)
2649         return;
2650
2651     info.isImage = true;
2652     info.imageURL = element.document().completeURL(renderImage.cachedImage()->url());
2653     info.isAnimatedImage = image->isAnimated();
2654
2655     if (!request.includeSnapshot)
2656         return;
2657
2658     FloatSize screenSizeInPixels = screenSize();
2659     FloatSize imageSize = renderImage.cachedImage()->imageSizeForRenderer(&renderImage);
2660     
2661     screenSizeInPixels.scale(page.corePage()->deviceScaleFactor());
2662     FloatSize scaledSize = largestRectWithAspectRatioInsideRect(imageSize.width() / imageSize.height(), FloatRect(0, 0, screenSizeInPixels.width(), screenSizeInPixels.height())).size();
2663     FloatSize bitmapSize = scaledSize.width() < imageSize.width() ? scaledSize : imageSize;
2664     
2665     // FIXME: Only select ExtendedColor on images known to need wide gamut
2666     ShareableBitmap::Configuration bitmapConfiguration;
2667     bitmapConfiguration.colorSpace.cgColorSpace = screenColorSpace(page.corePage()->mainFrame().view());
2668
2669     auto sharedBitmap = ShareableBitmap::createShareable(IntSize(bitmapSize), bitmapConfiguration);
2670     if (!sharedBitmap)
2671         return;
2672
2673     auto graphicsContext = sharedBitmap->createGraphicsContext();
2674     if (!graphicsContext)
2675         return;
2676
2677     graphicsContext->drawImage(*image, FloatRect(0, 0, bitmapSize.width(), bitmapSize.height()), { renderImage.imageOrientation() });
2678     info.image = sharedBitmap;
2679 }
2680
2681 static void boundsPositionInformation(RenderElement& renderer, InteractionInformationAtPosition& info)
2682 {
2683     if (renderer.isRenderImage())
2684         info.bounds = downcast<RenderImage>(renderer).absoluteContentQuad().enclosingBoundingBox();
2685     else
2686         info.bounds = renderer.absoluteBoundingBoxRect();
2687
2688     if (!renderer.document().frame()->isMainFrame()) {
2689         FrameView *view = renderer.document().frame()->view();
2690         info.bounds = view->contentsToRootView(info.bounds);
2691     }
2692 }
2693
2694 static void elementPositionInformation(WebPage& page, Element& element, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2695 {
2696     Element* linkElement = nullptr;
2697     if (element.renderer() && element.renderer()->isRenderImage())
2698         linkElement = containingLinkAnchorElement(element);
2699     else if (element.isLink())
2700         linkElement = &element;
2701
2702     info.isElement = true;
2703     info.idAttribute = element.getIdAttribute();
2704
2705     info.title = element.attributeWithoutSynchronization(HTMLNames::titleAttr).string();
2706     if (linkElement && info.title.isEmpty())
2707         info.title = element.innerText();
2708     if (element.renderer())
2709         info.touchCalloutEnabled = element.renderer()->style().touchCalloutEnabled();
2710
2711     if (linkElement) {
2712         info.isLink = true;
2713         info.url = linkElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(linkElement->getAttribute(HTMLNames::hrefAttr)));
2714
2715         linkIndicatorPositionInformation(page, *linkElement, request, info);
2716 #if ENABLE(DATA_DETECTION)
2717         dataDetectorLinkPositionInformation(element, info);
2718 #endif
2719     }
2720
2721     auto* elementForScrollTesting = linkElement ? linkElement : &element;
2722     if (auto* renderer = elementForScrollTesting->renderer()) {
2723 #if ENABLE(ASYNC_SCROLLING)
2724         if (auto* scrollingCoordinator = page.scrollingCoordinator())
2725             info.containerScrollingNodeID = scrollingCoordinator->scrollableContainerNodeID(*renderer);
2726 #endif
2727     }
2728
2729     if (auto* renderer = element.renderer()) {
2730         if (renderer->isRenderImage())
2731             imagePositionInformation(page, element, request, info);
2732         boundsPositionInformation(*renderer, info);
2733     }
2734
2735     info.elementContext = page.contextForElement(element);
2736 }
2737     
2738 static void selectionPositionInformation(WebPage& page, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2739 {
2740     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::DisallowUserAgentShadowContent, HitTestRequest::AllowVisibleChildFrameContentOnly };
2741     HitTestResult result = page.corePage()->mainFrame().eventHandler().hitTestResultAtPoint(request.point, hitType);
2742     Node* hitNode = result.innerNode();
2743
2744     // Hit test could return HTMLHtmlElement that has no renderer, if the body is smaller than the document.
2745     if (!hitNode || !hitNode->renderer())
2746         return;
2747
2748     RenderObject* renderer = hitNode->renderer();
2749     info.bounds = renderer->absoluteBoundingBoxRect(true);
2750     if (is<HTMLAttachmentElement>(*hitNode)) {
2751         info.isAttachment = true;
2752         HTMLAttachmentElement& attachment = downcast<HTMLAttachmentElement>(*hitNode);
2753         info.title = attachment.attachmentTitle();
2754         linkIndicatorPositionInformation(page, attachment, request, info);
2755         if (attachment.file())
2756             info.url = URL::fileURLWithFileSystemPath(downcast<HTMLAttachmentElement>(*hitNode).file()->path());
2757     } else {
2758         info.isSelectable = renderer->style().userSelect() != UserSelect::None;
2759         // We don't want to select blocks that are larger than 97% of the visible area of the document.
2760         // FIXME: Is this heuristic still needed, now that block selection has been removed?
2761         if (info.isSelectable && !hitNode->isTextNode())
2762             info.isSelectable = !isAssistableElement(*downcast<Element>(hitNode)) && !rectIsTooBigForSelection(info.bounds, *result.innerNodeFrame());
2763     }
2764     for (auto currentNode = makeRefPtr(hitNode); currentNode; currentNode = currentNode->parentOrShadowHostNode()) {
2765         auto* renderer = currentNode->renderer();
2766         if (!renderer)
2767             continue;
2768
2769         auto& style = renderer->style();
2770         if (style.userSelect() == UserSelect::None && style.userDrag() == UserDrag::Element) {
2771             info.prefersDraggingOverTextSelection = true;
2772             break;
2773         }
2774     }
2775 #if PLATFORM(MACCATALYST)
2776     bool isInsideFixedPosition;
2777     VisiblePosition caretPosition(renderer->positionForPoint(request.point, nullptr));
2778     info.caretRect = caretPosition.absoluteCaretBounds(&isInsideFixedPosition);
2779 #endif
2780 }
2781
2782 #if ENABLE(DATALIST_ELEMENT)
2783 static void textInteractionPositionInformation(WebPage& page, const HTMLInputElement& input, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2784 {
2785     if (!input.list())
2786         return;
2787
2788     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::AllowVisibleChildFrameContentOnly };
2789     HitTestResult result = page.corePage()->mainFrame().eventHandler().hitTestResultAtPoint(request.point, hitType);
2790     if (result.innerNode() == input.dataListButtonElement())
2791         info.preventTextInteraction = true;
2792 }
2793 #endif
2794
2795 RefPtr<ShareableBitmap> WebPage::shareableBitmapSnapshotForNode(Element& element)
2796 {
2797     // Ensure that the image contains at most 600K pixels, so that it is not too big.
2798     if (RefPtr<WebImage> snapshot = snapshotNode(element, SnapshotOptionsShareable, 600 * 1024))
2799         return &snapshot->bitmap();
2800     return nullptr;
2801 }
2802
2803 static bool canForceCaretForPosition(const VisiblePosition& position)
2804 {
2805     auto* node = position.deepEquivalent().anchorNode();
2806     if (!node)
2807         return false;
2808
2809     auto* renderer = node->renderer();
2810     auto* style = renderer ? &renderer->style() : nullptr;
2811     auto cursorType = style ? style->cursor() : CursorType::Auto;
2812
2813     if (cursorType == CursorType::Text)
2814         return true;
2815
2816     if (cursorType != CursorType::Auto)
2817         return false;
2818
2819     if (node->hasEditableStyle())
2820         return true;
2821
2822     if (!renderer)
2823         return false;
2824
2825     return renderer->isText() && node->canStartSelection();
2826 }
2827
2828 static void populateCaretContext(const HitTestResult& hitTestResult, const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2829 {
2830     auto frame = makeRefPtr(hitTestResult.innerNodeFrame());
2831     if (!frame)
2832         return;
2833
2834     auto view = makeRefPtr(frame->view());
2835     if (!view)
2836         return;
2837
2838     auto node = hitTestResult.innerNode();
2839     if (!node)
2840         return;
2841
2842     auto* renderer = node->renderer();
2843     if (!renderer)
2844         return;
2845
2846     while (renderer && !is<RenderBlockFlow>(*renderer))
2847         renderer = renderer->parent();
2848
2849     if (!renderer)
2850         return;
2851
2852     // FIXME: We should be able to retrieve this geometry information without
2853     // forcing the text to fall out of Simple Line Layout.
2854     auto& blockFlow = downcast<RenderBlockFlow>(*renderer);
2855     auto position = frame->visiblePositionForPoint(view->rootViewToContents(request.point));
2856     auto lineRect = position.absoluteSelectionBoundsForLine();
2857     bool isEditable = node->hasEditableStyle();
2858
2859     if (isEditable)
2860         lineRect.setWidth(blockFlow.contentWidth());
2861
2862     info.lineCaretExtent = view->contentsToRootView(lineRect);
2863     info.caretHeight = info.lineCaretExtent.height();
2864
2865     bool lineContainsRequestPoint = info.lineCaretExtent.contains(request.point);
2866     // Force an I-beam cursor if the page didn't request a hand, and we're inside the bounds of the line.
2867     if (lineContainsRequestPoint && info.cursor->type() != Cursor::Hand && canForceCaretForPosition(position))
2868         info.cursor = Cursor::fromType(Cursor::IBeam);
2869
2870     if (!lineContainsRequestPoint && info.cursor->type() == Cursor::IBeam) {
2871         auto approximateLineRectInContentCoordinates = renderer->absoluteBoundingBoxRect();
2872         approximateLineRectInContentCoordinates.setHeight(renderer->style().computedLineHeight());
2873         info.lineCaretExtent = view->contentsToRootView(approximateLineRectInContentCoordinates);
2874         if (!info.lineCaretExtent.contains(request.point) || !isEditable)
2875             info.lineCaretExtent.setY(request.point.y() - info.lineCaretExtent.height() / 2);
2876         info.caretHeight = info.lineCaretExtent.height();
2877     }
2878
2879     auto nodeShouldNotUseIBeam = ^(Node* node) {
2880         if (!node)
2881             return false;
2882         RenderObject *renderer = node->renderer();
2883         if (!renderer)
2884             return false;
2885         return is<RenderReplaced>(*renderer);
2886     };
2887
2888     const auto& deepPosition = position.deepEquivalent();
2889     info.shouldNotUseIBeamInEditableContent = nodeShouldNotUseIBeam(node) || nodeShouldNotUseIBeam(deepPosition.computeNodeBeforePosition()) || nodeShouldNotUseIBeam(deepPosition.computeNodeAfterPosition());
2890 }
2891
2892 InteractionInformationAtPosition WebPage::positionInformation(const InteractionInformationRequest& request)
2893 {
2894     InteractionInformationAtPosition info;
2895     info.request = request;
2896
2897     FloatPoint adjustedPoint;
2898     auto* nodeRespondingToClickEvents = m_page->mainFrame().nodeRespondingToClickEvents(request.point, adjustedPoint);
2899
2900     info.adjustedPointForNodeRespondingToClickEvents = adjustedPoint;
2901     info.nodeAtPositionHasDoubleClickHandler = m_page->mainFrame().nodeRespondingToDoubleClickEvent(request.point, adjustedPoint);
2902
2903     auto& eventHandler = m_page->mainFrame().eventHandler();
2904     constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::AllowFrameScrollbars, HitTestRequest::AllowVisibleChildFrameContentOnly };
2905     HitTestResult hitTestResultForCursor = eventHandler.hitTestResultAtPoint(request.point, hitType);
2906     if (auto* hitFrame = hitTestResultForCursor.innerNodeFrame()) {
2907         info.cursor = hitFrame->eventHandler().selectCursor(hitTestResultForCursor, false);
2908         if (request.includeCaretContext)
2909             populateCaretContext(hitTestResultForCursor, request, info);
2910     }
2911
2912     if (m_focusedElement)
2913         focusedElementPositionInformation(*this, *m_focusedElement, request, info);
2914
2915     if (is<Element>(nodeRespondingToClickEvents)) {
2916         auto& element = downcast<Element>(*nodeRespondingToClickEvents);
2917         elementPositionInformation(*this, element, request, info);
2918
2919         if (info.isLink && !info.isImage && request.includeSnapshot)
2920             info.image = shareableBitmapSnapshotForNode(element);
2921     }
2922
2923     if (!(info.isLink || info.isImage))
2924         selectionPositionInformation(*this, request, info);
2925
2926     // Prevent the callout bar from showing when tapping on the datalist button.
2927 #if ENABLE(DATALIST_ELEMENT)
2928     if (is<HTMLInputElement>(nodeRespondingToClickEvents))
2929         textInteractionPositionInformation(*this, downcast<HTMLInputElement>(*nodeRespondingToClickEvents), request, info);
2930 #endif
2931
2932     return info;
2933 }
2934
2935 void WebPage::requestPositionInformation(const InteractionInformationRequest& request)
2936 {
2937     send(Messages::WebPageProxy::DidReceivePositionInformation(positionInformation(request)));
2938 }
2939
2940 void WebPage::startInteractionWithElementContextOrPosition(Optional<WebCore::ElementContext>&& elementContext, WebCore::IntPoint&& point)
2941 {
2942     if (elementContext) {
2943         m_interactionNode = elementForContext(*elementContext);
2944         if (m_interactionNode)
2945             return;
2946     }
2947
2948     FloatPoint adjustedPoint;
2949     m_interactionNode = m_page->mainFrame().nodeRespondingToInteraction(point, adjustedPoint);
2950 }
2951
2952 void WebPage::stopInteraction()
2953 {
2954     m_interactionNode = nullptr;
2955 }
2956
2957 void WebPage::performActionOnElement(uint32_t action)
2958 {
2959     if (!is<HTMLElement>(m_interactionNode.get()))
2960         return;
2961
2962     HTMLElement& element = downcast<HTMLElement>(*m_interactionNode);
2963     if (!element.renderer())
2964         return;
2965
2966     if (static_cast<SheetAction>(action) == SheetAction::Copy) {
2967         if (is<RenderImage>(*element.renderer())) {
2968             URL url;
2969             String title;
2970             if (auto* linkElement = containingLinkAnchorElement(element)) {
2971                 url = linkElement->href();
2972                 title = linkElement->attributeWithoutSynchronization(HTMLNames::titleAttr);
2973                 if (!title.length())
2974                     title = linkElement->textContent();
2975                 title = stripLeadingAndTrailingHTMLSpaces(title);
2976             }
2977             m_interactionNode->document().frame()->editor().writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), element, url, title);
2978         } else if (element.isLink()) {
2979             m_interactionNode->document().frame()->editor().copyURL(element.document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.attributeWithoutSynchronization(HTMLNames::hrefAttr))), element.textContent());
2980         }
2981     } else if (static_cast<SheetAction>(action) == SheetAction::SaveImage) {
2982         if (!is<RenderImage>(*element.renderer()))
2983             return;
2984         CachedImage* cachedImage = downcast<RenderImage>(*element.renderer()).cachedImage();
2985         if (!cachedImage)
2986             return;
2987         RefPtr<SharedBuffer> buffer = cachedImage->resourceBuffer();
2988         if (!buffer)
2989             return;
2990         uint64_t bufferSize = buffer->size();
2991         RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::allocate(bufferSize);
2992         memcpy(sharedMemoryBuffer->data(), buffer->data(), bufferSize);
2993         SharedMemory::Handle handle;
2994         sharedMemoryBuffer->createHandle(handle, SharedMemory::Protection::ReadOnly);
2995         send(Messages::WebPageProxy::SaveImageToLibrary(handle, bufferSize));
2996     }
2997 }
2998
2999 static inline Element* nextAssistableElement(Node* startNode, Page& page, bool isForward)
3000 {
3001     if (!is<Element>(startNode))
3002         return nullptr;
3003
3004     Element* nextElement = downcast<Element>(startNode);
3005     do {
3006         nextElement = isForward
3007             ? page.focusController().nextFocusableElement(*nextElement)
3008             : page.focusController().previousFocusableElement(*nextElement);
3009     } while (nextElement && !isAssistableElement(*nextElement));
3010
3011     return nextElement;
3012 }
3013
3014 void WebPage::focusNextFocusedElement(bool isForward, CallbackID callbackID)
3015 {
3016     Element* nextElement = nextAssistableElement(m_focusedElement.get(), *m_page, isForward);
3017     m_userIsInteracting = true;
3018     if (nextElement)
3019         nextElement->focus();
3020     m_userIsInteracting = false;
3021     send(Messages::WebPageProxy::VoidCallback(callbackID));
3022 }
3023
3024 void WebPage::getFocusedElementInformation(FocusedElementInformation& information)
3025 {
3026     auto focusedElement = m_focusedElement.copyRef();
3027     layoutIfNeeded();
3028     if (focusedElement != m_focusedElement)
3029         return;
3030
3031     information.lastInteractionLocation = m_lastInteractionLocation;
3032     if (auto elementContext = contextForElement(*focusedElement))
3033         information.elementContext = WTFMove(*elementContext);
3034
3035     if (auto* renderer = focusedElement->renderer()) {
3036         information.interactionRect = rootViewInteractionBoundsForElement(*focusedElement);
3037         information.nodeFontSize = renderer->style().fontDescription().computedSize();
3038
3039         bool inFixed = false;
3040         renderer->localToContainerPoint(FloatPoint(), nullptr, UseTransforms, &inFixed);
3041         information.insideFixedPosition = inFixed;
3042         information.isRTL = renderer->style().direction() == TextDirection::RTL;
3043     } else
3044         information.interactionRect = { };
3045
3046     if (is<HTMLElement>(focusedElement))
3047         information.isSpellCheckingEnabled = downcast<HTMLElement>(*focusedElement).spellcheck();
3048
3049     information.minimumScaleFactor = minimumPageScaleFactor();
3050     information.maximumScaleFactor = maximumPageScaleFactor();
3051     information.maximumScaleFactorIgnoringAlwaysScalable = maximumPageScaleFactorIgnoringAlwaysScalable();
3052     information.allowsUserScaling = m_viewportConfiguration.allowsUserScaling();
3053     information.allowsUserScalingIgnoringAlwaysScalable = m_viewportConfiguration.allowsUserScalingIgnoringAlwaysScalable();
3054     if (auto* nextElement = nextAssistableElement(focusedElement.get(), *m_page, true)) {
3055         information.nextNodeRect = rootViewBoundsForElement(*nextElement);
3056         information.hasNextNode = true;
3057     }
3058     if (auto* previousElement = nextAssistableElement(focusedElement.get(), *m_page, false)) {
3059         information.previousNodeRect = rootViewBoundsForElement(*previousElement);
3060         information.hasPreviousNode = true;
3061     }
3062     information.focusedElementIdentifier = m_currentFocusedElementIdentifier;
3063
3064     if (is<LabelableElement>(*focusedElement)) {
3065         auto labels = downcast<LabelableElement>(*focusedElement).labels();
3066         Vector<Ref<Element>> associatedLabels;
3067         for (unsigned index = 0; index < labels->length(); ++index) {
3068             if (is<Element>(labels->item(index)) && labels->item(index)->renderer())
3069                 associatedLabels.append(downcast<Element>(*labels->item(index)));
3070         }
3071         for (auto& labelElement : associatedLabels) {
3072             auto text = labelElement->innerText();
3073             if (!text.isEmpty()) {
3074                 information.label = WTFMove(text);
3075                 break;
3076             }
3077         }
3078     }
3079
3080     information.title = focusedElement->title();
3081     information.ariaLabel = focusedElement->attributeWithoutSynchronization(HTMLNames::aria_labelAttr);
3082
3083     if (is<HTMLSelectElement>(*focusedElement)) {
3084         HTMLSelectElement& element = downcast<HTMLSelectElement>(*focusedElement);
3085         information.elementType = InputType::Select;
3086         const Vector<HTMLElement*>& items = element.listItems();
3087         size_t count = items.size();
3088         int parentGroupID = 0;
3089         // The parent group ID indicates the group the option belongs to and is 0 for group elements.
3090         // If there are option elements in between groups, they are given it's own group identifier.
3091         // If a select does not have groups, all the option elements have group ID 0.
3092         for (size_t i = 0; i < count; ++i) {
3093             HTMLElement* item = items[i];
3094             if (is<HTMLOptionElement>(*item)) {
3095                 HTMLOptionElement& option = downcast<HTMLOptionElement>(*item);
3096                 information.selectOptions.append(OptionItem(option.text(), false, parentGroupID, option.selected(), option.hasAttributeWithoutSynchronization(WebCore::HTMLNames::disabledAttr)));
3097             } else if (is<HTMLOptGroupElement>(*item)) {
3098                 HTMLOptGroupElement& group = downcast<HTMLOptGroupElement>(*item);
3099                 parentGroupID++;
3100                 information.selectOptions.append(OptionItem(group.groupLabelText(), true, 0, false, group.hasAttributeWithoutSynchronization(WebCore::HTMLNames::disabledAttr)));
3101             }
3102         }
3103         information.selectedIndex = element.selectedIndex();
3104         information.isMultiSelect = element.multiple();
3105     } else if (is<HTMLTextAreaElement>(*focusedElement)) {
3106         HTMLTextAreaElement& element = downcast<HTMLTextAreaElement>(*focusedElement);
3107         information.autocapitalizeType = element.autocapitalizeType();
3108         information.isAutocorrect = element.shouldAutocorrect();
3109         information.elementType = InputType::TextArea;
3110         information.isReadOnly = element.isReadOnly();
3111         information.value = element.value();
3112         information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
3113         information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
3114         information.inputMode = element.canonicalInputMode();
3115         information.enterKeyHint = element.canonicalEnterKeyHint();
3116     } else if (is<HTMLInputElement>(*focusedElement)) {
3117         HTMLInputElement& element = downcast<HTMLInputElement>(*focusedElement);
3118         HTMLFormElement* form = element.form();
3119         if (form)
3120             information.formAction = form->getURLAttribute(WebCore::HTMLNames::actionAttr);
3121         if (auto autofillElements = WebCore::AutofillElements::computeAutofillElements(element)) {
3122             information.acceptsAutofilledLoginCredentials = true;
3123             information.isAutofillableUsernameField = autofillElements->username() == focusedElement;
3124         }
3125         information.representingPageURL = element.document().urlForBindings();
3126         information.autocapitalizeType = element.autocapitalizeType();
3127         information.isAutocorrect = element.shouldAutocorrect();
3128         information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
3129         if (element.isPasswordField())
3130             information.elementType = InputType::Password;
3131         else if (element.isSearchField())
3132             information.elementType = InputType::Search;
3133         else if (element.isEmailField())
3134             information.elementType = InputType::Email;
3135         else if (element.isTelephoneField())
3136             information.elementType = InputType::Phone;
3137         else if (element.isNumberField())
3138             information.elementType = element.getAttribute("pattern") == "\\d*" || element.getAttribute("pattern") == "[0-9]*" ? InputType::NumberPad : InputType::Number;
3139         else if (element.isDateTimeLocalField())
3140             information.elementType = InputType::DateTimeLocal;
3141         else if (element.isDateField())
3142             information.elementType = InputType::Date;
3143         else if (element.isDateTimeField())
3144             information.elementType = InputType::DateTime;
3145         else if (element.isTimeField())
3146             information.elementType = InputType::Time;
3147         else if (element.isWeekField())
3148             information.elementType = InputType::Week;
3149         else if (element.isMonthField())
3150             information.elementType = InputType::Month;
3151         else if (element.isURLField())
3152             information.elementType = InputType::URL;
3153         else if (element.isText()) {
3154             const AtomString& pattern = element.attributeWithoutSynchronization(HTMLNames::patternAttr);
3155             if (pattern == "\\d*" || pattern == "[0-9]*")
3156                 information.elementType = InputType::NumberPad;
3157             else {
3158                 information.elementType = InputType::Text;
3159                 if (!information.formAction.isEmpty()
3160                     && (element.getNameAttribute().contains("search") || element.getIdAttribute().contains("search") || element.attributeWithoutSynchronization(HTMLNames::titleAttr).contains("search")))
3161                     information.elementType = InputType::Search;
3162             }
3163         }
3164 #if ENABLE(INPUT_TYPE_COLOR)
3165         else if (element.isColorControl()) {
3166             information.elementType = InputType::Color;
3167 #if ENABLE(DATALIST_ELEMENT)
3168             information.suggestedColors = element.suggestedColors();
3169 #endif
3170         }
3171 #endif
3172
3173 #if ENABLE(DATALIST_ELEMENT)
3174         information.hasSuggestions = !!element.list();
3175 #endif
3176         information.inputMode = element.canonicalInputMode();
3177         information.enterKeyHint = element.canonicalEnterKeyHint();
3178         information.isReadOnly = element.isReadOnly();
3179         information.value = element.value();
3180         information.valueAsNumber = element.valueAsNumber();
3181         information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
3182     } else if (is<HTMLImageElement>(*focusedElement) && downcast<HTMLImageElement>(*focusedElement).hasEditableImageAttribute()) {
3183         information.elementType = InputType::Drawing;
3184         information.embeddedViewID = downcast<HTMLImageElement>(*focusedElement).editableImageViewID();
3185     } else if (focusedElement->hasEditableStyle()) {
3186         information.elementType = InputType::ContentEditable;
3187         if (is<HTMLElement>(*focusedElement)) {
3188             auto& focusedHTMLElement = downcast<HTMLElement>(*focusedElement);
3189             information.isAutocorrect = focusedHTMLElement.shouldAutocorrect();
3190             information.autocapitalizeType = focusedHTMLElement.autocapitalizeType();
3191             information.inputMode = focusedHTMLElement.canonicalInputMode();
3192             information.enterKeyHint = focusedHTMLElement.canonicalEnterKeyHint();
3193             information.shouldSynthesizeKeyEventsForEditing = focusedHTMLElement.document().settings().syntheticEditingCommandsEnabled();
3194         } else {
3195             information.isAutocorrect = true;
3196             information.autocapitalizeType = AutocapitalizeTypeDefault;
3197         }
3198         information.isReadOnly = false;
3199     }
3200
3201     if (focusedElement->document().quirks().shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreas() && isTransparentOrFullyClipped(*focusedElement)) {
3202         information.autocapitalizeType = AutocapitalizeTypeNone;
3203         information.isAutocorrect = false;
3204     }
3205
3206     auto& quirks = focusedElement->document().quirks();
3207     information.shouldAvoidResizingWhenInputViewBoundsChange = quirks.shouldAvoidResizingWhenInputViewBoundsChange();
3208     information.shouldAvoidScrollingWhenFocusedContentIsVisible = quirks.shouldAvoidScrollingWhenFocusedContentIsVisible();
3209     information.shouldUseLegacySelectPopoverDismissalBehaviorInDataActivation = quirks.shouldUseLegacySelectPopoverDismissalBehaviorInDataActivation();
3210 }
3211
3212 void WebPage::autofillLoginCredentials(const String& username, const String& password)
3213 {
3214     if (is<HTMLInputElement>(m_focusedElement.get())) {
3215         if (auto autofillElements = AutofillElements::computeAutofillElements(downcast<HTMLInputElement>(*m_focusedElement)))
3216             autofillElements->autofill(username, password);
3217     }
3218 }
3219
3220 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
3221 // we need to ignore differences that are within a small rounding error on floats.
3222 static inline bool areEssentiallyEqualAsFloat(float a, float b)
3223 {
3224     return WTF::areEssentiallyEqual(a, b);
3225 }
3226
3227 void WebPage::setViewportConfigurationViewLayoutSize(const FloatSize& size, double scaleFactor, double minimumEffectiveDeviceWidth)
3228 {
3229     LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_identifier << " setViewportConfigurationViewLayoutSize " << size << " scaleFactor " << scaleFactor << " minimumEffectiveDeviceWidth " << minimumEffectiveDeviceWidth);
3230
3231     auto previousLayoutSizeScaleFactor = m_viewportConfiguration.layoutSizeScaleFactor();
3232     auto clampedMinimumEffectiveDevice = m_viewportConfiguration.isKnownToLayOutWiderThanViewport() ? WTF::nullopt : Optional<double>(minimumEffectiveDeviceWidth);
3233     if (!m_viewportConfiguration.setViewLayoutSize(size, scaleFactor, WTFMove(clampedMinimumEffectiveDevice)))
3234         return;
3235
3236     auto zoomToInitialScale = ZoomToInitialScale::No;
3237     auto newInitialScale = m_viewportConfiguration.initialScale();
3238     auto currentPageScaleFactor = pageScaleFactor();
3239     if (scaleFactor > previousLayoutSizeScaleFactor && newInitialScale > currentPageScaleFactor)
3240         zoomToInitialScale = ZoomToInitialScale::Yes;
3241     else if (scaleFactor < previousLayoutSizeScaleFactor && newInitialScale < currentPageScaleFactor)
3242         zoomToInitialScale = ZoomToInitialScale::Yes;
3243
3244     viewportConfigurationChanged(zoomToInitialScale);
3245 }
3246
3247 void WebPage::setMaximumUnobscuredSize(const FloatSize& maximumUnobscuredSize)
3248 {
3249     m_maximumUnobscuredSize = maximumUnobscuredSize;
3250     updateViewportSizeForCSSViewportUnits();
3251 }
3252
3253 void WebPage::setDeviceOrientation(int32_t deviceOrientation)
3254 {
3255     if (deviceOrientation == m_deviceOrientation)
3256         return;
3257     m_deviceOrientation = deviceOrientation;
3258     m_page->mainFrame().orientationChanged();
3259 }
3260
3261 void WebPage::setOverrideViewportArguments(const Optional<WebCore::ViewportArguments>& arguments)
3262 {
3263     m_page->setOverrideViewportArguments(arguments);
3264 }
3265
3266 void WebPage::dynamicViewportSizeUpdate(const FloatSize& viewLayoutSize, const WebCore::FloatSize& maximumUnobscuredSize, const FloatRect& targetExposedContentRect, const FloatRect& targetUnobscuredRect, const WebCore::FloatRect& targetUnobscuredRectInScrollViewCoordinates, const WebCore::FloatBoxExtent& targetUnobscuredSafeAreaInsets, double targetScale, int32_t deviceOrientation, double minimumEffectiveDeviceWidth, DynamicViewportSizeUpdateID dynamicViewportSizeUpdateID)
3267 {
3268     SetForScope<bool> dynamicSizeUpdateGuard(m_inDynamicSizeUpdate, true);
3269     // FIXME: this does not handle the cases where the content would change the content size or scroll position from JavaScript.
3270     // To handle those cases, we would need to redo this computation on every change until the next visible content rect update.
3271     LOG_WITH_STREAM(VisibleRects, stream << "\nWebPage::dynamicViewportSizeUpdate - viewLayoutSize " << viewLayoutSize << " targetUnobscuredRect " << targetUnobscuredRect << " targetExposedContentRect " << targetExposedContentRect << " targetScale " << targetScale);
3272
3273     FrameView& frameView = *m_page->mainFrame().view();
3274     IntSize oldContentSize = frameView.contentsSize();
3275     float oldPageScaleFactor = m_page->pageScaleFactor();
3276
3277     m_dynamicSizeUpdateHistory.add(std::make_pair(oldContentSize, oldPageScaleFactor), frameView.scrollPosition());
3278
3279     RefPtr<Node> oldNodeAtCenter;
3280     double visibleHorizontalFraction = 1;
3281     float relativeHorizontalPositionInNodeAtCenter = 0;
3282     float relativeVerticalPositionInNodeAtCenter = 0;
3283     {
3284         visibleHorizontalFraction = frameView.unobscuredContentSize().width() / oldContentSize.width();
3285         IntPoint unobscuredContentRectCenter = frameView.unobscuredContentRect().center();
3286
3287         HitTestResult hitTestResult = HitTestResult(unobscuredContentRectCenter);
3288
3289         if (auto* document = frameView.frame().document())
3290             document->hitTest(HitTestRequest(), hitTestResult);
3291
3292         if (Node* node = hitTestResult.innerNode()) {
3293             if (RenderObject* renderer = node->renderer()) {
3294                 FrameView& containingView = *node->document().frame()->view();
3295                 FloatRect boundingBox = containingView.contentsToRootView(renderer->absoluteBoundingBoxRect(true));
3296                 relativeHorizontalPositionInNodeAtCenter = (unobscuredContentRectCenter.x() - boundingBox.x()) / boundingBox.width();
3297                 relativeVerticalPositionInNodeAtCenter = (unobscuredContentRectCenter.y() - boundingBox.y()) / boundingBox.height();
3298                 oldNodeAtCenter = node;
3299             }
3300         }
3301     }
3302
3303     LOG_WITH_STREAM(VisibleRects, stream << "WebPage::dynamicViewportSizeUpdate setting view layout size to " << viewLayoutSize);
3304     bool viewportChanged = m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(false);
3305     viewportChanged |= m_viewportConfiguration.setViewLayoutSize(viewLayoutSize, WTF::nullopt, minimumEffectiveDeviceWidth);
3306     if (viewportChanged)
3307         viewportConfigurationChanged();
3308
3309     IntSize newLayoutSize = m_viewportConfiguration.layoutSize();
3310
3311 #if ENABLE(TEXT_AUTOSIZING)
3312     if (setFixedLayoutSize(newLayoutSize))
3313         resetTextAutosizing();
3314 #endif
3315     setMaximumUnobscuredSize(maximumUnobscuredSize);
3316     m_page->setUnobscuredSafeAreaInsets(targetUnobscuredSafeAreaInsets);
3317
3318     frameView.updateLayoutAndStyleIfNeededRecursive();
3319
3320     IntSize newContentSize = frameView.contentsSize();
3321
3322     double scale = scaleAfterViewportWidthChange(targetScale, m_userHasChangedPageScaleFactor, m_viewportConfiguration, targetUnobscuredRectInScrollViewCoordinates.width(), newContentSize, oldContentSize, visibleHorizontalFraction);
3323     FloatRect newUnobscuredContentRect = targetUnobscuredRect;
3324     FloatRect newExposedContentRect = targetExposedContentRect;
3325
3326     bool scaleChanged = !areEssentiallyEqualAsFloat(scale, targetScale);
3327     if (scaleChanged) {
3328         // The target scale the UI is using cannot be reached by the content. We need to compute new targets based
3329         // on the viewport constraint and report everything back to the UIProcess.
3330
3331         // 1) Compute a new unobscured rect centered around the original one.
3332         double scaleDifference = targetScale / scale;
3333         double newUnobscuredRectWidth = targetUnobscuredRect.width() * scaleDifference;
3334         double newUnobscuredRectHeight = targetUnobscuredRect.height() * scaleDifference;
3335         double newUnobscuredRectX = targetUnobscuredRect.x() - (newUnobscuredRectWidth - targetUnobscuredRect.width()) / 2;
3336         double newUnobscuredRectY = targetUnobscuredRect.y() - (newUnobscuredRectHeight - targetUnobscuredRect.height()) / 2;
3337         newUnobscuredContentRect = FloatRect(newUnobscuredRectX, newUnobscuredRectY, newUnobscuredRectWidth, newUnobscuredRectHeight);
3338
3339         // 2) Extend our new unobscuredRect by the obscured margins to get a new exposed rect.
3340         double obscuredTopMargin = (targetUnobscuredRect.y() - targetExposedContentRect.y()) * scaleDifference;
3341         double obscuredLeftMargin = (targetUnobscuredRect.x() - targetExposedContentRect.x()) * scaleDifference;
3342         double obscuredBottomMargin = (targetExposedContentRect.maxY() - targetUnobscuredRect.maxY()) * scaleDifference;
3343         double obscuredRightMargin = (targetExposedContentRect.maxX() - targetUnobscuredRect.maxX()) * scaleDifference;
3344         newExposedContentRect = FloatRect(newUnobscuredRectX - obscuredLeftMargin,
3345                                           newUnobscuredRectY - obscuredTopMargin,
3346                                           newUnobscuredRectWidth + obscuredLeftMargin + obscuredRightMargin,
3347                                           newUnobscuredRectHeight + obscuredTopMargin + obscuredBottomMargin);
3348     }
3349
3350     if (oldContentSize != newContentSize || scaleChanged) {
3351         // Snap the new unobscured rect back into the content rect.
3352         newUnobscuredContentRect.setWidth(std::min(static_cast<float>(newContentSize.width()), newUnobscuredContentRect.width()));
3353         newUnobscuredContentRect.setHeight(std::min(static_cast<float>(newContentSize.height()), newUnobscuredContentRect.height()));
3354
3355         bool positionWasRestoredFromSizeUpdateHistory = false;
3356         const auto& previousPosition = m_dynamicSizeUpdateHistory.find(std::pair<IntSize, float>(newContentSize, scale));
3357         if (previousPosition != m_dynamicSizeUpdateHistory.end()) {
3358             IntPoint restoredPosition = previousPosition->value;
3359             FloatPoint deltaPosition(restoredPosition.x() - newUnobscuredContentRect.x(), restoredPosition.y() - newUnobscuredContentRect.y());
3360             newUnobscuredContentRect.moveBy(deltaPosition);
3361             newExposedContentRect.moveBy(deltaPosition);
3362             positionWasRestoredFromSizeUpdateHistory = true;
3363         } else if (oldContentSize != newContentSize) {
3364             FloatPoint newRelativeContentCenter;
3365
3366             if (RenderObject* renderer = oldNodeAtCenter ? oldNodeAtCenter->renderer() : nullptr) {
3367                 FrameView& containingView = *oldNodeAtCenter->document().frame()->view();
3368                 FloatRect newBoundingBox = containingView.contentsToRootView(renderer->absoluteBoundingBoxRect(true));
3369                 newRelativeContentCenter = FloatPoint(newBoundingBox.x() + relativeHorizontalPositionInNodeAtCenter * newBoundingBox.width(), newBoundingBox.y() + relativeVerticalPositionInNodeAtCenter * newBoundingBox.height());
3370             } else
3371                 newRelativeContentCenter = relativeCenterAfterContentSizeChange(targetUnobscuredRect, oldContentSize, newContentSize);
3372
3373             FloatPoint newUnobscuredContentRectCenter = newUnobscuredContentRect.center();
3374             FloatPoint positionDelta(newRelativeContentCenter.x() - newUnobscuredContentRectCenter.x(), newRelativeContentCenter.y() - newUnobscuredContentRectCenter.y());
3375             newUnobscuredContentRect.moveBy(positionDelta);
3376             newExposedContentRect.moveBy(positionDelta);
3377         }
3378
3379         // Make the top/bottom edges "sticky" within 1 pixel.
3380         if (!positionWasRestoredFromSizeUpdateHistory) {
3381             if (targetUnobscuredRect.maxY() > oldContentSize.height() - 1) {
3382                 float bottomVerticalPosition = newContentSize.height() - newUnobscuredContentRect.height();
3383                 newUnobscuredContentRect.setY(bottomVerticalPosition);
3384                 newExposedContentRect.setY(bottomVerticalPosition);
3385             }
3386             if (targetUnobscuredRect.y() < 1) {
3387                 newUnobscuredContentRect.setY(0);
3388                 newExposedContentRect.setY(0);
3389             }
3390
3391             bool likelyResponsiveDesignViewport = newLayoutSize.width() == viewLayoutSize.width() && areEssentiallyEqualAsFloat(scale, 1);
3392             bool contentBleedsOutsideLayoutWidth = newContentSize.width() > newLayoutSize.width();
3393             bool originalScrollPositionWasOnTheLeftEdge = targetUnobscuredRect.x() <= 0;
3394             if (likelyResponsiveDesignViewport && contentBleedsOutsideLayoutWidth && originalScrollPositionWasOnTheLeftEdge) {
3395                 // This is a special heuristics for "responsive" design with odd layout. It is quite common for responsive design
3396                 // to have content "bleeding" outside of the minimal layout width, usually from an image or table larger than expected.
3397                 // In those cases, the design usually does not adapt to the new width and remain at the newLayoutSize except for the
3398                 // large boxes.
3399                 // It is worth revisiting this special case as web developers get better with responsive design.
3400                 newExposedContentRect.setX(0);
3401                 newUnobscuredContentRect.setX(0);
3402             }
3403         }
3404
3405         float horizontalAdjustment = 0;
3406         if (newUnobscuredContentRect.maxX() > newContentSize.width())
3407             horizontalAdjustment -= newUnobscuredContentRect.maxX() - newContentSize.width();
3408         float verticalAdjustment = 0;
3409         if (newUnobscuredContentRect.maxY() > newContentSize.height())
3410             verticalAdjustment -= newUnobscuredContentRect.maxY() - newContentSize.height();
3411         if (newUnobscuredContentRect.x() < 0)
3412             horizontalAdjustment += - newUnobscuredContentRect.x();
3413         if (newUnobscuredContentRect.y() < 0)
3414             verticalAdjustment += - newUnobscuredContentRect.y();
3415
3416         FloatPoint adjustmentDelta(horizontalAdjustment, verticalAdjustment);
3417         newUnobscuredContentRect.moveBy(adjustmentDelta);
3418         newExposedContentRect.moveBy(adjustmentDelta);
3419     }
3420
3421     frameView.setScrollVelocity({ 0, 0, 0, MonotonicTime::now() });
3422
3423     IntPoint roundedUnobscuredContentRectPosition = roundedIntPoint(newUnobscuredContentRect.location());
3424     frameView.setUnobscuredContentSize(newUnobscuredContentRect.size());
3425     m_drawingArea->setExposedContentRect(newExposedContentRect);
3426
3427     scalePage(scale, roundedUnobscuredContentRectPosition);
3428
3429     frameView.updateLayoutAndStyleIfNeededRecursive();
3430
3431     auto& settings = frameView.frame().settings();
3432     LayoutRect documentRect = IntRect(frameView.scrollOrigin(), frameView.contentsSize());
3433     auto layoutViewportSize = FrameView::expandedLayoutViewportSize(frameView.baseLayoutViewportSize(), LayoutSize(documentRect.size()), settings.layoutViewportHeightExpansionFactor());
3434     LayoutRect layoutViewportRect = FrameView::computeUpdatedLayoutViewportRect(frameView.layoutViewportRect(), documentRect, LayoutSize(newUnobscuredContentRect.size()), LayoutRect(newUnobscuredContentRect), layoutViewportSize, frameView.minStableLayoutViewportOrigin(), frameView.maxStableLayoutViewportOrigin(), FrameView::LayoutViewportConstraint::ConstrainedToDocumentRect);
3435     frameView.setLayoutViewportOverrideRect(layoutViewportRect);
3436     frameView.layoutOrVisualViewportChanged();
3437
3438     frameView.setCustomSizeForResizeEvent(expandedIntSize(targetUnobscuredRectInScrollViewCoordinates.size()));
3439     setDeviceOrientation(deviceOrientation);
3440     frameView.setScrollOffset(roundedUnobscuredContentRectPosition);
3441
3442     m_page->updateRendering();
3443
3444 #if ENABLE(VIEWPORT_RESIZING)
3445     if (immediatelyShrinkToFitContent())
3446         viewportConfigurationChanged();
3447 #endif
3448
3449     m_drawingArea->scheduleRenderingUpdate();
3450
3451     m_pendingDynamicViewportSizeUpdateID = dynamicViewportSizeUpdateID;
3452 }
3453
3454 void WebPage::resetViewportDefaultConfiguration(WebFrame* frame, bool hasMobileDocType)
3455 {
3456     LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_identifier << " resetViewportDefaultConfiguration");
3457     if (m_useTestingViewportConfiguration) {
3458         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::testingParameters());
3459         return;
3460     }
3461
3462     auto parametersForStandardFrame = [&] {
3463         if (shouldIgnoreMetaViewport())
3464             return m_viewportConfiguration.nativeWebpageParameters();
3465         return ViewportConfiguration::webpageParameters();
3466     };
3467
3468     if (!frame) {
3469         m_viewportConfiguration.setDefaultConfiguration(parametersForStandardFrame());
3470         return;
3471     }
3472
3473     if (hasMobileDocType) {
3474         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::xhtmlMobileParameters());
3475         return;
3476     }
3477
3478     auto* document = frame->coreFrame()->document();
3479     if (document->isImageDocument())
3480         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::imageDocumentParameters());
3481     else if (document->isTextDocument())
3482         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::textDocumentParameters());
3483     else
3484         m_viewportConfiguration.setDefaultConfiguration(parametersForStandardFrame());
3485 }
3486
3487 #if ENABLE(TEXT_AUTOSIZING)
3488 void WebPage::resetIdempotentTextAutosizingIfNeeded(double previousInitialScale)
3489 {
3490     if (!m_page->settings().textAutosizingEnabled() || !m_page->settings().textAutosizingUsesIdempotentMode())
3491         return;
3492
3493     const float minimumScaleChangeBeforeRecomputingTextAutosizing = 0.01;
3494     if (std::abs(previousInitialScale - m_page->initialScale()) < minimumScaleChangeBeforeRecomputingTextAutosizing)
3495         return;
3496
3497     if (m_page->initialScale() >= 1 && previousInitialScale >= 1)
3498         return;
3499
3500     if (!m_page->mainFrame().view())
3501         return;
3502
3503     auto textAutoSizingDelay = [&] {
3504         auto& frameView = *m_page->mainFrame().view();
3505         if (!frameView.isVisuallyNonEmpty()) {
3506             // We don't anticipate any painting after the next upcoming layout.
3507             const Seconds longTextAutoSizingDelayOnViewportChange = 100_ms;
3508             return longTextAutoSizingDelayOnViewportChange;
3509         }
3510         const Seconds defaultTextAutoSizingDelayOnViewportChange = 80_ms;
3511         return defaultTextAutoSizingDelayOnViewportChange;
3512     };
3513
3514     // We don't need to update text sizing eagerly. There might be multiple incoming dynamic viewport changes.
3515     m_textAutoSizingAdjustmentTimer.startOneShot(textAutoSizingDelay());
3516 }
3517
3518 void WebPage::resetTextAutosizing()
3519 {
3520     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
3521         Document* document = frame->document();
3522         if (!document || !document->renderView())
3523             continue;
3524         document->renderView()->resetTextAutosizing();
3525     }
3526 }
3527 #endif
3528
3529 #if ENABLE(VIEWPORT_RESIZING)
3530
3531 void WebPage::scheduleShrinkToFitContent()
3532 {
3533     if (m_isClosed)
3534         return;
3535
3536     m_shrinkToFitContentTimer.restart();
3537 }
3538
3539 void WebPage::shrinkToFitContentTimerFired()
3540 {
3541     if (immediatelyShrinkToFitContent())
3542         viewportConfigurationChanged(ZoomToInitialScale::Yes);
3543 }
3544
3545 bool WebPage::immediatelyShrinkToFitContent()
3546 {
3547     if (m_isClosed)
3548         return false;
3549
3550     if (!m_page->settings().allowViewportShrinkToFitContent())
3551         return false;
3552
3553     if (m_useTestingViewportConfiguration)
3554         return false;
3555
3556     if (!shouldIgnoreMetaViewport())
3557         return false;
3558
3559     if (!m_viewportConfiguration.viewportArguments().shrinkToFit)
3560         return false;
3561
3562     if (m_viewportConfiguration.canIgnoreScalingConstraints())
3563         return false;
3564
3565     auto mainFrame = makeRefPtr(m_mainFrame->coreFrame());
3566     if (!mainFrame)
3567         return false;
3568
3569     auto view = makeRefPtr(mainFrame->view());
3570     auto mainDocument = makeRefPtr(mainFrame->document());
3571     if (!view || !mainDocument)
3572         return false;
3573
3574     mainDocument->updateLayout();
3575
3576     static const int toleratedHorizontalScrollingDistance = 20;
3577     static const int maximumExpandedLayoutWidth = 1280;
3578     static const int maximumContentWidthBeforeAvoidingShrinkToFit = 1920;
3579
3580     auto scaledViewWidth = [&] () -> int {
3581         return std::round(m_viewportConfiguration.viewLayoutSize().width() / m_viewportConfiguration.initialScale());
3582     };
3583
3584     int originalContentWidth = view->contentsWidth();
3585     int originalViewWidth = scaledViewWidth();
3586     int originalLayoutWidth = m_viewportConfiguration.layoutWidth();
3587     int originalHorizontalOverflowAmount = originalContentWidth - originalViewWidth;
3588     if (originalHorizontalOverflowAmount <= toleratedHorizontalScrollingDistance || originalLayoutWidth >= maximumExpandedLayoutWidth || originalContentWidth <= originalViewWidth || originalContentWidth > maximumContentWidthBeforeAvoidingShrinkToFit)
3589         return false;
3590
3591     auto changeMinimumEffectiveDeviceWidth = [this, mainDocument] (int targetLayoutWidth) -> bool {
3592         if (m_viewportConfiguration.setMinimumEffectiveDeviceWidth(targetLayoutWidth)) {
3593             viewportConfigurationChanged();
3594             mainDocument->updateLayout();
3595             return true;
3596         }
3597         return false;
3598     };
3599
3600     m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(true);
3601     double originalMinimumDeviceWidth = m_viewportConfiguration.minimumEffectiveDeviceWidth();
3602     if (changeMinimumEffectiveDeviceWidth(std::min(maximumExpandedLayoutWidth, originalContentWidth)) && view->contentsWidth() - scaledViewWidth() > originalHorizontalOverflowAmount) {
3603         changeMinimumEffectiveDeviceWidth(originalMinimumDeviceWidth);
3604         m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(false);
3605     }
3606
3607     // FIXME (197429): Consider additionally logging an error message to the console if a responsive meta viewport tag was used.
3608     RELEASE_LOG(ViewportSizing, "Shrink-to-fit: content width %d => %d; layout width %d => %d", originalContentWidth, view->contentsWidth(), originalLayoutWidth, m_viewportConfiguration.layoutWidth());
3609     return true;
3610 }
3611
3612 #endif // ENABLE(VIEWPORT_RESIZING)
3613
3614 bool WebPage::shouldIgnoreMetaViewport() const
3615 {
3616     if (auto* mainDocument = m_page->mainFrame().document()) {
3617         auto* loader = mainDocument->loader();
3618         if (loader && loader->metaViewportPolicy() == WebCore::MetaViewportPolicy::Ignore)
3619             return true;
3620     }
3621     return m_page->settings().shouldIgnoreMetaViewport();
3622 }
3623
3624 void WebPage::viewportConfigurationChanged(ZoomToInitialScale zoomToInitialScale)
3625 {
3626     double initialScale = m_viewportConfiguration.initialScale();
3627 #if ENABLE(TEXT_AUTOSIZING)
3628     double previousInitialScale = m_page->initialScale();
3629     m_page->setInitialScale(initialScale);
3630     resetIdempotentTextAutosizingIfNeeded(previousInitialScale);
3631
3632     if (setFixedLayoutSize(m_viewportConfiguration.layoutSize()))
3633         resetTextAutosizing();
3634 #endif
3635     double scale;
3636     if (m_userHasChangedPageScaleFactor && zoomToInitialScale == ZoomToInitialScale::No)
3637         scale = std::max(std::min(pageScaleFactor(), m_viewportConfiguration.maximumScale()), m_viewportConfiguration.minimumScale());
3638     else
3639         scale = initialScale;
3640
3641     LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_identifier << " viewportConfigurationChanged - setting zoomedOutPageScaleFactor to " << m_viewportConfiguration.minimumScale() << " and scale to " << scale);
3642
3643     m_page->setZoomedOutPageScaleFactor(m_viewportConfiguration.minimumScale());
3644
3645     updateViewportSizeForCSSViewportUnits();
3646
3647     FrameView& frameView = *mainFrameView();
3648     IntPoint scrollPosition = frameView.scrollPosition();
3649     if (!m_hasReceivedVisibleContentRectsAfterDidCommitLoad) {
3650         FloatSize minimumLayoutSizeInScrollViewCoordinates = m_viewportConfiguration.viewLayoutSize();
3651         minimumLayoutSizeInScrollViewCoordinates.scale(1 / scale);
3652         IntSize minimumLayoutSizeInDocumentCoordinates = roundedIntSize(minimumLayoutSizeInScrollViewCoordinates);
3653         frameView.setUnobscuredContentSize(minimumLayoutSizeInDocumentCoordinates);
3654         frameView.setScrollVelocity({ 0, 0, 0, MonotonicTime::now() });
3655
3656         // FIXME: We could send down the obscured margins to find a better exposed rect and unobscured rect.
3657         // It is not a big deal at the moment because the tile coverage will always extend past the obscured bottom inset.
3658         if (!m_hasRestoredExposedContentRectAfterDidCommitLoad)
3659             m_drawingArea->setExposedContentRect(FloatRect(scrollPosition, minimumLayoutSizeInDocumentCoordinates));
3660     }
3661     scalePage(scale, scrollPosition);
3662     
3663     if (!m_hasReceivedVisibleContentRectsAfterDidCommitLoad) {
3664         // This takes scale into account, so do after the scale change.
3665         frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(frameView.viewportConstrainedObjectsRect()));
3666
3667         frameView.setCustomSizeForResizeEvent(expandedIntSize(m_viewportConfiguration.minimumLayoutSize()));
3668     }
3669 }
3670
3671 void WebPage::updateViewportSizeForCSSViewportUnits()
3672 {
3673     FloatSize largestUnobscuredSize = m_maximumUnobscuredSize;
3674     if (largestUnobscuredSize.isEmpty())
3675         largestUnobscuredSize = m_viewportConfiguration.viewLayoutSize();
3676
3677     FrameView& frameView = *mainFrameView();
3678     largestUnobscuredSize.scale(1 / m_viewportConfiguration.initialScaleIgnoringContentSize());
3679     frameView.setViewportSizeForCSSViewportUnits(roundedIntSize(largestUnobscuredSize));
3680 }
3681
3682 void WebPage::applicationWillResignActive()
3683 {
3684     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationWillResignActiveNotification object:nil];
3685     if (m_page)
3686         m_page->applicationWillResignActive();
3687 }
3688
3689 void WebPage::applicationDidEnterBackground(bool isSuspendedUnderLock)
3690 {
3691     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationDidEnterBackgroundNotification object:nil userInfo:@{@"isSuspendedUnderLock": [NSNumber numberWithBool:isSuspendedUnderLock]}];
3692
3693     m_isSuspendedUnderLock = isSuspendedUnderLock;
3694     freezeLayerTree(LayerTreeFreezeReason::BackgroundApplication);
3695
3696     if (m_page)
3697         m_page->applicationDidEnterBackground();
3698 }
3699
3700 void WebPage::applicationDidFinishSnapshottingAfterEnteringBackground()
3701 {
3702     markLayersVolatile();
3703 }
3704
3705 void WebPage::applicationWillEnterForeground(bool isSuspendedUnderLock)
3706 {
3707     m_isSuspendedUnderLock = false;
3708     cancelMarkLayersVolatile();
3709
3710     unfreezeLayerTree(LayerTreeFreezeReason::BackgroundApplication);
3711
3712     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationWillEnterForegroundNotification object:nil userInfo:@{@"isSuspendedUnderLock": @(isSuspendedUnderLock)}];
3713
3714     if (m_page)
3715         m_page->applicationWillEnterForeground();
3716 }
3717
3718 void WebPage::applicationDidBecomeActive()
3719 {
3720     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationDidBecomeActiveNotification object:nil];
3721     if (m_page)
3722         m_page->applicationDidBecomeActive();
3723 }
3724
3725 void WebPage::applicationDidEnterBackgroundForMedia(bool isSuspendedUnderLock)
3726 {
3727     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationDidEnterBackgroundNotification object:nil userInfo:@{@"isSuspendedUnderLock": @(isSuspendedUnderLock)}];
3728 }
3729
3730 void WebPage::applicationWillEnterForegroundForMedia(bool isSuspendedUnderLock)
3731 {
3732     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationWillEnterForegroundNotification object:nil userInfo:@{@"isSuspendedUnderLock": @(isSuspendedUnderLock)}];
3733 }
3734
3735 static inline void adjustVelocityDataForBoundedScale(VelocityData& velocityData, double exposedRectScale, double minimumScale, double maximumScale)
3736 {
3737     if (velocityData.scaleChangeRate) {
3738         velocityData.horizontalVelocity = 0;
3739         velocityData.verticalVelocity = 0;