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