Use a 1-byte enum class for TextDirection
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / ios / WebPageIOS.mm
1 /*
2  * Copyright (C) 2012-2018 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)
30
31 #import "AccessibilityIOS.h"
32 #import "AssistedNodeInformation.h"
33 #import "DataReference.h"
34 #import "DrawingArea.h"
35 #import "EditingRange.h"
36 #import "EditorState.h"
37 #import "GestureTypes.h"
38 #import "InteractionInformationAtPosition.h"
39 #import "Logging.h"
40 #import "PluginView.h"
41 #import "PrintInfo.h"
42 #import "RemoteLayerTreeDrawingArea.h"
43 #import "SandboxUtilities.h"
44 #import "UserData.h"
45 #import "VisibleContentRectUpdateInfo.h"
46 #import "WKAccessibilityWebPageObjectIOS.h"
47 #import "WebChromeClient.h"
48 #import "WebCoreArgumentCoders.h"
49 #import "WebFrame.h"
50 #import "WebImage.h"
51 #import "WebPageProxyMessages.h"
52 #import "WebPreviewLoaderClient.h"
53 #import "WebProcess.h"
54 #import <CoreText/CTFont.h>
55 #import <WebCore/Autofill.h>
56 #import <WebCore/AutofillElements.h>
57 #import <WebCore/Chrome.h>
58 #import <WebCore/DataDetection.h>
59 #import <WebCore/DiagnosticLoggingClient.h>
60 #import <WebCore/DiagnosticLoggingKeys.h>
61 #import <WebCore/DragController.h>
62 #import <WebCore/Editing.h>
63 #import <WebCore/Editor.h>
64 #import <WebCore/Element.h>
65 #import <WebCore/ElementAncestorIterator.h>
66 #import <WebCore/EventHandler.h>
67 #import <WebCore/File.h>
68 #import <WebCore/FloatQuad.h>
69 #import <WebCore/FocusController.h>
70 #import <WebCore/Frame.h>
71 #import <WebCore/FrameLoaderClient.h>
72 #import <WebCore/FrameView.h>
73 #import <WebCore/GeometryUtilities.h>
74 #import <WebCore/HTMLAreaElement.h>
75 #import <WebCore/HTMLAttachmentElement.h>
76 #import <WebCore/HTMLBodyElement.h>
77 #import <WebCore/HTMLElement.h>
78 #import <WebCore/HTMLElementTypeHelpers.h>
79 #import <WebCore/HTMLFormElement.h>
80 #import <WebCore/HTMLImageElement.h>
81 #import <WebCore/HTMLInputElement.h>
82 #import <WebCore/HTMLOptGroupElement.h>
83 #import <WebCore/HTMLOptionElement.h>
84 #import <WebCore/HTMLParserIdioms.h>
85 #import <WebCore/HTMLSelectElement.h>
86 #import <WebCore/HTMLTextAreaElement.h>
87 #import <WebCore/HistoryItem.h>
88 #import <WebCore/HitTestResult.h>
89 #import <WebCore/KeyboardEvent.h>
90 #import <WebCore/LibWebRTCProvider.h>
91 #import <WebCore/MediaSessionManagerIOS.h>
92 #import <WebCore/Node.h>
93 #import <WebCore/NodeList.h>
94 #import <WebCore/NotImplemented.h>
95 #import <WebCore/Page.h>
96 #import <WebCore/Pasteboard.h>
97 #import <WebCore/PlatformKeyboardEvent.h>
98 #import <WebCore/PlatformMouseEvent.h>
99 #import <WebCore/RenderBlock.h>
100 #import <WebCore/RenderImage.h>
101 #import <WebCore/RenderThemeIOS.h>
102 #import <WebCore/RenderView.h>
103 #import <WebCore/RuntimeApplicationChecks.h>
104 #import <WebCore/Settings.h>
105 #import <WebCore/SharedBuffer.h>
106 #import <WebCore/StyleProperties.h>
107 #import <WebCore/TextIndicator.h>
108 #import <WebCore/TextIterator.h>
109 #import <WebCore/VisibleUnits.h>
110 #import <WebCore/WKContentObservation.h>
111 #import <WebCore/WebEvent.h>
112 #import <wtf/MathExtras.h>
113 #import <wtf/MemoryPressureHandler.h>
114 #import <wtf/SetForScope.h>
115 #import <wtf/SoftLinking.h>
116 #import <wtf/cocoa/Entitlements.h>
117 #import <wtf/text/TextStream.h>
118
119 #if ENABLE(MEDIA_STREAM)
120 #import "CelestialSPI.h"
121 SOFT_LINK_PRIVATE_FRAMEWORK_OPTIONAL(Celestial)
122 SOFT_LINK_CLASS_OPTIONAL(Celestial, AVSystemController)
123 SOFT_LINK_CONSTANT_MAY_FAIL(Celestial, AVSystemController_PIDToInheritApplicationStateFrom, NSString *)
124 #endif
125
126 using namespace WebCore;
127
128 namespace WebKit {
129
130 const int blockSelectionStartWidth = 100;
131 const int blockSelectionStartHeight = 100;
132
133 void WebPage::platformInitialize()
134 {
135     platformInitializeAccessibility();
136 }
137
138 void WebPage::platformDetach()
139 {
140     [m_mockAccessibilityElement setWebPage:nullptr];
141 }
142     
143 void WebPage::platformInitializeAccessibility()
144 {
145     m_mockAccessibilityElement = adoptNS([[WKAccessibilityWebPageObject alloc] init]);
146     [m_mockAccessibilityElement setWebPage:this];
147     
148     NSData *remoteToken = newAccessibilityRemoteToken([NSUUID UUID]);
149     IPC::DataReference dataToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
150     send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
151 }
152
153 static void computeEditableRootHasContentAndPlainText(const VisibleSelection& selection, EditorState::PostLayoutData& data)
154 {
155     data.hasContent = false;
156     data.hasPlainText = false;
157     if (!selection.isContentEditable())
158         return;
159
160     if (data.selectedTextLength || data.characterAfterSelection || data.characterBeforeSelection || data.twoCharacterBeforeSelection) {
161         // 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.
162         data.hasContent = true;
163         data.hasPlainText = true;
164         return;
165     }
166
167     auto* root = selection.rootEditableElement();
168     if (!root)
169         return;
170
171     auto startInEditableRoot = firstPositionInNode(root);
172     data.hasContent = root->hasChildNodes() && !isEndOfEditableOrNonEditableContent(startInEditableRoot);
173     data.hasPlainText = data.hasContent && hasAnyPlainText(Range::create(root->document(), VisiblePosition { startInEditableRoot }, VisiblePosition { lastPositionInNode(root) }));
174 }
175
176 void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
177 {
178     if (frame.editor().hasComposition()) {
179         RefPtr<Range> compositionRange = frame.editor().compositionRange();
180         Vector<WebCore::SelectionRect> compositionRects;
181         if (compositionRange) {
182             compositionRange->collectSelectionRects(compositionRects);
183             if (compositionRects.size())
184                 result.firstMarkedRect = compositionRects[0].rect();
185             if (compositionRects.size() > 1)
186                 result.lastMarkedRect = compositionRects.last().rect();
187             else
188                 result.lastMarkedRect = result.firstMarkedRect;
189             result.markedText = plainTextReplacingNoBreakSpace(compositionRange.get());
190         }
191     }
192
193     // We only set the remaining EditorState entries if the layout is done. To compute these
194     // entries, we need the layout to be done and we don't want to trigger a synchronous
195     // layout as this would be bad for performance. If we have a composition, we send everything
196     // right away as the UIProcess needs the caretRects ASAP for marked text.
197     bool frameViewHasFinishedLayout = frame.view() && !frame.view()->needsLayout();
198     if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::No && !frameViewHasFinishedLayout && !frame.editor().hasComposition()) {
199         result.isMissingPostLayoutData = true;
200         return;
201     }
202
203     auto& postLayoutData = result.postLayoutData();
204     FrameView* view = frame.view();
205     const VisibleSelection& selection = frame.selection().selection();
206     postLayoutData.isStableStateUpdate = m_isInStableState;
207     bool startNodeIsInsideFixedPosition = false;
208     bool endNodeIsInsideFixedPosition = false;
209     if (selection.isCaret()) {
210         postLayoutData.caretRectAtStart = view->contentsToRootView(frame.selection().absoluteCaretBounds(&startNodeIsInsideFixedPosition));
211         endNodeIsInsideFixedPosition = startNodeIsInsideFixedPosition;
212         postLayoutData.caretRectAtEnd = postLayoutData.caretRectAtStart;
213         // FIXME: The following check should take into account writing direction.
214         postLayoutData.isReplaceAllowed = result.isContentEditable && atBoundaryOfGranularity(selection.start(), WordGranularity, DirectionForward);
215         postLayoutData.wordAtSelection = plainTextReplacingNoBreakSpace(wordRangeFromPosition(selection.start()).get());
216         if (selection.isContentEditable())
217             charactersAroundPosition(selection.start(), postLayoutData.characterAfterSelection, postLayoutData.characterBeforeSelection, postLayoutData.twoCharacterBeforeSelection);
218     } else if (selection.isRange()) {
219         postLayoutData.caretRectAtStart = view->contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds(&startNodeIsInsideFixedPosition));
220         postLayoutData.caretRectAtEnd = view->contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds(&endNodeIsInsideFixedPosition));
221         RefPtr<Range> selectedRange = selection.toNormalizedRange();
222         String selectedText;
223         if (selectedRange) {
224             selectedRange->collectSelectionRects(postLayoutData.selectionRects);
225             convertSelectionRectsToRootView(view, postLayoutData.selectionRects);
226             selectedText = plainTextReplacingNoBreakSpace(selectedRange.get(), TextIteratorDefaultBehavior, true);
227             postLayoutData.selectedTextLength = selectedText.length();
228             const int maxSelectedTextLength = 200;
229             if (selectedText.length() <= maxSelectedTextLength)
230                 postLayoutData.wordAtSelection = selectedText;
231         }
232         // FIXME: We should disallow replace when the string contains only CJ characters.
233         postLayoutData.isReplaceAllowed = result.isContentEditable && !result.isInPasswordField && !selectedText.isAllSpecialCharacters<isHTMLSpace>();
234     }
235     postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
236     if (!selection.isNone()) {
237         if (m_assistedNode && m_assistedNode->renderer()) {
238             postLayoutData.selectionClipRect = view->contentsToRootView(m_assistedNode->renderer()->absoluteBoundingBoxRect());
239             postLayoutData.caretColor = m_assistedNode->renderer()->style().caretColor();
240         }
241         computeEditableRootHasContentAndPlainText(selection, postLayoutData);
242     }
243 }
244
245 FloatSize WebPage::screenSize() const
246 {
247     return m_screenSize;
248 }
249
250 FloatSize WebPage::availableScreenSize() const
251 {
252     return m_availableScreenSize;
253 }
254
255 FloatSize WebPage::overrideScreenSize() const
256 {
257     return m_overrideScreenSize;
258 }
259
260 void WebPage::didReceiveMobileDocType(bool isMobileDoctype)
261 {
262     resetViewportDefaultConfiguration(m_mainFrame.get(), isMobileDoctype);
263 }
264
265 void WebPage::savePageState(HistoryItem& historyItem)
266 {
267     historyItem.setScaleIsInitial(!m_userHasChangedPageScaleFactor);
268     historyItem.setMinimumLayoutSizeInScrollViewCoordinates(m_viewportConfiguration.minimumLayoutSize());
269     historyItem.setContentSize(m_viewportConfiguration.contentsSize());
270 }
271
272 static double scaleAfterViewportWidthChange(double currentScale, bool userHasChangedPageScaleFactor, const ViewportConfiguration& viewportConfiguration, float unobscuredWidthInScrollViewCoordinates, const IntSize& newContentSize, const IntSize& oldContentSize, float visibleHorizontalFraction)
273 {
274     double scale;
275     if (!userHasChangedPageScaleFactor)
276         scale = viewportConfiguration.initialScale();
277     else
278         scale = std::max(std::min(currentScale, viewportConfiguration.maximumScale()), viewportConfiguration.minimumScale());
279
280     LOG(VisibleRects, "scaleAfterViewportWidthChange getting scale %.2f", scale);
281
282     if (userHasChangedPageScaleFactor) {
283         // When the content size changes, we keep the same relative horizontal content width in view, otherwise we would
284         // end up zoomed too far in landscape->portrait, and too close in portrait->landscape.
285         double widthToKeepInView = visibleHorizontalFraction * newContentSize.width();
286         double newScale = unobscuredWidthInScrollViewCoordinates / widthToKeepInView;
287         scale = std::max(std::min(newScale, viewportConfiguration.maximumScale()), viewportConfiguration.minimumScale());
288     }
289     return scale;
290 }
291
292 static FloatPoint relativeCenterAfterContentSizeChange(const FloatRect& originalContentRect, IntSize oldContentSize, IntSize newContentSize)
293 {
294     // If the content size has changed, keep the same relative position.
295     FloatPoint oldContentCenter = originalContentRect.center();
296     float relativeHorizontalPosition = oldContentCenter.x() / oldContentSize.width();
297     float relativeVerticalPosition =  oldContentCenter.y() / oldContentSize.height();
298     return FloatPoint(relativeHorizontalPosition * newContentSize.width(), relativeVerticalPosition * newContentSize.height());
299 }
300
301 static inline FloatRect adjustExposedRectForNewScale(const FloatRect& exposedRect, double exposedRectScale, double newScale)
302 {
303     if (exposedRectScale == newScale)
304         return exposedRect;
305
306     float horizontalChange = exposedRect.width() * exposedRectScale / newScale - exposedRect.width();
307     float verticalChange = exposedRect.height() * exposedRectScale / newScale - exposedRect.height();
308
309     auto adjustedRect = exposedRect;
310     adjustedRect.inflate({ horizontalChange / 2, verticalChange / 2 });
311     return adjustedRect;
312 }
313
314 void WebPage::restorePageState(const HistoryItem& historyItem)
315 {
316     // When a HistoryItem is cleared, its scale factor and scroll point are set to zero. We should not try to restore the other
317     // parameters in those conditions.
318     if (!historyItem.pageScaleFactor()) {
319         send(Messages::WebPageProxy::CouldNotRestorePageState());
320         return;
321     }
322
323     // We can restore the exposed rect and scale, but we cannot touch the scroll position since the obscured insets
324     // may be changing in the UIProcess. The UIProcess can update the position from the information we send and will then
325     // scroll to the correct position through a regular VisibleContentRectUpdate.
326
327     m_userHasChangedPageScaleFactor = !historyItem.scaleIsInitial();
328
329     FrameView& frameView = *m_page->mainFrame().view();
330
331     FloatSize currentMinimumLayoutSizeInScrollViewCoordinates = m_viewportConfiguration.minimumLayoutSize();
332     if (historyItem.minimumLayoutSizeInScrollViewCoordinates() == currentMinimumLayoutSizeInScrollViewCoordinates) {
333         float boundedScale = std::min<float>(m_viewportConfiguration.maximumScale(), std::max<float>(m_viewportConfiguration.minimumScale(), historyItem.pageScaleFactor()));
334         scalePage(boundedScale, IntPoint());
335
336         std::optional<FloatPoint> scrollPosition;
337         if (historyItem.shouldRestoreScrollPosition()) {
338             m_drawingArea->setExposedContentRect(historyItem.exposedContentRect());
339             scrollPosition = FloatPoint(historyItem.scrollPosition());
340         }
341         send(Messages::WebPageProxy::RestorePageState(scrollPosition, frameView.scrollOrigin(), historyItem.obscuredInsets(), boundedScale));
342     } else {
343         IntSize oldContentSize = historyItem.contentSize();
344         IntSize newContentSize = frameView.contentsSize();
345         double visibleHorizontalFraction = static_cast<float>(historyItem.unobscuredContentRect().width()) / oldContentSize.width();
346
347         double newScale = scaleAfterViewportWidthChange(historyItem.pageScaleFactor(), !historyItem.scaleIsInitial(), m_viewportConfiguration, currentMinimumLayoutSizeInScrollViewCoordinates.width(), newContentSize, oldContentSize, visibleHorizontalFraction);
348
349         std::optional<FloatPoint> newCenter;
350         if (historyItem.shouldRestoreScrollPosition()) {
351             if (!oldContentSize.isEmpty() && !newContentSize.isEmpty() && newContentSize != oldContentSize)
352                 newCenter = relativeCenterAfterContentSizeChange(historyItem.unobscuredContentRect(), oldContentSize, newContentSize);
353             else
354                 newCenter = FloatRect(historyItem.unobscuredContentRect()).center();
355         }
356
357         scalePage(newScale, IntPoint());
358         send(Messages::WebPageProxy::RestorePageCenterAndScale(newCenter, newScale));
359     }
360 }
361
362 double WebPage::minimumPageScaleFactor() const
363 {
364     if (!m_viewportConfiguration.allowsUserScaling())
365         return m_page->pageScaleFactor();
366     return m_viewportConfiguration.minimumScale();
367 }
368
369 double WebPage::maximumPageScaleFactor() const
370 {
371     if (!m_viewportConfiguration.allowsUserScaling())
372         return m_page->pageScaleFactor();
373     return m_viewportConfiguration.maximumScale();
374 }
375
376 double WebPage::maximumPageScaleFactorIgnoringAlwaysScalable() const
377 {
378     if (!m_viewportConfiguration.allowsUserScalingIgnoringAlwaysScalable())
379         return m_page->pageScaleFactor();
380     return m_viewportConfiguration.maximumScaleIgnoringAlwaysScalable();
381 }
382
383 bool WebPage::allowsUserScaling() const
384 {
385     return m_viewportConfiguration.allowsUserScaling();
386 }
387
388 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event)
389 {
390     auto* platformEvent = event->underlyingPlatformEvent();
391     if (!platformEvent)
392         return false;
393
394     // FIXME: Interpret the event immediately upon receiving it in UI process, without sending to WebProcess first.
395     bool eventWasHandled = false;
396     bool sendResult = WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(editorState(), platformEvent->type() == PlatformKeyboardEvent::Char),
397         Messages::WebPageProxy::InterpretKeyEvent::Reply(eventWasHandled), m_pageID);
398     return sendResult && eventWasHandled;
399 }
400
401 bool WebPage::parentProcessHasServiceWorkerEntitlement() const
402 {
403     static bool hasEntitlement = WTF::hasEntitlement(WebProcess::singleton().parentProcessConnection()->xpcConnection(), "com.apple.developer.WebKit.ServiceWorkers");
404     return hasEntitlement;
405 }
406
407 void WebPage::sendComplexTextInputToPlugin(uint64_t, const String&)
408 {
409     notImplemented();
410 }
411
412 void WebPage::performDictionaryLookupAtLocation(const FloatPoint&)
413 {
414     notImplemented();
415 }
416
417 void WebPage::performDictionaryLookupForSelection(Frame&, const VisibleSelection&, TextIndicatorPresentationTransition)
418 {
419     notImplemented();
420 }
421
422 void WebPage::performDictionaryLookupForRange(Frame&, Range&, NSDictionary *, TextIndicatorPresentationTransition)
423 {
424     notImplemented();
425 }
426
427 bool WebPage::performNonEditingBehaviorForSelector(const String&, WebCore::KeyboardEvent*)
428 {
429     notImplemented();
430     return false;
431 }
432
433 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
434 {
435     notImplemented();
436     return false;
437 }
438
439 void WebPage::getSelectionContext(CallbackID callbackID)
440 {
441     Frame& frame = m_page->focusController().focusedOrMainFrame();
442     if (!frame.selection().isRange()) {
443         send(Messages::WebPageProxy::SelectionContextCallback(String(), String(), String(), callbackID));
444         return;
445     }
446     const int selectionExtendedContextLength = 350;
447     
448     String selectedText = plainTextReplacingNoBreakSpace(frame.selection().selection().toNormalizedRange().get());
449     String textBefore = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(frame.selection().selection().start(), selectionExtendedContextLength, DirectionBackward).get(), TextIteratorDefaultBehavior, true);
450     String textAfter = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(frame.selection().selection().end(), selectionExtendedContextLength, DirectionForward).get(), TextIteratorDefaultBehavior, true);
451
452     send(Messages::WebPageProxy::SelectionContextCallback(selectedText, textBefore, textAfter, callbackID));
453 }
454
455 NSObject *WebPage::accessibilityObjectForMainFramePlugin()
456 {
457     if (!m_page)
458         return nil;
459     
460     if (auto* pluginView = pluginViewForFrame(&m_page->mainFrame()))
461         return pluginView->accessibilityObject();
462     
463     return nil;
464 }
465     
466 void WebPage::registerUIProcessAccessibilityTokens(const IPC::DataReference& elementToken, const IPC::DataReference&)
467 {
468     NSData *elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
469     [m_mockAccessibilityElement setRemoteTokenData:elementTokenData];
470 }
471
472 void WebPage::readSelectionFromPasteboard(const String&, bool&)
473 {
474     notImplemented();
475 }
476
477 void WebPage::getStringSelectionForPasteboard(String&)
478 {
479     notImplemented();
480 }
481
482 void WebPage::getDataSelectionForPasteboard(const String, SharedMemory::Handle&, uint64_t&)
483 {
484     notImplemented();
485 }
486
487 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
488 {
489     notImplemented();
490     return 0;
491 }
492
493 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest&)
494 {
495     notImplemented();
496     return false;
497 }
498
499 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent&, bool&)
500 {
501     notImplemented();
502 }
503
504 void WebPage::acceptsFirstMouse(int, const WebKit::WebMouseEvent&, bool&)
505 {
506     notImplemented();
507 }
508
509 void WebPage::computePagesForPrintingPDFDocument(uint64_t, const PrintInfo&, Vector<IntRect>&)
510 {
511     notImplemented();
512 }
513
514 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef, PDFDocument *, const PrintInfo&, uint32_t, uint32_t)
515 {
516     notImplemented();
517 }
518
519 void WebPage::advanceToNextMisspelling(bool)
520 {
521     notImplemented();
522 }
523
524 IntRect WebPage::rectForElementAtInteractionLocation()
525 {
526     HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(m_lastInteractionLocation, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AllowChildFrameContent);
527     Node* hitNode = result.innerNode();
528     if (!hitNode || !hitNode->renderer())
529         return IntRect();
530     return result.innerNodeFrame()->view()->contentsToRootView(hitNode->renderer()->absoluteBoundingBoxRect(true));
531 }
532
533 void WebPage::updateSelectionAppearance()
534 {
535     Frame& frame = m_page->focusController().focusedOrMainFrame();
536     if (!frame.editor().ignoreSelectionChanges() && (frame.editor().hasComposition() || !frame.selection().selection().isNone()))
537         didChangeSelection();
538 }
539
540 void WebPage::handleSyntheticClick(Node* nodeRespondingToClick, const WebCore::FloatPoint& location)
541 {
542     IntPoint roundedAdjustedPoint = roundedIntPoint(location);
543     Frame& mainframe = m_page->mainFrame();
544
545     WKBeginObservingContentChanges(true);
546
547     mainframe.eventHandler().mouseMoved(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, WebCore::NoTap));
548     mainframe.document()->updateStyleIfNeeded();
549
550     WKStopObservingContentChanges();
551
552     m_pendingSyntheticClickNode = nullptr;
553     m_pendingSyntheticClickLocation = FloatPoint();
554
555     if (m_isClosed)
556         return;
557
558     switch (WKObservedContentChange()) {
559     case WKContentVisibilityChange:
560         // The move event caused new contents to appear. Don't send the click event.
561         return;
562     case WKContentIndeterminateChange:
563         // Wait for callback to completePendingSyntheticClickForContentChangeObserver() to decide whether to send the click event.
564         m_pendingSyntheticClickNode = nodeRespondingToClick;
565         m_pendingSyntheticClickLocation = location;
566         return;
567     case WKContentNoChange:
568         completeSyntheticClick(nodeRespondingToClick, location, WebCore::OneFingerTap);
569         return;
570     }
571     ASSERT_NOT_REACHED();
572 }
573
574 void WebPage::completePendingSyntheticClickForContentChangeObserver()
575 {
576     if (!m_pendingSyntheticClickNode)
577         return;
578     // Only dispatch the click if the document didn't get changed by any timers started by the move event.
579     if (WKObservedContentChange() == WKContentNoChange)
580         completeSyntheticClick(m_pendingSyntheticClickNode.get(), m_pendingSyntheticClickLocation, WebCore::OneFingerTap);
581
582     m_pendingSyntheticClickNode = nullptr;
583     m_pendingSyntheticClickLocation = FloatPoint();
584 }
585
586 void WebPage::completeSyntheticClick(Node* nodeRespondingToClick, const WebCore::FloatPoint& location, SyntheticClickType syntheticClickType)
587 {
588     IntPoint roundedAdjustedPoint = roundedIntPoint(location);
589     Frame& mainframe = m_page->mainFrame();
590
591     RefPtr<Frame> oldFocusedFrame = m_page->focusController().focusedFrame();
592     RefPtr<Element> oldFocusedElement = oldFocusedFrame ? oldFocusedFrame->document()->focusedElement() : nullptr;
593
594     SetForScope<bool> userIsInteractingChange { m_userIsInteracting, true };
595
596     bool tapWasHandled = false;
597     m_lastInteractionLocation = roundedAdjustedPoint;
598
599     tapWasHandled |= mainframe.eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, syntheticClickType));
600     if (m_isClosed)
601         return;
602
603     tapWasHandled |= mainframe.eventHandler().handleMouseReleaseEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, syntheticClickType));
604     if (m_isClosed)
605         return;
606
607     RefPtr<Frame> newFocusedFrame = m_page->focusController().focusedFrame();
608     RefPtr<Element> newFocusedElement = newFocusedFrame ? newFocusedFrame->document()->focusedElement() : nullptr;
609
610     // If the focus has not changed, we need to notify the client anyway, since it might be
611     // necessary to start assisting the node.
612     // If the node has been focused by JavaScript without user interaction, the
613     // keyboard is not on screen.
614     if (newFocusedElement && newFocusedElement == oldFocusedElement)
615         elementDidFocus(newFocusedElement.get());
616
617     if (!tapWasHandled || !nodeRespondingToClick || !nodeRespondingToClick->isElementNode())
618         send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(location)));
619     
620     send(Messages::WebPageProxy::DidCompleteSyntheticClick());
621 }
622
623 void WebPage::handleTap(const IntPoint& point, uint64_t lastLayerTreeTransactionId)
624 {
625     FloatPoint adjustedPoint;
626     Node* nodeRespondingToClick = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
627     Frame* frameRespondingToClick = nodeRespondingToClick ? nodeRespondingToClick->document().frame() : nullptr;
628     IntPoint adjustedIntPoint = roundedIntPoint(adjustedPoint);
629
630     if (!frameRespondingToClick || lastLayerTreeTransactionId < WebFrame::fromCoreFrame(*frameRespondingToClick)->firstLayerTreeTransactionIDAfterDidCommitLoad())
631         send(Messages::WebPageProxy::DidNotHandleTapAsClick(adjustedIntPoint));
632 #if ENABLE(DATA_DETECTION)
633     else if (is<Element>(*nodeRespondingToClick) && DataDetection::shouldCancelDefaultAction(downcast<Element>(*nodeRespondingToClick))) {
634         InteractionInformationRequest request(adjustedIntPoint);
635         requestPositionInformation(request);
636         send(Messages::WebPageProxy::DidNotHandleTapAsClick(adjustedIntPoint));
637     }
638 #endif
639     else
640         handleSyntheticClick(nodeRespondingToClick, adjustedPoint);
641 }
642
643 void WebPage::requestAssistedNodeInformation(WebKit::CallbackID callbackID)
644 {
645     AssistedNodeInformation info;
646     if (m_assistedNode)
647         getAssistedNodeInformation(info);
648
649     send(Messages::WebPageProxy::AssistedNodeInformationCallback(info, callbackID));
650 }
651
652 #if ENABLE(DATA_INTERACTION)
653 void WebPage::requestStartDataInteraction(const IntPoint& clientPosition, const IntPoint& globalPosition)
654 {
655     bool didStart = m_page->mainFrame().eventHandler().tryToBeginDataInteractionAtPoint(clientPosition, globalPosition);
656     send(Messages::WebPageProxy::DidHandleStartDataInteractionRequest(didStart));
657 }
658
659 void WebPage::requestAdditionalItemsForDragSession(const IntPoint& clientPosition, const IntPoint& globalPosition)
660 {
661     // 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.
662     // This process is opaque to the UI process, which still maintains the old drag item in its drag session. Similarly, this persistent drag session
663     // is opaque to the web process, which only sees that the current drag has ended, and that a new one is beginning.
664     PlatformMouseEvent event(clientPosition, globalPosition, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), 0, NoTap);
665     m_page->dragController().dragEnded();
666     m_page->mainFrame().eventHandler().dragSourceEndedAt(event, DragOperationNone, MayExtendDragSession::Yes);
667
668     bool didHandleDrag = m_page->mainFrame().eventHandler().tryToBeginDataInteractionAtPoint(clientPosition, globalPosition);
669     send(Messages::WebPageProxy::DidHandleAdditionalDragItemsRequest(didHandleDrag));
670 }
671
672 void WebPage::didConcludeEditDataInteraction()
673 {
674     std::optional<TextIndicatorData> textIndicatorData;
675
676     static auto defaultEditDataInteractionTextIndicatorOptions = TextIndicatorOptionIncludeSnapshotOfAllVisibleContentWithoutSelection | TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionPaintAllContent | TextIndicatorOptionIncludeMarginIfRangeMatchesSelection | TextIndicatorOptionPaintBackgrounds | TextIndicatorOptionComputeEstimatedBackgroundColor| TextIndicatorOptionUseSelectionRectForSizing | TextIndicatorOptionIncludeSnapshotWithSelectionHighlight;
677     auto& frame = m_page->focusController().focusedOrMainFrame();
678     if (auto range = frame.selection().selection().toNormalizedRange()) {
679         if (auto textIndicator = TextIndicator::createWithRange(*range, defaultEditDataInteractionTextIndicatorOptions, TextIndicatorPresentationTransition::None, FloatSize()))
680             textIndicatorData = textIndicator->data();
681     }
682
683     send(Messages::WebPageProxy::DidConcludeEditDataInteraction(textIndicatorData));
684 }
685 #endif
686
687 void WebPage::sendTapHighlightForNodeIfNecessary(uint64_t requestID, Node* node)
688 {
689 #if ENABLE(TOUCH_EVENTS)
690     if (!node)
691         return;
692
693     if (m_page->isEditable() && node == m_page->mainFrame().document()->body())
694         return;
695
696     if (is<Element>(*node)) {
697         ASSERT(m_page);
698         m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host().toString());
699     }
700
701     if (is<HTMLAreaElement>(node)) {
702         node = downcast<HTMLAreaElement>(node)->imageElement();
703         if (!node)
704             return;
705     }
706
707     Vector<FloatQuad> quads;
708     if (RenderObject *renderer = node->renderer()) {
709         renderer->absoluteQuads(quads);
710         Color highlightColor = renderer->style().tapHighlightColor();
711         if (!node->document().frame()->isMainFrame()) {
712             FrameView* view = node->document().frame()->view();
713             for (size_t i = 0; i < quads.size(); ++i) {
714                 FloatQuad& currentQuad = quads[i];
715                 currentQuad.setP1(view->contentsToRootView(roundedIntPoint(currentQuad.p1())));
716                 currentQuad.setP2(view->contentsToRootView(roundedIntPoint(currentQuad.p2())));
717                 currentQuad.setP3(view->contentsToRootView(roundedIntPoint(currentQuad.p3())));
718                 currentQuad.setP4(view->contentsToRootView(roundedIntPoint(currentQuad.p4())));
719             }
720         }
721
722         RoundedRect::Radii borderRadii;
723         if (is<RenderBox>(*renderer))
724             borderRadii = downcast<RenderBox>(*renderer).borderRadii();
725
726         send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, highlightColor, quads, roundedIntSize(borderRadii.topLeft()), roundedIntSize(borderRadii.topRight()), roundedIntSize(borderRadii.bottomLeft()), roundedIntSize(borderRadii.bottomRight())));
727     }
728 #else
729     UNUSED_PARAM(requestID);
730     UNUSED_PARAM(node);
731 #endif
732 }
733
734 void WebPage::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, uint64_t requestID)
735 {
736     FloatPoint adjustedPoint;
737     Node* nodeRespondingToClick = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
738     if (!nodeRespondingToClick || !nodeRespondingToClick->renderer()) {
739         send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(adjustedPoint)));
740         return;
741     }
742     sendTapHighlightForNodeIfNecessary(requestID, nodeRespondingToClick);
743 #if ENABLE(DATA_DETECTION)
744     if (is<Element>(*nodeRespondingToClick) && DataDetection::shouldCancelDefaultAction(downcast<Element>(*nodeRespondingToClick))) {
745         InteractionInformationRequest request(roundedIntPoint(adjustedPoint));
746         requestPositionInformation(request);
747         send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(adjustedPoint)));
748     } else
749 #endif
750         completeSyntheticClick(nodeRespondingToClick, adjustedPoint, WebCore::TwoFingerTap);
751 }
752
753 void WebPage::potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint& position)
754 {
755     m_potentialTapNode = m_page->mainFrame().nodeRespondingToClickEvents(position, m_potentialTapLocation, m_potentialTapSecurityOrigin.get());
756     sendTapHighlightForNodeIfNecessary(requestID, m_potentialTapNode.get());
757 #if ENABLE(TOUCH_EVENTS)
758     if (m_potentialTapNode && !m_potentialTapNode->allowsDoubleTapGesture())
759         send(Messages::WebPageProxy::DisableDoubleTapGesturesDuringTapIfNecessary(requestID));
760 #endif
761 }
762
763 void WebPage::commitPotentialTap(uint64_t lastLayerTreeTransactionId)
764 {
765     if (!m_potentialTapNode || (!m_potentialTapNode->renderer() && !is<HTMLAreaElement>(m_potentialTapNode.get()))) {
766         commitPotentialTapFailed();
767         return;
768     }
769
770     FloatPoint adjustedPoint;
771     Node* nodeRespondingToClick = m_page->mainFrame().nodeRespondingToClickEvents(m_potentialTapLocation, adjustedPoint, m_potentialTapSecurityOrigin.get());
772     Frame* frameRespondingToClick = nodeRespondingToClick ? nodeRespondingToClick->document().frame() : nullptr;
773
774     if (!frameRespondingToClick || lastLayerTreeTransactionId < WebFrame::fromCoreFrame(*frameRespondingToClick)->firstLayerTreeTransactionIDAfterDidCommitLoad()) {
775         commitPotentialTapFailed();
776         return;
777     }
778
779     if (m_potentialTapNode == nodeRespondingToClick) {
780 #if ENABLE(DATA_DETECTION)
781         if (is<Element>(*nodeRespondingToClick) && DataDetection::shouldCancelDefaultAction(downcast<Element>(*nodeRespondingToClick))) {
782             InteractionInformationRequest request(roundedIntPoint(m_potentialTapLocation));
783             requestPositionInformation(request);
784             commitPotentialTapFailed();
785         } else
786 #endif
787             handleSyntheticClick(nodeRespondingToClick, adjustedPoint);
788     } else
789         commitPotentialTapFailed();
790
791     m_potentialTapNode = nullptr;
792     m_potentialTapLocation = FloatPoint();
793     m_potentialTapSecurityOrigin = nullptr;
794 }
795
796 void WebPage::commitPotentialTapFailed()
797 {
798     send(Messages::WebPageProxy::CommitPotentialTapFailed());
799     send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(m_potentialTapLocation)));
800 }
801
802 void WebPage::cancelPotentialTap()
803 {
804     cancelPotentialTapInFrame(*m_mainFrame);
805 }
806
807 void WebPage::cancelPotentialTapInFrame(WebFrame& frame)
808 {
809     if (m_potentialTapNode) {
810         Frame* potentialTapFrame = m_potentialTapNode->document().frame();
811         if (potentialTapFrame && !potentialTapFrame->tree().isDescendantOf(frame.coreFrame()))
812             return;
813     }
814
815     m_potentialTapNode = nullptr;
816     m_potentialTapLocation = FloatPoint();
817     m_potentialTapSecurityOrigin = nullptr;
818 }
819
820 void WebPage::tapHighlightAtPosition(uint64_t requestID, const FloatPoint& position)
821 {
822     Frame& mainframe = m_page->mainFrame();
823     FloatPoint adjustedPoint;
824     sendTapHighlightForNodeIfNecessary(requestID, mainframe.nodeRespondingToClickEvents(position, adjustedPoint));
825 }
826
827 void WebPage::inspectorNodeSearchMovedToPosition(const FloatPoint& position)
828 {
829     IntPoint adjustedPoint = roundedIntPoint(position);
830     Frame& mainframe = m_page->mainFrame();
831
832     mainframe.eventHandler().mouseMoved(PlatformMouseEvent(adjustedPoint, adjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, false, false, false, false, { }, 0, WebCore::NoTap));
833     mainframe.document()->updateStyleIfNeeded();
834 }
835
836 void WebPage::inspectorNodeSearchEndedAtPosition(const FloatPoint& position)
837 {
838     if (Node* node = m_page->mainFrame().deepestNodeAtLocation(position))
839         node->inspect();
840 }
841
842 void WebPage::blurAssistedNode()
843 {
844     if (is<Element>(m_assistedNode.get()))
845         downcast<Element>(*m_assistedNode).blur();
846 }
847
848 void WebPage::setAssistedNodeValue(const String& value)
849 {
850     // FIXME: should also handle the case of HTMLSelectElement.
851     if (is<HTMLInputElement>(m_assistedNode.get()))
852         downcast<HTMLInputElement>(*m_assistedNode).setValue(value, DispatchInputAndChangeEvent);
853 }
854
855 void WebPage::setAssistedNodeValueAsNumber(double value)
856 {
857     if (is<HTMLInputElement>(m_assistedNode.get()))
858         downcast<HTMLInputElement>(*m_assistedNode).setValueAsNumber(value, DispatchInputAndChangeEvent);
859 }
860
861 void WebPage::setAssistedNodeSelectedIndex(uint32_t index, bool allowMultipleSelection)
862 {
863     if (is<HTMLSelectElement>(m_assistedNode.get()))
864         downcast<HTMLSelectElement>(*m_assistedNode).optionSelectedByUser(index, true, allowMultipleSelection);
865 }
866
867 void WebPage::showInspectorHighlight(const WebCore::Highlight& highlight)
868 {
869     send(Messages::WebPageProxy::ShowInspectorHighlight(highlight));
870 }
871
872 void WebPage::hideInspectorHighlight()
873 {
874     send(Messages::WebPageProxy::HideInspectorHighlight());
875 }
876
877 void WebPage::showInspectorIndication()
878 {
879     send(Messages::WebPageProxy::ShowInspectorIndication());
880 }
881
882 void WebPage::hideInspectorIndication()
883 {
884     send(Messages::WebPageProxy::HideInspectorIndication());
885 }
886
887 void WebPage::enableInspectorNodeSearch()
888 {
889     send(Messages::WebPageProxy::EnableInspectorNodeSearch());
890 }
891
892 void WebPage::disableInspectorNodeSearch()
893 {
894     send(Messages::WebPageProxy::DisableInspectorNodeSearch());
895 }
896
897 void WebPage::setForceAlwaysUserScalable(bool userScalable)
898 {
899     m_forceAlwaysUserScalable = userScalable;
900     m_viewportConfiguration.setForceAlwaysUserScalable(userScalable);
901 }
902
903 static FloatQuad innerFrameQuad(const Frame& frame, const Node& assistedNode)
904 {
905     frame.document()->updateLayoutIgnorePendingStylesheets();
906     RenderElement* renderer = nullptr;
907     if (assistedNode.hasTagName(HTMLNames::textareaTag) || assistedNode.hasTagName(HTMLNames::inputTag) || assistedNode.hasTagName(HTMLNames::selectTag))
908         renderer = downcast<RenderElement>(assistedNode.renderer());
909     else if (Element* rootEditableElement = assistedNode.rootEditableElement())
910         renderer = rootEditableElement->renderer();
911     
912     if (!renderer)
913         return FloatQuad();
914
915     auto& style = renderer->style();
916     IntRect boundingBox = renderer->absoluteBoundingBoxRect(true /* use transforms*/);
917
918     boundingBox.move(style.borderLeftWidth(), style.borderTopWidth());
919     boundingBox.setWidth(boundingBox.width() - style.borderLeftWidth() - style.borderRightWidth());
920     boundingBox.setHeight(boundingBox.height() - style.borderBottomWidth() - style.borderTopWidth());
921
922     return FloatQuad(boundingBox);
923 }
924
925 static IntPoint constrainPoint(const IntPoint& point, const Frame& frame, const Node& assistedNode)
926 {
927     ASSERT(&assistedNode.document() == frame.document());
928     const int DEFAULT_CONSTRAIN_INSET = 2;
929     IntRect innerFrame = innerFrameQuad(frame, assistedNode).enclosingBoundingBox();
930     IntPoint constrainedPoint = point;
931
932     int minX = innerFrame.x() + DEFAULT_CONSTRAIN_INSET;
933     int maxX = innerFrame.maxX() - DEFAULT_CONSTRAIN_INSET;
934     int minY = innerFrame.y() + DEFAULT_CONSTRAIN_INSET;
935     int maxY = innerFrame.maxY() - DEFAULT_CONSTRAIN_INSET;
936
937     if (point.x() < minX)
938         constrainedPoint.setX(minX);
939     else if (point.x() > maxX)
940         constrainedPoint.setX(maxX);
941
942     if (point.y() < minY)
943         constrainedPoint.setY(minY);
944     else if (point.y() >= maxY)
945         constrainedPoint.setY(maxY);
946                     
947     return constrainedPoint;
948 }
949
950 static IntRect selectionBoxForRange(WebCore::Range* range)
951 {
952     if (!range)
953         return IntRect();
954     
955     IntRect boundingRect;
956     Vector<SelectionRect> selectionRects;
957     range->collectSelectionRects(selectionRects);
958     unsigned size = selectionRects.size();
959     
960     for (unsigned i = 0; i < size; ++i) {
961         const IntRect &coreRect = selectionRects[i].rect();
962         if (!i)
963             boundingRect = coreRect;
964         else
965             boundingRect.unite(coreRect);
966     }
967     return boundingRect;
968 }
969
970 static bool canShrinkToTextSelection(Node* node)
971 {
972     if (node && !is<Element>(*node))
973         node = node->parentElement();
974     
975     auto* renderer = (node) ? node->renderer() : nullptr;
976     return renderer && renderer->childrenInline() && (is<RenderBlock>(*renderer) && !downcast<RenderBlock>(*renderer).inlineContinuation()) && !renderer->isTable();
977 }
978
979 static bool hasCustomLineHeight(Node& node)
980 {
981     auto* renderer = node.renderer();
982     return renderer && renderer->style().lineHeight().isSpecified();
983 }
984     
985 RefPtr<Range> WebPage::rangeForWebSelectionAtPosition(const IntPoint& point, const VisiblePosition& position, SelectionFlags& flags)
986 {
987     HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint((point), HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowChildFrameContent);
988
989     Node* currentNode = result.innerNode();
990     if (!currentNode)
991         return nullptr;
992     RefPtr<Range> range;
993     FloatRect boundingRectInScrollViewCoordinates;
994
995     if (!currentNode->isTextNode() && !canShrinkToTextSelection(currentNode) && hasCustomLineHeight(*currentNode)) {
996         auto* renderer = currentNode->renderer();
997         if (is<RenderBlockFlow>(renderer)) {
998             auto* renderText = downcast<RenderBlockFlow>(*renderer).findClosestTextAtAbsolutePoint(point);
999             if (renderText && renderText->textNode())
1000                 currentNode = renderText->textNode();
1001         }
1002     }
1003
1004     if (currentNode->isTextNode()) {
1005         range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
1006         if (!range || range->collapsed())
1007             range = Range::create(currentNode->document(), position, position);
1008         else {
1009             m_blockRectForTextSelection = selectionBoxForRange(range.get());
1010             range = wordRangeFromPosition(position);
1011         }
1012
1013         return range;
1014     }
1015
1016     if (!currentNode->isElementNode())
1017         currentNode = currentNode->parentElement();
1018
1019     Node* bestChoice = currentNode;
1020     while (currentNode) {
1021         if (currentNode->renderer()) {
1022             boundingRectInScrollViewCoordinates = currentNode->renderer()->absoluteBoundingBoxRect(true);
1023             boundingRectInScrollViewCoordinates.scale(m_page->pageScaleFactor());
1024             if (boundingRectInScrollViewCoordinates.width() > m_blockSelectionDesiredSize.width() && boundingRectInScrollViewCoordinates.height() > m_blockSelectionDesiredSize.height())
1025                 break;
1026             bestChoice = currentNode;
1027         }
1028         currentNode = currentNode->parentElement();
1029     }
1030
1031     if (!bestChoice)
1032         return nullptr;
1033
1034     RenderObject* renderer = bestChoice->renderer();
1035     if (!renderer || renderer->style().userSelect() == UserSelect::None)
1036         return nullptr;
1037
1038     if (renderer->childrenInline() && (is<RenderBlock>(*renderer) && !downcast<RenderBlock>(*renderer).inlineContinuation()) && !renderer->isTable()) {
1039         range = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionBackward);
1040         if (range && !range->collapsed())
1041             return range;
1042     }
1043
1044     // If all we could find is a block whose height is very close to the height
1045     // of the visible area, don't use it.
1046     const float adjustmentFactor = .97;
1047     boundingRectInScrollViewCoordinates = renderer->absoluteBoundingBoxRect(true);
1048
1049     if (boundingRectInScrollViewCoordinates.height() > m_page->mainFrame().view()->exposedContentRect().height() * adjustmentFactor)
1050         return nullptr;
1051
1052     range = Range::create(bestChoice->document());
1053     range->selectNodeContents(*bestChoice);
1054     return range->collapsed() ? nullptr : range;
1055 }
1056
1057 void WebPage::selectWithGesture(const IntPoint& point, uint32_t granularity, uint32_t gestureType, uint32_t gestureState, bool isInteractingWithAssistedNode, CallbackID callbackID)
1058 {
1059     auto& frame = m_page->focusController().focusedOrMainFrame();
1060     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode);
1061
1062     if (position.isNull()) {
1063         send(Messages::WebPageProxy::GestureCallback(point, gestureType, gestureState, 0, callbackID));
1064         return;
1065     }
1066     RefPtr<Range> range;
1067     SelectionFlags flags = None;
1068     GestureRecognizerState wkGestureState = static_cast<GestureRecognizerState>(gestureState);
1069     switch (static_cast<GestureType>(gestureType)) {
1070     case GestureType::PhraseBoundary:
1071     {
1072         if (!frame.editor().hasComposition())
1073             break;
1074         RefPtr<Range> markedRange = frame.editor().compositionRange();
1075         if (position < markedRange->startPosition())
1076             position = markedRange->startPosition();
1077         if (position > markedRange->endPosition())
1078             position = markedRange->endPosition();
1079         if (wkGestureState != GestureRecognizerState::Began)
1080             flags = distanceBetweenPositions(markedRange->startPosition(), frame.selection().selection().start()) != distanceBetweenPositions(markedRange->startPosition(), position) ? PhraseBoundaryChanged : None;
1081         else
1082             flags = PhraseBoundaryChanged;
1083         range = Range::create(*frame.document(), position, position);
1084     }
1085         break;
1086
1087     case GestureType::OneFingerTap:
1088     {
1089         VisiblePosition result;
1090         // move the position at the end of the word
1091         if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
1092             // Don't cross line boundaries.
1093             result = position;
1094         } else if (withinTextUnitOfGranularity(position, WordGranularity, DirectionForward)) {
1095             // The position lies within a word.
1096             RefPtr<Range> wordRange = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionForward);
1097             if (wordRange) {
1098                 result = wordRange->startPosition();
1099                 if (distanceBetweenPositions(position, result) > 1)
1100                     result = wordRange->endPosition();
1101             }
1102         } else if (atBoundaryOfGranularity(position, WordGranularity, DirectionBackward)) {
1103             // The position is at the end of a word.
1104             result = position;
1105         } else {
1106             // The position is not within a word.
1107             // Go to the next boundary.
1108             result = positionOfNextBoundaryOfGranularity(position, WordGranularity, DirectionForward);
1109
1110             // If there is no such boundary we go to the end of the element.
1111             if (result.isNull())
1112                 result = endOfEditableContent(position);
1113         }
1114         if (result.isNotNull())
1115             range = Range::create(*frame.document(), result, result);
1116     }
1117         break;
1118
1119     case GestureType::Loupe:
1120         if (position.rootEditableElement())
1121             range = Range::create(*frame.document(), position, position);
1122         else
1123 #if !PLATFORM(IOSMAC)
1124             range = wordRangeFromPosition(position);
1125 #else
1126             switch (wkGestureState) {
1127             case GestureRecognizerState::Began:
1128                 m_startingGestureRange = Range::create(*frame.document(), position, position);
1129                 break;
1130             case GestureRecognizerState::Changed:
1131                 if (m_startingGestureRange) {
1132                     if (m_startingGestureRange->startPosition() < position)
1133                         range = Range::create(*frame.document(), m_startingGestureRange->startPosition(), position);
1134                     else
1135                         range = Range::create(*frame.document(), position, m_startingGestureRange->startPosition());
1136                 }
1137                 break;
1138             case GestureRecognizerState::Ended:
1139             case GestureRecognizerState::Cancelled:
1140                 m_startingGestureRange = nullptr;
1141                 break;
1142             case GestureRecognizerState::Failed:
1143             case GestureRecognizerState::Possible:
1144                 ASSERT_NOT_REACHED();
1145                 break;
1146             }
1147 #endif
1148         break;
1149
1150     case GestureType::TapAndAHalf:
1151         switch (wkGestureState) {
1152         case GestureRecognizerState::Began:
1153             range = wordRangeFromPosition(position);
1154             m_currentWordRange = range ? RefPtr<Range>(Range::create(*frame.document(), range->startPosition(), range->endPosition())) : nullptr;
1155             break;
1156         case GestureRecognizerState::Changed:
1157             if (!m_currentWordRange)
1158                 break;
1159             range = Range::create(*frame.document(), m_currentWordRange->startPosition(), m_currentWordRange->endPosition());
1160             if (position < range->startPosition())
1161                 range->setStart(position.deepEquivalent());
1162             if (position > range->endPosition())
1163                 range->setEnd(position.deepEquivalent());
1164             break;
1165         case GestureRecognizerState::Ended:
1166         case GestureRecognizerState::Cancelled:
1167             m_currentWordRange = nullptr;
1168             break;
1169         case GestureRecognizerState::Failed:
1170         case GestureRecognizerState::Possible:
1171             ASSERT_NOT_REACHED();
1172         }
1173         break;
1174
1175     case GestureType::OneFingerDoubleTap:
1176         if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
1177             // Double-tap at end of line only places insertion point there.
1178             // This helps to get the callout for pasting at ends of lines,
1179             // paragraphs, and documents.
1180             range = Range::create(*frame.document(), position, position);
1181          } else
1182             range = wordRangeFromPosition(position);
1183         break;
1184
1185     case GestureType::TwoFingerSingleTap:
1186         // Single tap with two fingers selects the entire paragraph.
1187         range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
1188         break;
1189
1190     case GestureType::OneFingerTripleTap:
1191         if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
1192             // Triple-tap at end of line only places insertion point there.
1193             // This helps to get the callout for pasting at ends of lines,
1194             // paragraphs, and documents.
1195             range = Range::create(*frame.document(), position, position);
1196         } else
1197             range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
1198         break;
1199
1200     case GestureType::MakeWebSelection:
1201         if (wkGestureState == GestureRecognizerState::Began) {
1202             m_blockSelectionDesiredSize.setWidth(blockSelectionStartWidth);
1203             m_blockSelectionDesiredSize.setHeight(blockSelectionStartHeight);
1204             m_currentBlockSelection = nullptr;
1205         }
1206         range = rangeForWebSelectionAtPosition(point, position, flags);
1207         break;
1208
1209     default:
1210         break;
1211     }
1212     if (range)
1213         frame.selection().setSelectedRange(range.get(), position.affinity(), true, UserTriggered);
1214
1215     send(Messages::WebPageProxy::GestureCallback(point, gestureType, gestureState, static_cast<uint32_t>(flags), callbackID));
1216 }
1217
1218 static RefPtr<Range> rangeForPointInRootViewCoordinates(Frame& frame, const IntPoint& pointInRootViewCoordinates, bool baseIsStart)
1219 {
1220     IntPoint pointInDocument = frame.view()->rootViewToContents(pointInRootViewCoordinates);
1221     Position result;
1222     RefPtr<Range> range;
1223     
1224     HitTestResult hitTest = frame.eventHandler().hitTestResultAtPoint(pointInDocument, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AllowChildFrameContent);
1225     if (hitTest.targetNode())
1226         result = frame.eventHandler().selectionExtentRespectingEditingBoundary(frame.selection().selection(), hitTest.localPoint(), hitTest.targetNode()).deepEquivalent();
1227     else
1228         result = frame.visiblePositionForPoint(pointInDocument).deepEquivalent();
1229     
1230     VisibleSelection existingSelection = frame.selection().selection();
1231     Position selectionStart = existingSelection.visibleStart().deepEquivalent();
1232     Position selectionEnd = existingSelection.visibleEnd().deepEquivalent();
1233     
1234     if (baseIsStart) {
1235         if (comparePositions(result, selectionStart) <= 0)
1236             result = selectionStart.next();
1237         else if (&selectionStart.anchorNode()->treeScope() != &hitTest.targetNode()->treeScope())
1238             result = VisibleSelection::adjustPositionForEnd(result, selectionStart.containerNode());
1239         if (result.isNotNull())
1240             range = Range::create(*frame.document(), selectionStart, result);
1241     } else {
1242         if (comparePositions(selectionEnd, result) <= 0)
1243             result = selectionEnd.previous();
1244         else if (&hitTest.targetNode()->treeScope() != &selectionEnd.anchorNode()->treeScope())
1245             result = VisibleSelection::adjustPositionForStart(result, selectionEnd.containerNode());
1246         if (result.isNotNull())
1247             range = Range::create(*frame.document(), result, selectionEnd);
1248     }
1249     
1250     return range;
1251 }
1252
1253 static RefPtr<Range> rangeAtWordBoundaryForPosition(Frame* frame, const VisiblePosition& position, bool baseIsStart, SelectionDirection direction)
1254 {
1255     SelectionDirection sameDirection = baseIsStart ? DirectionForward : DirectionBackward;
1256     SelectionDirection oppositeDirection = baseIsStart ? DirectionBackward : DirectionForward;
1257     VisiblePosition base = baseIsStart ? frame->selection().selection().visibleStart() : frame->selection().selection().visibleEnd();
1258     VisiblePosition extent = baseIsStart ? frame->selection().selection().visibleEnd() : frame->selection().selection().visibleStart();
1259     VisiblePosition initialExtent = position;
1260
1261     if (atBoundaryOfGranularity(extent, WordGranularity, sameDirection)) {
1262         // This is a word boundary. Leave selection where it is.
1263         return nullptr;
1264     }
1265
1266     if (atBoundaryOfGranularity(extent, WordGranularity, oppositeDirection)) {
1267         // This is a word boundary in the wrong direction. Nudge the selection to a character before proceeding.
1268         extent = baseIsStart ? extent.previous() : extent.next();
1269     }
1270
1271     // Extend to the boundary of the word.
1272
1273     VisiblePosition wordBoundary = positionOfNextBoundaryOfGranularity(extent, WordGranularity, sameDirection);
1274     if (wordBoundary.isNotNull()
1275         && atBoundaryOfGranularity(wordBoundary, WordGranularity, sameDirection)
1276         && initialExtent != wordBoundary) {
1277         extent = wordBoundary;
1278         return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base);
1279     }
1280     // Conversely, if the initial extent equals the current word boundary, then
1281     // run the rest of this function to see if the selection should extend
1282     // the other direction to the other word.
1283
1284     // If this is where the extent was initially, then iterate in the other direction in the document until we hit the next word.
1285     while (extent.isNotNull()
1286         && !atBoundaryOfGranularity(extent, WordGranularity, sameDirection)
1287         && extent != base
1288         && !atBoundaryOfGranularity(extent, LineGranularity, sameDirection)
1289         && !atBoundaryOfGranularity(extent, LineGranularity, oppositeDirection)) {
1290         extent = baseIsStart ? extent.next() : extent.previous();
1291     }
1292
1293     // Don't let the smart extension make the extent equal the base.
1294     // Expand out to word boundary.
1295     if (extent.isNull() || extent == base)
1296         extent = wordBoundary;
1297     if (extent.isNull())
1298         return nullptr;
1299
1300     return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base);
1301 }
1302
1303 void WebPage::clearSelection(){
1304     m_startingGestureRange = nullptr;
1305     m_currentBlockSelection = nullptr;
1306     m_page->focusController().focusedOrMainFrame().selection().clear();
1307 }
1308
1309 void WebPage::updateSelectionWithTouches(const IntPoint& point, uint32_t touches, bool baseIsStart, CallbackID callbackID)
1310 {
1311     Frame& frame = m_page->focusController().focusedOrMainFrame();
1312     IntPoint pointInDocument = frame.view()->rootViewToContents(point);
1313     VisiblePosition position = frame.visiblePositionForPoint(pointInDocument);
1314     if (position.isNull()) {
1315         send(Messages::WebPageProxy::TouchesCallback(point, touches, 0, callbackID));
1316         return;
1317     }
1318
1319     RefPtr<Range> range;
1320     VisiblePosition result;
1321     SelectionFlags flags = None;
1322
1323     switch (static_cast<SelectionTouch>(touches)) {
1324     case SelectionTouch::Started:
1325     case SelectionTouch::EndedNotMoving:
1326         break;
1327     
1328     case SelectionTouch::Ended:
1329         if (frame.selection().selection().isContentEditable()) {
1330             result = closestWordBoundaryForPosition(position);
1331             if (result.isNotNull())
1332                 range = Range::create(*frame.document(), result, result);
1333         } else
1334             range = rangeForPointInRootViewCoordinates(frame, point, baseIsStart);
1335         break;
1336
1337     case SelectionTouch::EndedMovingForward:
1338         range = rangeAtWordBoundaryForPosition(&frame, position, baseIsStart, DirectionForward);
1339         break;
1340         
1341     case SelectionTouch::EndedMovingBackward:
1342         range = rangeAtWordBoundaryForPosition(&frame, position, baseIsStart, DirectionBackward);
1343         break;
1344
1345     case SelectionTouch::Moved:
1346         range = rangeForPointInRootViewCoordinates(frame, point, baseIsStart);
1347         break;
1348     }
1349     if (range)
1350         frame.selection().setSelectedRange(range.get(), position.affinity(), true, UserTriggered);
1351
1352     send(Messages::WebPageProxy::TouchesCallback(point, touches, flags, callbackID));
1353 }
1354
1355 void WebPage::selectWithTwoTouches(const WebCore::IntPoint& from, const WebCore::IntPoint& to, uint32_t gestureType, uint32_t gestureState, CallbackID callbackID)
1356 {
1357     Frame& frame = m_page->focusController().focusedOrMainFrame();
1358     VisiblePosition fromPosition = frame.visiblePositionForPoint(frame.view()->rootViewToContents(from));
1359     VisiblePosition toPosition = frame.visiblePositionForPoint(frame.view()->rootViewToContents(to));
1360     RefPtr<Range> range;
1361     if (fromPosition.isNotNull() && toPosition.isNotNull()) {
1362         if (fromPosition < toPosition)
1363             range = Range::create(*frame.document(), fromPosition, toPosition);
1364         else
1365             range = Range::create(*frame.document(), toPosition, fromPosition);
1366         frame.selection().setSelectedRange(range.get(), fromPosition.affinity(), true, UserTriggered);
1367     }
1368
1369     // We can use the same callback for the gestures with one point.
1370     send(Messages::WebPageProxy::GestureCallback(from, gestureType, gestureState, 0, callbackID));
1371 }
1372
1373 void WebPage::extendSelection(uint32_t granularity)
1374 {
1375     Frame& frame = m_page->focusController().focusedOrMainFrame();
1376     // For the moment we handle only WordGranularity.
1377     if (granularity != WordGranularity || !frame.selection().isCaret())
1378         return;
1379
1380     VisiblePosition position = frame.selection().selection().start();
1381     frame.selection().setSelectedRange(wordRangeFromPosition(position).get(), position.affinity(), true, UserTriggered);
1382 }
1383
1384 void WebPage::selectWordBackward()
1385 {
1386     Frame& frame = m_page->focusController().focusedOrMainFrame();
1387     if (!frame.selection().isCaret())
1388         return;
1389
1390     VisiblePosition position = frame.selection().selection().start();
1391     VisiblePosition startPosition = positionOfNextBoundaryOfGranularity(position, WordGranularity, DirectionBackward);
1392     if (startPosition.isNotNull() && startPosition != position)
1393         frame.selection().setSelectedRange(Range::create(*frame.document(), startPosition, position).ptr(), position.affinity(), true, UserTriggered);
1394 }
1395
1396 void WebPage::moveSelectionByOffset(int32_t offset, CallbackID callbackID)
1397 {
1398     Frame& frame = m_page->focusController().focusedOrMainFrame();
1399     
1400     VisiblePosition startPosition = frame.selection().selection().end();
1401     if (startPosition.isNull())
1402         return;
1403     SelectionDirection direction = offset < 0 ? DirectionBackward : DirectionForward;
1404     VisiblePosition position = startPosition;
1405     for (int i = 0; i < abs(offset); ++i) {
1406         position = positionOfNextBoundaryOfGranularity(position, CharacterGranularity, direction);
1407         if (position.isNull())
1408             break;
1409     }
1410     if (position.isNotNull() && startPosition != position)
1411         frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), position.affinity(), true, UserTriggered);
1412     send(Messages::WebPageProxy::VoidCallback(callbackID));
1413 }
1414     
1415 void WebPage::startAutoscrollAtPosition(const WebCore::FloatPoint& positionInWindow)
1416 {
1417     if (m_assistedNode && m_assistedNode->renderer())
1418         m_page->mainFrame().eventHandler().startSelectionAutoscroll(m_assistedNode->renderer(), positionInWindow);
1419     else {
1420         Frame& frame = m_page->focusController().focusedOrMainFrame();
1421         VisibleSelection selection = frame.selection().selection();
1422         if (selection.isRange()) {
1423             RefPtr<Range> range = frame.selection().toNormalizedRange();
1424             Node& node = range->startContainer();
1425             auto* renderer = node.renderer();
1426             if (renderer)
1427                 m_page->mainFrame().eventHandler().startSelectionAutoscroll(renderer, positionInWindow);
1428         }
1429     }
1430 }
1431     
1432 void WebPage::cancelAutoscroll()
1433 {
1434     m_page->mainFrame().eventHandler().cancelSelectionAutoscroll();
1435 }
1436
1437 void WebPage::getRectsForGranularityWithSelectionOffset(uint32_t granularity, int32_t offset, CallbackID callbackID)
1438 {
1439     Frame& frame = m_page->focusController().focusedOrMainFrame();
1440     VisibleSelection selection = m_storedSelectionForAccessibility.isNone() ? frame.selection().selection() : m_storedSelectionForAccessibility;
1441     VisiblePosition selectionStart = selection.visibleStart();
1442
1443     if (selectionStart.isNull()) {
1444         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1445         return;
1446     }
1447
1448     auto position = visiblePositionForPositionWithOffset(selectionStart, offset);
1449     SelectionDirection direction = offset < 0 ? DirectionBackward : DirectionForward;
1450
1451     auto range = enclosingTextUnitOfGranularity(position, static_cast<WebCore::TextGranularity>(granularity), direction);
1452     if (!range || range->collapsed()) {
1453         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1454         return;
1455     }
1456
1457     Vector<WebCore::SelectionRect> selectionRects;
1458     range->collectSelectionRectsWithoutUnionInteriorLines(selectionRects);
1459     convertSelectionRectsToRootView(frame.view(), selectionRects);
1460     send(Messages::WebPageProxy::SelectionRectsCallback(selectionRects, callbackID));
1461 }
1462
1463 void WebPage::storeSelectionForAccessibility(bool shouldStore)
1464 {
1465     if (!shouldStore)
1466         m_storedSelectionForAccessibility = VisibleSelection();
1467     else {
1468         Frame& frame = m_page->focusController().focusedOrMainFrame();
1469         m_storedSelectionForAccessibility = frame.selection().selection();
1470     }
1471 }
1472
1473 static RefPtr<Range> rangeNearPositionMatchesText(const VisiblePosition& position, RefPtr<Range> originalRange, const String& matchText, RefPtr<Range> selectionRange)
1474 {
1475     auto range = Range::create(selectionRange->ownerDocument(), selectionRange->startPosition(), position.deepEquivalent().parentAnchoredEquivalent());
1476     unsigned targetOffset = TextIterator::rangeLength(range.ptr(), true);
1477     return findClosestPlainText(*selectionRange.get(), matchText, { }, targetOffset);
1478 }
1479
1480 void WebPage::getRectsAtSelectionOffsetWithText(int32_t offset, const String& text, CallbackID callbackID)
1481 {
1482     Frame& frame = m_page->focusController().focusedOrMainFrame();
1483     uint32_t length = text.length();
1484     VisibleSelection selection = m_storedSelectionForAccessibility.isNone() ? frame.selection().selection() : m_storedSelectionForAccessibility;
1485     VisiblePosition selectionStart = selection.visibleStart();
1486     VisiblePosition selectionEnd = selection.visibleEnd();
1487
1488     if (selectionStart.isNull() || selectionEnd.isNull()) {
1489         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1490         return;
1491     }
1492
1493     auto startPosition = visiblePositionForPositionWithOffset(selectionStart, offset);
1494     auto endPosition = visiblePositionForPositionWithOffset(startPosition, length);
1495     auto range = Range::create(*frame.document(), startPosition, endPosition);
1496
1497     if (range->collapsed()) {
1498         send(Messages::WebPageProxy::SelectionRectsCallback({ }, callbackID));
1499         return;
1500     }
1501
1502     String rangeText = plainTextReplacingNoBreakSpace(range.ptr(), TextIteratorDefaultBehavior, true);
1503     if (rangeText != text) {
1504         auto selectionRange = selection.toNormalizedRange();
1505         // Try to search for a range which is the closest to the position within the selection range that matches the passed in text.
1506         if (auto wordRange = rangeNearPositionMatchesText(startPosition, range.ptr(), text, selectionRange)) {
1507             if (!wordRange->collapsed())
1508                 range = *wordRange;
1509         }
1510     }
1511
1512     Vector<WebCore::SelectionRect> selectionRects;
1513     range->collectSelectionRectsWithoutUnionInteriorLines(selectionRects);
1514     convertSelectionRectsToRootView(frame.view(), selectionRects);
1515     send(Messages::WebPageProxy::SelectionRectsCallback(selectionRects, callbackID));
1516 }
1517
1518 VisiblePosition WebPage::visiblePositionInFocusedNodeForPoint(const Frame& frame, const IntPoint& point, bool isInteractingWithAssistedNode)
1519 {
1520     IntPoint adjustedPoint(frame.view()->rootViewToContents(point));
1521     IntPoint constrainedPoint = m_assistedNode && isInteractingWithAssistedNode ? constrainPoint(adjustedPoint, frame, *m_assistedNode) : adjustedPoint;
1522     return frame.visiblePositionForPoint(constrainedPoint);
1523 }
1524
1525 void WebPage::selectPositionAtPoint(const WebCore::IntPoint& point, bool isInteractingWithAssistedNode, CallbackID callbackID)
1526 {
1527     auto& frame = m_page->focusController().focusedOrMainFrame();
1528     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode);
1529     
1530     if (position.isNotNull())
1531         frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), position.affinity(), true, UserTriggered);
1532     send(Messages::WebPageProxy::VoidCallback(callbackID));
1533 }
1534
1535 void WebPage::selectPositionAtBoundaryWithDirection(const WebCore::IntPoint& point, uint32_t granularity, uint32_t direction, bool isInteractingWithAssistedNode, CallbackID callbackID)
1536 {
1537     auto& frame = m_page->focusController().focusedOrMainFrame();
1538     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode);
1539
1540     if (position.isNotNull()) {
1541         position = positionOfNextBoundaryOfGranularity(position, static_cast<WebCore::TextGranularity>(granularity), static_cast<SelectionDirection>(direction));
1542         if (position.isNotNull())
1543             frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), UPSTREAM, true, UserTriggered);
1544     }
1545     send(Messages::WebPageProxy::VoidCallback(callbackID));
1546 }
1547
1548 void WebPage::moveSelectionAtBoundaryWithDirection(uint32_t granularity, uint32_t direction, CallbackID callbackID)
1549 {
1550     Frame& frame = m_page->focusController().focusedOrMainFrame();
1551     
1552     if (!frame.selection().selection().isNone()) {
1553         bool isForward = (direction == DirectionForward || direction == DirectionRight);
1554         VisiblePosition position = (isForward) ? frame.selection().selection().visibleEnd() : frame.selection().selection().visibleStart();
1555         position = positionOfNextBoundaryOfGranularity(position, static_cast<WebCore::TextGranularity>(granularity), static_cast<SelectionDirection>(direction));
1556         if (position.isNotNull())
1557             frame.selection().setSelectedRange(Range::create(*frame.document(), position, position).ptr(), isForward? UPSTREAM : DOWNSTREAM, true, UserTriggered);
1558     }
1559     send(Messages::WebPageProxy::VoidCallback(callbackID));
1560 }
1561
1562 RefPtr<Range> WebPage::rangeForGranularityAtPoint(Frame& frame, const WebCore::IntPoint& point, uint32_t granularity, bool isInteractingWithAssistedNode)
1563 {
1564     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode);
1565
1566     RefPtr<Range> range;
1567     switch (static_cast<WebCore::TextGranularity>(granularity)) {
1568     case WordGranularity:
1569         range = wordRangeFromPosition(position);
1570         break;
1571     case SentenceGranularity:
1572         range = enclosingTextUnitOfGranularity(position, SentenceGranularity, DirectionForward);
1573         break;
1574     case ParagraphGranularity:
1575         range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
1576         break;
1577     case DocumentGranularity:
1578         frame.selection().selectAll();
1579         break;
1580     default:
1581         break;
1582     }
1583     return range;
1584 }
1585
1586 static inline bool rectIsTooBigForSelection(const IntRect& blockRect, const Frame& frame)
1587 {
1588     const float factor = 0.97;
1589     return blockRect.height() > frame.view()->unobscuredContentRect().height() * factor;
1590 }
1591
1592 void WebPage::selectTextWithGranularityAtPoint(const WebCore::IntPoint& point, uint32_t granularity, bool isInteractingWithAssistedNode, CallbackID callbackID)
1593 {
1594     auto& frame = m_page->focusController().focusedOrMainFrame();
1595     RefPtr<Range> range = rangeForGranularityAtPoint(frame, point, granularity, isInteractingWithAssistedNode);
1596     if (!isInteractingWithAssistedNode) {
1597         m_blockSelectionDesiredSize.setWidth(blockSelectionStartWidth);
1598         m_blockSelectionDesiredSize.setHeight(blockSelectionStartHeight);
1599         m_currentBlockSelection = nullptr;
1600         auto* renderer = range ? range->startContainer().renderer() : nullptr;
1601         if (renderer && renderer->style().preserveNewline())
1602             m_blockRectForTextSelection = renderer->absoluteBoundingBoxRect(true);
1603         else {
1604             auto paragraphRange = enclosingTextUnitOfGranularity(visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode), ParagraphGranularity, DirectionForward);
1605             if (paragraphRange && !paragraphRange->collapsed())
1606                 m_blockRectForTextSelection = selectionBoxForRange(paragraphRange.get());
1607         }
1608         
1609         if (rectIsTooBigForSelection(m_blockRectForTextSelection, frame))
1610             m_blockRectForTextSelection.setHeight(0);
1611     }
1612
1613     if (range)
1614         frame.selection().setSelectedRange(range.get(), UPSTREAM, true, UserTriggered);
1615     m_initialSelection = range;
1616     send(Messages::WebPageProxy::VoidCallback(callbackID));
1617 }
1618
1619 void WebPage::beginSelectionInDirection(uint32_t direction, CallbackID callbackID)
1620 {
1621     m_selectionAnchor = (static_cast<SelectionDirection>(direction) == DirectionLeft) ? Start : End;
1622     send(Messages::WebPageProxy::UnsignedCallback(m_selectionAnchor == Start, callbackID));
1623 }
1624
1625 void WebPage::updateSelectionWithExtentPointAndBoundary(const WebCore::IntPoint& point, uint32_t granularity, bool isInteractingWithAssistedNode, CallbackID callbackID)
1626 {
1627     auto& frame = m_page->focusController().focusedOrMainFrame();
1628     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode);
1629     RefPtr<Range> newRange = rangeForGranularityAtPoint(frame, point, granularity, isInteractingWithAssistedNode);
1630     
1631     if (position.isNull() || !m_initialSelection || !newRange) {
1632         send(Messages::WebPageProxy::UnsignedCallback(false, callbackID));
1633         return;
1634     }
1635     
1636     RefPtr<Range> range;
1637     VisiblePosition selectionStart = m_initialSelection->startPosition();
1638     VisiblePosition selectionEnd = m_initialSelection->endPosition();
1639
1640     if (position > m_initialSelection->endPosition())
1641         selectionEnd = newRange->endPosition();
1642     else if (position < m_initialSelection->startPosition())
1643         selectionStart = newRange->startPosition();
1644     
1645     if (selectionStart.isNotNull() && selectionEnd.isNotNull())
1646         range = Range::create(*frame.document(), selectionStart, selectionEnd);
1647     
1648     if (range)
1649         frame.selection().setSelectedRange(range.get(), UPSTREAM, true, UserTriggered);
1650     
1651     send(Messages::WebPageProxy::UnsignedCallback(selectionStart == m_initialSelection->startPosition(), callbackID));
1652 }
1653
1654 void WebPage::updateSelectionWithExtentPoint(const WebCore::IntPoint& point, bool isInteractingWithAssistedNode, CallbackID callbackID)
1655 {
1656     auto& frame = m_page->focusController().focusedOrMainFrame();
1657     VisiblePosition position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithAssistedNode);
1658
1659     if (position.isNull()) {
1660         send(Messages::WebPageProxy::UnsignedCallback(false, callbackID));
1661         return;
1662     }
1663
1664     RefPtr<Range> range;
1665     VisiblePosition selectionStart;
1666     VisiblePosition selectionEnd;
1667     
1668     if (m_selectionAnchor == Start) {
1669         selectionStart = frame.selection().selection().visibleStart();
1670         selectionEnd = position;
1671
1672         if (position <= selectionStart) {
1673             selectionStart = selectionStart.previous();
1674             selectionEnd = frame.selection().selection().visibleEnd();
1675             m_selectionAnchor = End;
1676         }
1677     } else {
1678         selectionStart = position;
1679         selectionEnd = frame.selection().selection().visibleEnd();
1680         
1681         if (position >= selectionEnd) {
1682             selectionStart = frame.selection().selection().visibleStart();
1683             selectionEnd = selectionEnd.next();
1684             m_selectionAnchor = Start;
1685         }
1686     }
1687     
1688     if (selectionStart.isNotNull() && selectionEnd.isNotNull())
1689         range = Range::create(*frame.document(), selectionStart, selectionEnd);
1690
1691     if (range)
1692         frame.selection().setSelectedRange(range.get(), UPSTREAM, true, UserTriggered);
1693
1694     send(Messages::WebPageProxy::UnsignedCallback(m_selectionAnchor == Start, callbackID));
1695 }
1696
1697 void WebPage::convertSelectionRectsToRootView(FrameView* view, Vector<SelectionRect>& selectionRects)
1698 {
1699     for (size_t i = 0; i < selectionRects.size(); ++i) {
1700         SelectionRect& currentRect = selectionRects[i];
1701         currentRect.setRect(view->contentsToRootView(currentRect.rect()));
1702     }
1703 }
1704
1705 void WebPage::requestDictationContext(CallbackID callbackID)
1706 {
1707     Frame& frame = m_page->focusController().focusedOrMainFrame();
1708     VisiblePosition startPosition = frame.selection().selection().start();
1709     VisiblePosition endPosition = frame.selection().selection().end();
1710     const unsigned dictationContextWordCount = 5;
1711
1712     String selectedText;
1713     if (frame.selection().isRange())
1714         selectedText = plainTextReplacingNoBreakSpace(frame.selection().selection().toNormalizedRange().get());
1715
1716     String contextBefore;
1717     if (startPosition != startOfEditableContent(startPosition)) {
1718         VisiblePosition currentPosition = startPosition;
1719         VisiblePosition lastPosition = startPosition;
1720         for (unsigned i = 0; i < dictationContextWordCount; ++i) {
1721             currentPosition = startOfWord(positionOfNextBoundaryOfGranularity(lastPosition, WordGranularity, DirectionBackward));
1722             if (currentPosition.isNull())
1723                 break;
1724             lastPosition = currentPosition;
1725         }
1726         if (lastPosition.isNotNull() && lastPosition != startPosition)
1727             contextBefore = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), lastPosition, startPosition).ptr());
1728     }
1729
1730     String contextAfter;
1731     if (endPosition != endOfEditableContent(endPosition)) {
1732         VisiblePosition currentPosition = endPosition;
1733         VisiblePosition lastPosition = endPosition;
1734         for (unsigned i = 0; i < dictationContextWordCount; ++i) {
1735             currentPosition = endOfWord(positionOfNextBoundaryOfGranularity(lastPosition, WordGranularity, DirectionForward));
1736             if (currentPosition.isNull())
1737                 break;
1738             lastPosition = currentPosition;
1739         }
1740         if (lastPosition.isNotNull() && lastPosition != endPosition)
1741             contextAfter = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), endPosition, lastPosition).ptr());
1742     }
1743
1744     send(Messages::WebPageProxy::SelectionContextCallback(selectedText, contextBefore, contextAfter, callbackID));
1745 }
1746
1747 void WebPage::replaceSelectedText(const String& oldText, const String& newText)
1748 {
1749     Frame& frame = m_page->focusController().focusedOrMainFrame();
1750     RefPtr<Range> wordRange = frame.selection().isCaret() ? wordRangeFromPosition(frame.selection().selection().start()) : frame.selection().toNormalizedRange();
1751     if (plainTextReplacingNoBreakSpace(wordRange.get()) != oldText)
1752         return;
1753     
1754     frame.editor().setIgnoreSelectionChanges(true);
1755     frame.selection().setSelectedRange(wordRange.get(), UPSTREAM, true);
1756     frame.editor().insertText(newText, 0);
1757     frame.editor().setIgnoreSelectionChanges(false);
1758 }
1759
1760 void WebPage::replaceDictatedText(const String& oldText, const String& newText)
1761 {
1762     Frame& frame = m_page->focusController().focusedOrMainFrame();
1763     if (frame.selection().isNone())
1764         return;
1765     
1766     if (frame.selection().isRange()) {
1767         frame.editor().deleteSelectionWithSmartDelete(false);
1768         return;
1769     }
1770     VisiblePosition position = frame.selection().selection().start();
1771     for (size_t i = 0; i < oldText.length(); ++i)
1772         position = position.previous();
1773     if (position.isNull())
1774         position = startOfDocument(static_cast<Node*>(frame.document()->documentElement()));
1775     RefPtr<Range> range = Range::create(*frame.document(), position, frame.selection().selection().start());
1776
1777     if (plainTextReplacingNoBreakSpace(range.get()) != oldText)
1778         return;
1779
1780     // We don't want to notify the client that the selection has changed until we are done inserting the new text.
1781     frame.editor().setIgnoreSelectionChanges(true);
1782     frame.selection().setSelectedRange(range.get(), UPSTREAM, true);
1783     frame.editor().insertText(newText, 0);
1784     frame.editor().setIgnoreSelectionChanges(false);
1785 }
1786
1787 void WebPage::requestAutocorrectionData(const String& textForAutocorrection, CallbackID callbackID)
1788 {
1789     Frame& frame = m_page->focusController().focusedOrMainFrame();
1790     if (!frame.selection().isCaret()) {
1791         send(Messages::WebPageProxy::AutocorrectionDataCallback(Vector<FloatRect>(), String(), 0, 0, callbackID));
1792         return;
1793     }
1794
1795     VisiblePosition position = frame.selection().selection().start();
1796     RefPtr<Range> range = wordRangeFromPosition(position);
1797     if (!range) {
1798         send(Messages::WebPageProxy::AutocorrectionDataCallback(Vector<FloatRect>(), String(), 0, 0, callbackID));
1799         return;
1800     }
1801
1802     String textForRange = plainTextReplacingNoBreakSpace(range.get());
1803     const unsigned maxSearchAttempts = 5;
1804     for (size_t i = 0;  i < maxSearchAttempts && textForRange != textForAutocorrection; ++i)
1805     {
1806         position = range->startPosition().previous();
1807         if (position.isNull() || position == range->startPosition())
1808             break;
1809         range = Range::create(*frame.document(), wordRangeFromPosition(position)->startPosition(), range->endPosition());
1810         textForRange = plainTextReplacingNoBreakSpace(range.get());
1811     }
1812
1813     Vector<SelectionRect> selectionRects;
1814     if (textForRange == textForAutocorrection)
1815         range->collectSelectionRects(selectionRects);
1816
1817     Vector<FloatRect> rectsForText;
1818     rectsForText.grow(selectionRects.size());
1819
1820     convertSelectionRectsToRootView(frame.view(), selectionRects);
1821     for (size_t i = 0; i < selectionRects.size(); i++)
1822         rectsForText[i] = selectionRects[i].rect();
1823
1824     bool multipleFonts = false;
1825     CTFontRef font = nil;
1826     if (auto* coreFont = frame.editor().fontForSelection(multipleFonts))
1827         font = coreFont->getCTFont();
1828
1829     CGFloat fontSize = CTFontGetSize(font);
1830     uint64_t fontTraits = CTFontGetSymbolicTraits(font);
1831     RetainPtr<NSString> fontName = adoptNS((NSString *)CTFontCopyFamilyName(font));
1832     send(Messages::WebPageProxy::AutocorrectionDataCallback(rectsForText, fontName.get(), fontSize, fontTraits, callbackID));
1833 }
1834
1835 void WebPage::applyAutocorrection(const String& correction, const String& originalText, CallbackID callbackID)
1836 {
1837     bool correctionApplied;
1838     syncApplyAutocorrection(correction, originalText, correctionApplied);
1839     send(Messages::WebPageProxy::StringCallback(correctionApplied ? correction : String(), callbackID));
1840 }
1841
1842 Seconds WebPage::eventThrottlingDelay() const
1843 {
1844     auto behaviorOverride = m_page->eventThrottlingBehaviorOverride();
1845     if (behaviorOverride) {
1846         switch (behaviorOverride.value()) {
1847         case EventThrottlingBehavior::Responsive:
1848             return 0_s;
1849         case EventThrottlingBehavior::Unresponsive:
1850             return 1_s;
1851         }
1852     }
1853
1854     if (m_isInStableState || m_estimatedLatency <= Seconds(1.0 / 60))
1855         return 0_s;
1856
1857     return std::min(m_estimatedLatency * 2, 1_s);
1858 }
1859
1860 void WebPage::syncApplyAutocorrection(const String& correction, const String& originalText, bool& correctionApplied)
1861 {
1862     correctionApplied = false;
1863
1864     Frame& frame = m_page->focusController().focusedOrMainFrame();
1865     if (!frame.selection().isCaretOrRange())
1866         return;
1867
1868     RefPtr<Range> range;
1869     String textForRange;
1870
1871     if (frame.selection().isCaret()) {
1872         VisiblePosition position = frame.selection().selection().start();
1873         range = wordRangeFromPosition(position);
1874         textForRange = plainTextReplacingNoBreakSpace(range.get());
1875         if (textForRange != originalText) {
1876             // Search for the original text before the selection caret.
1877             for (size_t i = 0; i < originalText.length(); ++i)
1878                 position = position.previous();
1879             if (position.isNull())
1880                 position = startOfDocument(static_cast<Node*>(frame.document()->documentElement()));
1881             range = Range::create(*frame.document(), position, frame.selection().selection().start());
1882             if (range)
1883                 textForRange = (range) ? plainTextReplacingNoBreakSpace(range.get()) : emptyString();
1884             unsigned loopCount = 0;
1885             const unsigned maxPositionsAttempts = 10;
1886             while (textForRange.length() && textForRange.length() > originalText.length() && loopCount < maxPositionsAttempts) {
1887                 position = position.next();
1888                 if (position.isNotNull() && position >= frame.selection().selection().start())
1889                     range = nullptr;
1890                 else
1891                     range = Range::create(*frame.document(), position, frame.selection().selection().start());
1892                 textForRange = (range) ? plainTextReplacingNoBreakSpace(range.get()) : emptyString();
1893                 loopCount++;
1894             }
1895         }
1896     } else {
1897         // Range selection.
1898         range = frame.selection().toNormalizedRange();
1899         if (!range) {
1900             correctionApplied = false;
1901             return;
1902         }
1903         textForRange = plainTextReplacingNoBreakSpace(range.get());
1904     }
1905
1906     if (textForRange != originalText) {
1907         correctionApplied = false;
1908         return;
1909     }
1910     
1911     // Correctly determine affinity, using logic currently only present in VisiblePosition
1912     EAffinity affinity = DOWNSTREAM;
1913     if (range && range->collapsed())
1914         affinity = VisiblePosition(range->startPosition(), UPSTREAM).affinity();
1915     
1916     frame.selection().setSelectedRange(range.get(), affinity, true);
1917     if (correction.length())
1918         frame.editor().insertText(correction, 0, originalText.isEmpty() ? TextEventInputKeyboard : TextEventInputAutocompletion);
1919     else
1920         frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, false, true);
1921     correctionApplied = true;
1922 }
1923
1924 static void computeAutocorrectionContext(Frame& frame, String& contextBefore, String& markedText, String& selectedText, String& contextAfter, uint64_t& location, uint64_t& length)
1925 {
1926     RefPtr<Range> range;
1927     VisiblePosition startPosition = frame.selection().selection().start();
1928     VisiblePosition endPosition = frame.selection().selection().end();
1929     location = NSNotFound;
1930     length = 0;
1931     const unsigned minContextWordCount = 3;
1932     const unsigned minContextLenght = 12;
1933     const unsigned maxContextLength = 30;
1934
1935     if (frame.selection().isRange())
1936         selectedText = plainTextReplacingNoBreakSpace(frame.selection().selection().toNormalizedRange().get());
1937
1938     if (auto compositionRange = frame.editor().compositionRange()) {
1939         range = Range::create(*frame.document(), compositionRange->startPosition(), startPosition);
1940         String markedTextBefore;
1941         if (range)
1942             markedTextBefore = plainTextReplacingNoBreakSpace(range.get());
1943         range = Range::create(*frame.document(), endPosition, compositionRange->endPosition());
1944         String markedTextAfter;
1945         if (range)
1946             markedTextAfter = plainTextReplacingNoBreakSpace(range.get());
1947         markedText = markedTextBefore + selectedText + markedTextAfter;
1948         if (!markedText.isEmpty()) {
1949             location = markedTextBefore.length();
1950             length = selectedText.length();
1951         }
1952     } else {
1953         if (startPosition != startOfEditableContent(startPosition)) {
1954             VisiblePosition currentPosition = startPosition;
1955             VisiblePosition previousPosition;
1956             unsigned totalContextLength = 0;
1957             for (unsigned i = 0; i < minContextWordCount; ++i) {
1958                 if (contextBefore.length() >= minContextLenght)
1959                     break;
1960                 previousPosition = startOfWord(positionOfNextBoundaryOfGranularity(currentPosition, WordGranularity, DirectionBackward));
1961                 if (previousPosition.isNull())
1962                     break;
1963                 String currentWord = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), previousPosition, currentPosition).ptr());
1964                 totalContextLength += currentWord.length();
1965                 if (totalContextLength >= maxContextLength)
1966                     break;
1967                 currentPosition = previousPosition;
1968             }
1969             if (currentPosition.isNotNull() && currentPosition != startPosition) {
1970                 contextBefore = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), currentPosition, startPosition).ptr());
1971                 if (atBoundaryOfGranularity(currentPosition, ParagraphGranularity, DirectionBackward))
1972                     contextBefore = makeString("\n "_s, contextBefore);
1973             }
1974         }
1975
1976         if (endPosition != endOfEditableContent(endPosition)) {
1977             VisiblePosition nextPosition;
1978             if (!atBoundaryOfGranularity(endPosition, WordGranularity, DirectionForward) && withinTextUnitOfGranularity(endPosition, WordGranularity, DirectionForward))
1979                 nextPosition = positionOfNextBoundaryOfGranularity(endPosition, WordGranularity, DirectionForward);
1980             if (nextPosition.isNotNull())
1981                 contextAfter = plainTextReplacingNoBreakSpace(Range::create(*frame.document(), endPosition, nextPosition).ptr());
1982         }
1983     }
1984 }
1985
1986 void WebPage::requestAutocorrectionContext(CallbackID callbackID)
1987 {
1988     String contextBefore;
1989     String contextAfter;
1990     String selectedText;
1991     String markedText;
1992     uint64_t location;
1993     uint64_t length;
1994
1995     computeAutocorrectionContext(m_page->focusController().focusedOrMainFrame(), contextBefore, markedText, selectedText, contextAfter, location, length);
1996
1997     send(Messages::WebPageProxy::AutocorrectionContextCallback(contextBefore, markedText, selectedText, contextAfter, location, length, callbackID));
1998 }
1999
2000 void WebPage::getAutocorrectionContext(String& contextBefore, String& markedText, String& selectedText, String& contextAfter, uint64_t& location, uint64_t& length)
2001 {
2002     computeAutocorrectionContext(m_page->focusController().focusedOrMainFrame(), contextBefore, markedText, selectedText, contextAfter, location, length);
2003 }
2004
2005 static HTMLAnchorElement* containingLinkElement(Element* element)
2006 {
2007     for (auto& currentElement : elementLineage(element)) {
2008         if (currentElement.isLink() && is<HTMLAnchorElement>(currentElement))
2009             return downcast<HTMLAnchorElement>(&currentElement);
2010     }
2011     return nullptr;
2012 }
2013
2014 static inline bool isAssistableElement(Element& node)
2015 {
2016     if (is<HTMLSelectElement>(node))
2017         return true;
2018     if (is<HTMLTextAreaElement>(node))
2019         return true;
2020     if (is<HTMLInputElement>(node)) {
2021         HTMLInputElement& inputElement = downcast<HTMLInputElement>(node);
2022         // FIXME: This laundry list of types is not a good way to factor this. Need a suitable function on HTMLInputElement itself.
2023 #if ENABLE(INPUT_TYPE_COLOR)
2024         if (inputElement.isColorControl())
2025             return true;
2026 #endif
2027         return inputElement.isTextField() || inputElement.isDateField() || inputElement.isDateTimeLocalField() || inputElement.isMonthField() || inputElement.isTimeField();
2028     }
2029     return node.isContentEditable();
2030 }
2031
2032 void WebPage::getPositionInformation(const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
2033 {
2034     info.request = request;
2035
2036     FloatPoint adjustedPoint;
2037     Node* hitNode = m_page->mainFrame().nodeRespondingToClickEvents(request.point, adjustedPoint);
2038
2039     info.nodeAtPositionIsAssistedNode = (hitNode == m_assistedNode);
2040     if (m_assistedNode) {
2041         const Frame& frame = m_page->focusController().focusedOrMainFrame();
2042         if (frame.editor().hasComposition()) {
2043             const uint32_t kHitAreaWidth = 66;
2044             const uint32_t kHitAreaHeight = 66;
2045             FrameView& view = *frame.view();
2046             IntPoint adjustedPoint(view.rootViewToContents(request.point));
2047             IntPoint constrainedPoint = m_assistedNode ? constrainPoint(adjustedPoint, frame, *m_assistedNode) : adjustedPoint;
2048             VisiblePosition position = frame.visiblePositionForPoint(constrainedPoint);
2049
2050             RefPtr<Range> compositionRange = frame.editor().compositionRange();
2051             if (position < compositionRange->startPosition())
2052                 position = compositionRange->startPosition();
2053             else if (position > compositionRange->endPosition())
2054                 position = compositionRange->endPosition();
2055             IntRect caretRect = view.contentsToRootView(position.absoluteCaretBounds());
2056             float deltaX = abs(caretRect.x() + (caretRect.width() / 2) - request.point.x());
2057             float deltaYFromTheTop = abs(caretRect.y() - request.point.y());
2058             float deltaYFromTheBottom = abs(caretRect.y() + caretRect.height() - request.point.y());
2059
2060             info.isNearMarkedText = !(deltaX > kHitAreaWidth || deltaYFromTheTop > kHitAreaHeight || deltaYFromTheBottom > kHitAreaHeight);
2061         }
2062     }
2063     bool elementIsLinkOrImage = false;
2064     if (hitNode) {
2065         Element* element = is<Element>(*hitNode) ? downcast<Element>(hitNode) : nullptr;
2066         if (element) {
2067             info.isElement = true;
2068             info.idAttribute = element->getIdAttribute();
2069             Element* linkElement = nullptr;
2070             if (element->renderer() && element->renderer()->isRenderImage()) {
2071                 elementIsLinkOrImage = true;
2072                 linkElement = containingLinkElement(element);
2073             } else if (element->isLink()) {
2074                 linkElement = element;
2075                 elementIsLinkOrImage = true;
2076             }
2077
2078             if (elementIsLinkOrImage) {
2079                 if (linkElement) {
2080                     info.isLink = true;
2081
2082                     if (request.includeSnapshot) {
2083                         // Ensure that the image contains at most 600K pixels, so that it is not too big.
2084                         if (RefPtr<WebImage> snapshot = snapshotNode(*element, SnapshotOptionsShareable, 600 * 1024))
2085                             info.image = &snapshot->bitmap();
2086                     }
2087
2088                     if (request.includeLinkIndicator) {
2089                         RefPtr<Range> linkRange = rangeOfContents(*linkElement);
2090                         if (linkRange) {
2091                             float deviceScaleFactor = corePage()->deviceScaleFactor();
2092                             const float marginInPoints = 4;
2093
2094                             RefPtr<TextIndicator> textIndicator = TextIndicator::createWithRange(*linkRange, TextIndicatorOptionTightlyFitContent | TextIndicatorOptionRespectTextColor | TextIndicatorOptionPaintBackgrounds | TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges |
2095                                 TextIndicatorOptionIncludeMarginIfRangeMatchesSelection, TextIndicatorPresentationTransition::None, FloatSize(marginInPoints * deviceScaleFactor, marginInPoints * deviceScaleFactor));
2096
2097                             if (textIndicator)
2098                                 info.linkIndicator = textIndicator->data();
2099                         }
2100                     }
2101
2102 #if ENABLE(DATA_DETECTION)
2103                     info.isDataDetectorLink = DataDetection::isDataDetectorLink(*element);
2104                     if (info.isDataDetectorLink) {
2105                         const int dataDetectionExtendedContextLength = 350;
2106                         info.dataDetectorIdentifier = DataDetection::dataDetectorIdentifier(*element);
2107                         info.dataDetectorResults = element->document().frame()->dataDetectionResults();
2108                         if (DataDetection::requiresExtendedContext(*element)) {
2109                             RefPtr<Range> linkRange = Range::create(element->document());
2110                             linkRange->selectNodeContents(*element);
2111                             info.textBefore = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(linkRange->startPosition(), dataDetectionExtendedContextLength, DirectionBackward).get(), TextIteratorDefaultBehavior, true);
2112                             info.textAfter = plainTextReplacingNoBreakSpace(rangeExpandedByCharactersInDirectionAtWordBoundary(linkRange->endPosition(), dataDetectionExtendedContextLength, DirectionForward).get(), TextIteratorDefaultBehavior, true);
2113                         }
2114                     }
2115 #endif
2116                 }
2117                 if (element->renderer() && element->renderer()->isRenderImage()) {
2118                     info.isImage = true;
2119                     auto& renderImage = downcast<RenderImage>(*(element->renderer()));
2120                     if (renderImage.cachedImage() && !renderImage.cachedImage()->errorOccurred()) {
2121                         if (Image* image = renderImage.cachedImage()->imageForRenderer(&renderImage)) {
2122                             if (image->width() > 1 && image->height() > 1) {
2123                                 info.imageURL = element->document().completeURL(renderImage.cachedImage()->url());
2124                                 info.isAnimatedImage = image->isAnimated();
2125
2126                                 if (request.includeSnapshot) {
2127                                     FloatSize screenSizeInPixels = screenSize();
2128                                     screenSizeInPixels.scale(corePage()->deviceScaleFactor());
2129                                     FloatSize scaledSize = largestRectWithAspectRatioInsideRect(image->size().width() / image->size().height(), FloatRect(0, 0, screenSizeInPixels.width(), screenSizeInPixels.height())).size();
2130                                     FloatSize bitmapSize = scaledSize.width() < image->size().width() ? scaledSize : image->size();
2131                                     // FIXME: Only select ExtendedColor on images known to need wide gamut
2132                                     ShareableBitmap::Configuration bitmapConfiguration;
2133                                     bitmapConfiguration.colorSpace.cgColorSpace = screenColorSpace(m_page->mainFrame().view());
2134                                     if (RefPtr<ShareableBitmap> sharedBitmap = ShareableBitmap::createShareable(IntSize(bitmapSize), bitmapConfiguration)) {
2135                                         auto graphicsContext = sharedBitmap->createGraphicsContext();
2136                                         graphicsContext->drawImage(*image, FloatRect(0, 0, bitmapSize.width(), bitmapSize.height()));
2137                                         info.image = sharedBitmap;
2138                                     }
2139                                 }
2140                             }
2141                         }
2142                     }
2143                 }
2144             }
2145             if (linkElement)
2146                 info.url = linkElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(linkElement->getAttribute(HTMLNames::hrefAttr)));
2147             info.title = element->attributeWithoutSynchronization(HTMLNames::titleAttr).string();
2148             if (linkElement && info.title.isEmpty())
2149                 info.title = element->innerText();
2150             if (element->renderer())
2151                 info.touchCalloutEnabled = element->renderer()->style().touchCalloutEnabled();
2152
2153             if (RenderElement* renderer = element->renderer()) {
2154                 if (renderer->isRenderImage())
2155                     info.bounds = downcast<RenderImage>(*renderer).absoluteContentQuad().enclosingBoundingBox();
2156                 else
2157                     info.bounds = renderer->absoluteBoundingBoxRect();
2158
2159                 if (!renderer->document().frame()->isMainFrame()) {
2160                     FrameView *view = renderer->document().frame()->view();
2161                     info.bounds = view->contentsToRootView(info.bounds);
2162                 }
2163             }
2164         }
2165     }
2166
2167     if (!elementIsLinkOrImage) {
2168         HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(request.point, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowChildFrameContent);
2169         hitNode = result.innerNode();
2170         // Hit test could return HTMLHtmlElement that has no renderer, if the body is smaller than the document.
2171         if (hitNode && hitNode->renderer()) {
2172             RenderObject* renderer = hitNode->renderer();
2173             m_page->focusController().setFocusedFrame(result.innerNodeFrame());
2174             info.bounds = renderer->absoluteBoundingBoxRect(true);
2175             // We don't want to select blocks that are larger than 97% of the visible area of the document.
2176             if (is<HTMLAttachmentElement>(*hitNode)) {
2177                 info.isAttachment = true;
2178                 const HTMLAttachmentElement& attachment = downcast<HTMLAttachmentElement>(*hitNode);
2179                 info.title = attachment.attachmentTitle();
2180                 if (attachment.file())
2181                     info.url = URL::fileURLWithFileSystemPath(downcast<HTMLAttachmentElement>(*hitNode).file()->path());
2182             } else {
2183                 info.isSelectable = renderer->style().userSelect() != UserSelect::None;
2184                 if (info.isSelectable && !hitNode->isTextNode())
2185                     info.isSelectable = !isAssistableElement(*downcast<Element>(hitNode)) && !rectIsTooBigForSelection(info.bounds, *result.innerNodeFrame());
2186             }
2187 #if PLATFORM(IOSMAC)
2188             bool isInsideFixedPosition;
2189             VisiblePosition caretPosition(renderer->positionForPoint(request.point, nullptr));
2190             info.caretRect = caretPosition.absoluteCaretBounds(&isInsideFixedPosition);
2191 #endif
2192         }
2193     }
2194
2195 #if ENABLE(DATA_INTERACTION)
2196     info.hasSelectionAtPosition = m_page->hasSelectionAtPosition(adjustedPoint);
2197 #endif
2198     info.adjustedPointForNodeRespondingToClickEvents = adjustedPoint;
2199 }
2200
2201 void WebPage::requestPositionInformation(const InteractionInformationRequest& request)
2202 {
2203     InteractionInformationAtPosition info;
2204
2205     getPositionInformation(request, info);
2206     send(Messages::WebPageProxy::DidReceivePositionInformation(info));
2207 }
2208
2209 void WebPage::startInteractionWithElementAtPosition(const WebCore::IntPoint& point)
2210 {
2211     FloatPoint adjustedPoint;
2212     m_interactionNode = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
2213 }
2214
2215 void WebPage::stopInteraction()
2216 {
2217     m_interactionNode = nullptr;
2218 }
2219
2220 void WebPage::performActionOnElement(uint32_t action)
2221 {
2222     if (!is<HTMLElement>(m_interactionNode.get()))
2223         return;
2224
2225     HTMLElement& element = downcast<HTMLElement>(*m_interactionNode);
2226     if (!element.renderer())
2227         return;
2228
2229     if (static_cast<SheetAction>(action) == SheetAction::Copy) {
2230         if (is<RenderImage>(*element.renderer())) {
2231             URL url;
2232             String title;
2233             if (auto* linkElement = containingLinkElement(&element)) {
2234                 url = linkElement->href();
2235                 title = linkElement->attributeWithoutSynchronization(HTMLNames::titleAttr);
2236                 if (!title.length())
2237                     title = linkElement->textContent();
2238                 title = stripLeadingAndTrailingHTMLSpaces(title);
2239             }
2240             m_interactionNode->document().frame()->editor().writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), element, url, title);
2241         } else if (element.isLink()) {
2242             m_interactionNode->document().frame()->editor().copyURL(element.document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.attributeWithoutSynchronization(HTMLNames::hrefAttr))), element.textContent());
2243         }
2244     } else if (static_cast<SheetAction>(action) == SheetAction::SaveImage) {
2245         if (!is<RenderImage>(*element.renderer()))
2246             return;
2247         CachedImage* cachedImage = downcast<RenderImage>(*element.renderer()).cachedImage();
2248         if (!cachedImage)
2249             return;
2250         RefPtr<SharedBuffer> buffer = cachedImage->resourceBuffer();
2251         if (!buffer)
2252             return;
2253         uint64_t bufferSize = buffer->size();
2254         RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::allocate(bufferSize);
2255         memcpy(sharedMemoryBuffer->data(), buffer->data(), bufferSize);
2256         SharedMemory::Handle handle;
2257         sharedMemoryBuffer->createHandle(handle, SharedMemory::Protection::ReadOnly);
2258         send(Messages::WebPageProxy::SaveImageToLibrary(handle, bufferSize));
2259     }
2260 }
2261
2262 static inline Element* nextAssistableElement(Node* startNode, Page& page, bool isForward)
2263 {
2264     if (!is<Element>(startNode))
2265         return nullptr;
2266
2267     Element* nextElement = downcast<Element>(startNode);
2268     do {
2269         nextElement = isForward
2270             ? page.focusController().nextFocusableElement(*nextElement)
2271             : page.focusController().previousFocusableElement(*nextElement);
2272     } while (nextElement && !isAssistableElement(*nextElement));
2273
2274     return nextElement;
2275 }
2276
2277 void WebPage::focusNextAssistedNode(bool isForward, CallbackID callbackID)
2278 {
2279     Element* nextElement = nextAssistableElement(m_assistedNode.get(), *m_page, isForward);
2280     m_userIsInteracting = true;
2281     if (nextElement)
2282         nextElement->focus();
2283     m_userIsInteracting = false;
2284     send(Messages::WebPageProxy::VoidCallback(callbackID));
2285 }
2286
2287 static IntRect elementRectInRootViewCoordinates(const Node& node, const Frame& frame)
2288 {
2289     auto* view = frame.view();
2290     if (!view)
2291         return { };
2292
2293     auto* renderer = node.renderer();
2294     if (!renderer)
2295         return { };
2296
2297     return view->contentsToRootView(renderer->absoluteBoundingBoxRect());
2298 }
2299
2300 void WebPage::getAssistedNodeInformation(AssistedNodeInformation& information)
2301 {
2302     layoutIfNeeded();
2303
2304     // FIXME: information.selectionRect should be set to the actual selection rect, but when this is called at focus time
2305     // we don't have a selection yet. Using the last interaction location is a reasonable approximation for now.
2306     information.selectionRect = IntRect(m_lastInteractionLocation, IntSize(1, 1));
2307
2308     if (RenderObject* renderer = m_assistedNode->renderer()) {
2309         Frame& elementFrame = m_page->focusController().focusedOrMainFrame();
2310         information.elementRect = elementRectInRootViewCoordinates(*m_assistedNode, elementFrame);
2311         information.nodeFontSize = renderer->style().fontDescription().computedSize();
2312
2313         bool inFixed = false;
2314         renderer->localToContainerPoint(FloatPoint(), nullptr, UseTransforms, &inFixed);
2315         information.insideFixedPosition = inFixed;
2316         information.isRTL = renderer->style().direction() == TextDirection::RTL;
2317
2318         FrameView* frameView = elementFrame.view();
2319         if (inFixed && elementFrame.isMainFrame() && !frameView->frame().settings().visualViewportEnabled()) {
2320             IntRect currentFixedPositionRect = frameView->customFixedPositionLayoutRect();
2321             frameView->setCustomFixedPositionLayoutRect(frameView->renderView()->documentRect());
2322             information.elementRect = frameView->contentsToRootView(renderer->absoluteBoundingBoxRect());
2323             frameView->setCustomFixedPositionLayoutRect(currentFixedPositionRect);
2324             
2325             if (!information.elementRect.contains(m_lastInteractionLocation))
2326                 information.selectionRect.setLocation(information.elementRect.location());
2327         } else {
2328             // Don't use the selection rect if interaction was outside the element rect.
2329             if (!information.elementRect.contains(m_lastInteractionLocation))
2330                 information.selectionRect = IntRect();
2331         }
2332     } else
2333         information.elementRect = IntRect();
2334
2335     information.minimumScaleFactor = minimumPageScaleFactor();
2336     information.maximumScaleFactor = maximumPageScaleFactor();
2337     information.maximumScaleFactorIgnoringAlwaysScalable = maximumPageScaleFactorIgnoringAlwaysScalable();
2338     information.allowsUserScaling = m_viewportConfiguration.allowsUserScaling();
2339     information.allowsUserScalingIgnoringAlwaysScalable = m_viewportConfiguration.allowsUserScalingIgnoringAlwaysScalable();
2340     if (auto* nextElement = nextAssistableElement(m_assistedNode.get(), *m_page, true)) {
2341         if (auto* frame = nextElement->document().frame())
2342             information.nextNodeRect = elementRectInRootViewCoordinates(*nextElement, *frame);
2343         information.hasNextNode = true;
2344     }
2345     if (auto* previousElement = nextAssistableElement(m_assistedNode.get(), *m_page, false)) {
2346         if (auto* frame = previousElement->document().frame())
2347             information.previousNodeRect = elementRectInRootViewCoordinates(*previousElement, *frame);
2348         information.hasPreviousNode = true;
2349     }
2350     information.assistedNodeIdentifier = m_currentAssistedNodeIdentifier;
2351
2352     if (is<LabelableElement>(*m_assistedNode)) {
2353         auto labels = downcast<LabelableElement>(*m_assistedNode).labels();
2354         Vector<Ref<Element>> associatedLabels;
2355         for (unsigned index = 0; index < labels->length(); ++index) {
2356             if (is<Element>(labels->item(index)) && labels->item(index)->renderer())
2357                 associatedLabels.append(downcast<Element>(*labels->item(index)));
2358         }
2359         for (auto& labelElement : associatedLabels) {
2360             auto text = labelElement->innerText();
2361             if (!text.isEmpty()) {
2362                 information.label = WTFMove(text);
2363                 break;
2364             }
2365         }
2366     }
2367
2368     if (is<Element>(m_assistedNode.get())) {
2369         auto& element = downcast<Element>(*m_assistedNode);
2370         information.title = element.title();
2371         information.ariaLabel = element.attributeWithoutSynchronization(HTMLNames::aria_labelAttr);
2372     }
2373
2374     if (is<HTMLSelectElement>(*m_assistedNode)) {
2375         HTMLSelectElement& element = downcast<HTMLSelectElement>(*m_assistedNode);
2376         information.elementType = InputType::Select;
2377         const Vector<HTMLElement*>& items = element.listItems();
2378         size_t count = items.size();
2379         int parentGroupID = 0;
2380         // The parent group ID indicates the group the option belongs to and is 0 for group elements.
2381         // If there are option elements in between groups, they are given it's own group identifier.
2382         // If a select does not have groups, all the option elements have group ID 0.
2383         for (size_t i = 0; i < count; ++i) {
2384             HTMLElement* item = items[i];
2385             if (is<HTMLOptionElement>(*item)) {
2386                 HTMLOptionElement& option = downcast<HTMLOptionElement>(*item);
2387                 information.selectOptions.append(OptionItem(option.text(), false, parentGroupID, option.selected(), option.hasAttributeWithoutSynchronization(WebCore::HTMLNames::disabledAttr)));
2388             } else if (is<HTMLOptGroupElement>(*item)) {
2389                 HTMLOptGroupElement& group = downcast<HTMLOptGroupElement>(*item);
2390                 parentGroupID++;
2391                 information.selectOptions.append(OptionItem(group.groupLabelText(), true, 0, false, group.hasAttributeWithoutSynchronization(WebCore::HTMLNames::disabledAttr)));
2392             }
2393         }
2394         information.selectedIndex = element.selectedIndex();
2395         information.isMultiSelect = element.multiple();
2396     } else if (is<HTMLTextAreaElement>(*m_assistedNode)) {
2397         HTMLTextAreaElement& element = downcast<HTMLTextAreaElement>(*m_assistedNode);
2398         information.autocapitalizeType = element.autocapitalizeType();
2399         information.isAutocorrect = element.shouldAutocorrect();
2400         information.elementType = InputType::TextArea;
2401         information.isReadOnly = element.isReadOnly();
2402         information.value = element.value();
2403         information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
2404         information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
2405     } else if (is<HTMLInputElement>(*m_assistedNode)) {
2406         HTMLInputElement& element = downcast<HTMLInputElement>(*m_assistedNode);
2407         HTMLFormElement* form = element.form();
2408         if (form)
2409             information.formAction = form->getURLAttribute(WebCore::HTMLNames::actionAttr);
2410         if (auto autofillElements = WebCore::AutofillElements::computeAutofillElements(element)) {
2411             information.acceptsAutofilledLoginCredentials = true;
2412             information.isAutofillableUsernameField = autofillElements->username() == m_assistedNode;
2413         }
2414         information.representingPageURL = element.document().urlForBindings();
2415         information.autocapitalizeType = element.autocapitalizeType();
2416         information.isAutocorrect = element.shouldAutocorrect();
2417         information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
2418         if (element.isPasswordField())
2419             information.elementType = InputType::Password;
2420         else if (element.isSearchField())
2421             information.elementType = InputType::Search;
2422         else if (element.isEmailField())
2423             information.elementType = InputType::Email;
2424         else if (element.isTelephoneField())
2425             information.elementType = InputType::Phone;
2426         else if (element.isNumberField())
2427             information.elementType = element.getAttribute("pattern") == "\\d*" || element.getAttribute("pattern") == "[0-9]*" ? InputType::NumberPad : InputType::Number;
2428         else if (element.isDateTimeLocalField())
2429             information.elementType = InputType::DateTimeLocal;
2430         else if (element.isDateField())
2431             information.elementType = InputType::Date;
2432         else if (element.isDateTimeField())
2433             information.elementType = InputType::DateTime;
2434         else if (element.isTimeField())
2435             information.elementType = InputType::Time;
2436         else if (element.isWeekField())
2437             information.elementType = InputType::Week;
2438         else if (element.isMonthField())
2439             information.elementType = InputType::Month;
2440         else if (element.isURLField())
2441             information.elementType = InputType::URL;
2442         else if (element.isText()) {
2443             const AtomicString& pattern = element.attributeWithoutSynchronization(HTMLNames::patternAttr);
2444             if (pattern == "\\d*" || pattern == "[0-9]*")
2445                 information.elementType = InputType::NumberPad;
2446             else {
2447                 information.elementType = InputType::Text;
2448                 if (!information.formAction.isEmpty()
2449                     && (element.getNameAttribute().contains("search") || element.getIdAttribute().contains("search") || element.attributeWithoutSynchronization(HTMLNames::titleAttr).contains("search")))
2450                     information.elementType = InputType::Search;
2451             }
2452         }
2453 #if ENABLE(INPUT_TYPE_COLOR)
2454         else if (element.isColorControl())
2455             information.elementType = InputType::Color;
2456 #endif
2457
2458         information.isReadOnly = element.isReadOnly();
2459         information.value = element.value();
2460         information.valueAsNumber = element.valueAsNumber();
2461         information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
2462     } else if (m_assistedNode->hasEditableStyle()) {
2463         information.elementType = InputType::ContentEditable;
2464         if (is<HTMLElement>(*m_assistedNode)) {
2465             auto& assistedElement = downcast<HTMLElement>(*m_assistedNode);
2466             information.isAutocorrect = assistedElement.shouldAutocorrect();
2467             information.autocapitalizeType = assistedElement.autocapitalizeType();
2468         } else {
2469             information.isAutocorrect = true;
2470             information.autocapitalizeType = AutocapitalizeTypeDefault;
2471         }
2472         information.isReadOnly = false;
2473     }
2474 }
2475
2476 void WebPage::autofillLoginCredentials(const String& username, const String& password)
2477 {
2478     if (is<HTMLInputElement>(m_assistedNode.get())) {
2479         if (auto autofillElements = AutofillElements::computeAutofillElements(downcast<HTMLInputElement>(*m_assistedNode)))
2480             autofillElements->autofill(username, password);
2481     }
2482 }
2483
2484 void WebPage::setViewportConfigurationViewLayoutSize(const FloatSize& size)
2485 {
2486     LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_pageID << " setViewportConfigurationViewLayoutSize " << size);
2487     if (m_viewportConfiguration.setViewLayoutSize(size))
2488         viewportConfigurationChanged();
2489 }
2490
2491 void WebPage::setMaximumUnobscuredSize(const FloatSize& maximumUnobscuredSize)
2492 {
2493     m_maximumUnobscuredSize = maximumUnobscuredSize;
2494     updateViewportSizeForCSSViewportUnits();
2495 }
2496
2497 void WebPage::setDeviceOrientation(int32_t deviceOrientation)
2498 {
2499     if (deviceOrientation == m_deviceOrientation)
2500         return;
2501     m_deviceOrientation = deviceOrientation;
2502     m_page->mainFrame().orientationChanged();
2503 }
2504
2505 void WebPage::setOverrideViewportArguments(const std::optional<WebCore::ViewportArguments>& arguments)
2506 {
2507     if (auto* document = m_page->mainFrame().document())
2508         document->setOverrideViewportArguments(arguments);
2509 }
2510
2511 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
2512 // we need to ignore differences that are within a small rounding error on floats.
2513 static inline bool areEssentiallyEqualAsFloat(float a, float b)
2514 {
2515     return WTF::areEssentiallyEqual(a, b);
2516 }
2517
2518 void WebPage::resetTextAutosizing()
2519 {
2520     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
2521         Document* document = frame->document();
2522         if (!document || !document->renderView())
2523             continue;
2524         document->renderView()->resetTextAutosizing();
2525     }
2526 }
2527
2528 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, DynamicViewportSizeUpdateID dynamicViewportSizeUpdateID)
2529 {
2530     SetForScope<bool> dynamicSizeUpdateGuard(m_inDynamicSizeUpdate, true);
2531     // FIXME: this does not handle the cases where the content would change the content size or scroll position from JavaScript.
2532     // To handle those cases, we would need to redo this computation on every change until the next visible content rect update.
2533     LOG_WITH_STREAM(VisibleRects, stream << "\nWebPage::dynamicViewportSizeUpdate - viewLayoutSize " << viewLayoutSize << " targetUnobscuredRect " << targetUnobscuredRect << " targetExposedContentRect " << targetExposedContentRect << " targetScale " << targetScale);
2534
2535     FrameView& frameView = *m_page->mainFrame().view();
2536     IntSize oldContentSize = frameView.contentsSize();
2537     float oldPageScaleFactor = m_page->pageScaleFactor();
2538
2539     m_dynamicSizeUpdateHistory.add(std::make_pair(oldContentSize, oldPageScaleFactor), frameView.scrollPosition());
2540
2541     RefPtr<Node> oldNodeAtCenter;
2542     double visibleHorizontalFraction = 1;
2543     float relativeHorizontalPositionInNodeAtCenter = 0;
2544     float relativeVerticalPositionInNodeAtCenter = 0;
2545     {
2546         visibleHorizontalFraction = frameView.unobscuredContentSize().width() / oldContentSize.width();
2547         IntPoint unobscuredContentRectCenter = frameView.unobscuredContentRect().center();
2548
2549         HitTestResult hitTestResult = HitTestResult(unobscuredContentRectCenter);
2550
2551         if (RenderView* mainFrameRenderView = frameView.renderView())
2552             mainFrameRenderView->hitTest(HitTestRequest(), hitTestResult);
2553
2554         if (Node* node = hitTestResult.innerNode()) {
2555             if (RenderObject* renderer = node->renderer()) {
2556                 FrameView& containingView = *node->document().frame()->view();
2557                 FloatRect boundingBox = containingView.contentsToRootView(renderer->absoluteBoundingBoxRect(true));
2558                 relativeHorizontalPositionInNodeAtCenter = (unobscuredContentRectCenter.x() - boundingBox.x()) / boundingBox.width();
2559                 relativeVerticalPositionInNodeAtCenter = (unobscuredContentRectCenter.y() - boundingBox.y()) / boundingBox.height();
2560                 oldNodeAtCenter = node;
2561             }
2562         }
2563     }
2564
2565     LOG_WITH_STREAM(VisibleRects, stream << "WebPage::dynamicViewportSizeUpdate setting view layout size to " << viewLayoutSize);
2566     m_viewportConfiguration.setViewLayoutSize(viewLayoutSize);
2567     IntSize newLayoutSize = m_viewportConfiguration.layoutSize();
2568
2569     if (setFixedLayoutSize(newLayoutSize))
2570         resetTextAutosizing();
2571
2572     setMaximumUnobscuredSize(maximumUnobscuredSize);
2573     m_page->setUnobscuredSafeAreaInsets(targetUnobscuredSafeAreaInsets);
2574
2575     frameView.updateLayoutAndStyleIfNeededRecursive();
2576
2577     IntSize newContentSize = frameView.contentsSize();
2578
2579     double scale = scaleAfterViewportWidthChange(targetScale, m_userHasChangedPageScaleFactor, m_viewportConfiguration, targetUnobscuredRectInScrollViewCoordinates.width(), newContentSize, oldContentSize, visibleHorizontalFraction);
2580     FloatRect newUnobscuredContentRect = targetUnobscuredRect;
2581     FloatRect newExposedContentRect = targetExposedContentRect;
2582
2583     bool scaleChanged = !areEssentiallyEqualAsFloat(scale, targetScale);
2584     if (scaleChanged) {
2585         // The target scale the UI is using cannot be reached by the content. We need to compute new targets based
2586         // on the viewport constraint and report everything back to the UIProcess.
2587
2588         // 1) Compute a new unobscured rect centered around the original one.
2589         double scaleDifference = targetScale / scale;
2590         double newUnobscuredRectWidth = targetUnobscuredRect.width() * scaleDifference;
2591         double newUnobscuredRectHeight = targetUnobscuredRect.height() * scaleDifference;
2592         double newUnobscuredRectX = targetUnobscuredRect.x() - (newUnobscuredRectWidth - targetUnobscuredRect.width()) / 2;
2593         double newUnobscuredRectY = targetUnobscuredRect.y() - (newUnobscuredRectHeight - targetUnobscuredRect.height()) / 2;
2594         newUnobscuredContentRect = FloatRect(newUnobscuredRectX, newUnobscuredRectY, newUnobscuredRectWidth, newUnobscuredRectHeight);
2595
2596         // 2) Extend our new unobscuredRect by the obscured margins to get a new exposed rect.
2597         double obscuredTopMargin = (targetUnobscuredRect.y() - targetExposedContentRect.y()) * scaleDifference;
2598         double obscuredLeftMargin = (targetUnobscuredRect.x() - targetExposedContentRect.x()) * scaleDifference;
2599         double obscuredBottomMargin = (targetExposedContentRect.maxY() - targetUnobscuredRect.maxY()) * scaleDifference;
2600         double obscuredRightMargin = (targetExposedContentRect.maxX() - targetUnobscuredRect.maxX()) * scaleDifference;
2601         newExposedContentRect = FloatRect(newUnobscuredRectX - obscuredLeftMargin,
2602                                           newUnobscuredRectY - obscuredTopMargin,
2603                                           newUnobscuredRectWidth + obscuredLeftMargin + obscuredRightMargin,
2604                                           newUnobscuredRectHeight + obscuredTopMargin + obscuredBottomMargin);
2605     }
2606
2607     if (oldContentSize != newContentSize || scaleChanged) {
2608         // Snap the new unobscured rect back into the content rect.
2609         newUnobscuredContentRect.setWidth(std::min(static_cast<float>(newContentSize.width()), newUnobscuredContentRect.width()));
2610         newUnobscuredContentRect.setHeight(std::min(static_cast<float>(newContentSize.height()), newUnobscuredContentRect.height()));
2611
2612         bool positionWasRestoredFromSizeUpdateHistory = false;
2613         const auto& previousPosition = m_dynamicSizeUpdateHistory.find(std::pair<IntSize, float>(newContentSize, scale));
2614         if (previousPosition != m_dynamicSizeUpdateHistory.end()) {
2615             IntPoint restoredPosition = previousPosition->value;
2616             FloatPoint deltaPosition(restoredPosition.x() - newUnobscuredContentRect.x(), restoredPosition.y() - newUnobscuredContentRect.y());
2617             newUnobscuredContentRect.moveBy(deltaPosition);
2618             newExposedContentRect.moveBy(deltaPosition);
2619             positionWasRestoredFromSizeUpdateHistory = true;
2620         } else if (oldContentSize != newContentSize) {
2621             FloatPoint newRelativeContentCenter;
2622
2623             if (RenderObject* renderer = oldNodeAtCenter ? oldNodeAtCenter->renderer() : nullptr) {
2624                 FrameView& containingView = *oldNodeAtCenter->document().frame()->view();
2625                 FloatRect newBoundingBox = containingView.contentsToRootView(renderer->absoluteBoundingBoxRect(true));
2626                 newRelativeContentCenter = FloatPoint(newBoundingBox.x() + relativeHorizontalPositionInNodeAtCenter * newBoundingBox.width(), newBoundingBox.y() + relativeVerticalPositionInNodeAtCenter * newBoundingBox.height());
2627             } else
2628                 newRelativeContentCenter = relativeCenterAfterContentSizeChange(targetUnobscuredRect, oldContentSize, newContentSize);
2629
2630             FloatPoint newUnobscuredContentRectCenter = newUnobscuredContentRect.center();
2631             FloatPoint positionDelta(newRelativeContentCenter.x() - newUnobscuredContentRectCenter.x(), newRelativeContentCenter.y() - newUnobscuredContentRectCenter.y());
2632             newUnobscuredContentRect.moveBy(positionDelta);
2633             newExposedContentRect.moveBy(positionDelta);
2634         }
2635
2636         // Make the top/bottom edges "sticky" within 1 pixel.
2637         if (!positionWasRestoredFromSizeUpdateHistory) {
2638             if (targetUnobscuredRect.maxY() > oldContentSize.height() - 1) {
2639                 float bottomVerticalPosition = newContentSize.height() - newUnobscuredContentRect.height();
2640                 newUnobscuredContentRect.setY(bottomVerticalPosition);
2641                 newExposedContentRect.setY(bottomVerticalPosition);
2642             }
2643             if (targetUnobscuredRect.y() < 1) {
2644                 newUnobscuredContentRect.setY(0);
2645                 newExposedContentRect.setY(0);
2646             }
2647
2648             bool likelyResponsiveDesignViewport = newLayoutSize.width() == viewLayoutSize.width() && areEssentiallyEqualAsFloat(scale, 1);
2649             bool contentBleedsOutsideLayoutWidth = newContentSize.width() > newLayoutSize.width();
2650             bool originalScrollPositionWasOnTheLeftEdge = targetUnobscuredRect.x() <= 0;
2651             if (likelyResponsiveDesignViewport && contentBleedsOutsideLayoutWidth && originalScrollPositionWasOnTheLeftEdge) {
2652                 // This is a special heuristics for "responsive" design with odd layout. It is quite common for responsive design
2653                 // to have content "bleeding" outside of the minimal layout width, usually from an image or table larger than expected.
2654                 // In those cases, the design usually does not adapt to the new width and remain at the newLayoutSize except for the
2655                 // large boxes.
2656                 // It is worth revisiting this special case as web developers get better with responsive design.
2657                 newExposedContentRect.setX(0);
2658                 newUnobscuredContentRect.setX(0);
2659             }
2660         }
2661
2662         float horizontalAdjustment = 0;
2663         if (newUnobscuredContentRect.maxX() > newContentSize.width())
2664             horizontalAdjustment -= newUnobscuredContentRect.maxX() - newContentSize.width();
2665         float verticalAdjustment = 0;
2666         if (newUnobscuredContentRect.maxY() > newContentSize.height())
2667             verticalAdjustment -= newUnobscuredContentRect.maxY() - newContentSize.height();
2668         if (newUnobscuredContentRect.x() < 0)
2669             horizontalAdjustment += - newUnobscuredContentRect.x();
2670         if (newUnobscuredContentRect.y() < 0)
2671             verticalAdjustment += - newUnobscuredContentRect.y();
2672
2673         FloatPoint adjustmentDelta(horizontalAdjustment, verticalAdjustment);
2674         newUnobscuredContentRect.moveBy(adjustmentDelta);
2675         newExposedContentRect.moveBy(adjustmentDelta);
2676     }
2677
2678     frameView.setScrollVelocity(0, 0, 0, MonotonicTime::now());
2679
2680     IntPoint roundedUnobscuredContentRectPosition = roundedIntPoint(newUnobscuredContentRect.location());
2681     frameView.setUnobscuredContentSize(newUnobscuredContentRect.size());
2682     m_drawingArea->setExposedContentRect(newExposedContentRect);
2683
2684     scalePage(scale, roundedUnobscuredContentRectPosition);
2685
2686     frameView.updateLayoutAndStyleIfNeededRecursive();
2687
2688     auto& settings = frameView.frame().settings();
2689     if (settings.visualViewportEnabled()) {
2690         LayoutRect documentRect = IntRect(frameView.scrollOrigin(), frameView.contentsSize());
2691         auto layoutViewportSize = FrameView::expandedLayoutViewportSize(frameView.baseLayoutViewportSize(), LayoutSize(documentRect.size()), settings.layoutViewportHeightExpansionFactor());
2692         LayoutRect layoutViewportRect = FrameView::computeUpdatedLayoutViewportRect(frameView.layoutViewportRect(), documentRect, LayoutSize(newUnobscuredContentRect.size()), LayoutRect(newUnobscuredContentRect), layoutViewportSize, frameView.minStableLayoutViewportOrigin(), frameView.maxStableLayoutViewportOrigin(), FrameView::LayoutViewportConstraint::ConstrainedToDocumentRect);
2693         frameView.setLayoutViewportOverrideRect(layoutViewportRect);
2694     } else {
2695         IntRect fixedPositionLayoutRect = enclosingIntRect(frameView.viewportConstrainedObjectsRect());
2696         frameView.setCustomFixedPositionLayoutRect(fixedPositionLayoutRect);
2697     }
2698
2699     frameView.setCustomSizeForResizeEvent(expandedIntSize(targetUnobscuredRectInScrollViewCoordinates.size()));
2700     setDeviceOrientation(deviceOrientation);
2701     frameView.setScrollOffset(roundedUnobscuredContentRectPosition);
2702
2703     m_drawingArea->scheduleCompositingLayerFlush();
2704
2705     m_pendingDynamicViewportSizeUpdateID = dynamicViewportSizeUpdateID;
2706 }
2707
2708 void WebPage::resetViewportDefaultConfiguration(WebFrame* frame, bool hasMobileDocType)
2709 {
2710     LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_pageID << " resetViewportDefaultConfiguration");
2711     if (m_useTestingViewportConfiguration) {
2712         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::testingParameters());
2713         return;
2714     }
2715
2716     if (!frame) {
2717         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::webpageParameters());
2718         return;
2719     }
2720
2721     if (hasMobileDocType) {
2722         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::xhtmlMobileParameters());
2723         return;
2724     }
2725
2726     Document* document = frame->coreFrame()->document();
2727     if (document->isImageDocument())
2728         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::imageDocumentParameters());
2729     else if (document->isTextDocument())
2730         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::textDocumentParameters());
2731     else
2732         m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::webpageParameters());
2733 }
2734
2735 void WebPage::viewportConfigurationChanged()
2736 {
2737     if (setFixedLayoutSize(m_viewportConfiguration.layoutSize()))
2738         resetTextAutosizing();
2739
2740     double initialScale = m_viewportConfiguration.initialScale();
2741     double scale;
2742     if (m_userHasChangedPageScaleFactor)
2743         scale = std::max(std::min(pageScaleFactor(), m_viewportConfiguration.maximumScale()), m_viewportConfiguration.minimumScale());
2744     else
2745         scale = initialScale;
2746
2747     LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_pageID << " viewportConfigurationChanged - setting zoomedOutPageScaleFactor to " << m_viewportConfiguration.minimumScale() << " and scale to " << scale);
2748
2749     m_page->setZoomedOutPageScaleFactor(m_viewportConfiguration.minimumScale());
2750
2751     updateViewportSizeForCSSViewportUnits();
2752
2753     FrameView& frameView = *mainFrameView();
2754     IntPoint scrollPosition = frameView.scrollPosition();
2755     if (!m_hasReceivedVisibleContentRectsAfterDidCommitLoad) {
2756         FloatSize minimumLayoutSizeInScrollViewCoordinates = m_viewportConfiguration.viewLayoutSize();
2757         minimumLayoutSizeInScrollViewCoordinates.scale(1 / scale);
2758         IntSize minimumLayoutSizeInDocumentCoordinates = roundedIntSize(minimumLayoutSizeInScrollViewCoordinates);
2759         frameView.setUnobscuredContentSize(minimumLayoutSizeInDocumentCoordinates);
2760         frameView.setScrollVelocity(0, 0, 0, MonotonicTime::now());
2761
2762         // FIXME: We could send down the obscured margins to find a better exposed rect and unobscured rect.
2763         // It is not a big deal at the moment because the tile coverage will always extend past the obscured bottom inset.
2764         m_drawingArea->setExposedContentRect(FloatRect(scrollPosition, minimumLayoutSizeInDocumentCoordinates));
2765     }
2766     scalePage(scale, scrollPosition);
2767     
2768     if (!m_hasReceivedVisibleContentRectsAfterDidCommitLoad) {
2769         // This takes scale into account, so do after the scale change.
2770         frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(frameView.viewportConstrainedObjectsRect()));
2771
2772         frameView.setCustomSizeForResizeEvent(expandedIntSize(m_viewportConfiguration.minimumLayoutSize()));
2773     }
2774 }
2775
2776 void WebPage::updateViewportSizeForCSSViewportUnits()
2777 {
2778     FloatSize largestUnobscuredSize = m_maximumUnobscuredSize;
2779     if (largestUnobscuredSize.isEmpty())
2780         largestUnobscuredSize = m_viewportConfiguration.viewLayoutSize();
2781
2782     FrameView& frameView = *mainFrameView();
2783     largestUnobscuredSize.scale(1 / m_viewportConfiguration.initialScaleIgnoringContentSize());
2784     frameView.setViewportSizeForCSSViewportUnits(roundedIntSize(largestUnobscuredSize));
2785 }
2786
2787 void WebPage::applicationWillResignActive()
2788 {
2789     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationWillResignActiveNotification object:nil];
2790     if (m_page)
2791         m_page->applicationWillResignActive();
2792 }
2793
2794 void WebPage::applicationDidEnterBackground(bool isSuspendedUnderLock)
2795 {
2796     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationDidEnterBackgroundNotification object:nil userInfo:@{@"isSuspendedUnderLock": [NSNumber numberWithBool:isSuspendedUnderLock]}];
2797
2798     m_isSuspendedUnderLock = isSuspendedUnderLock;
2799     setLayerTreeStateIsFrozen(true);
2800
2801     if (m_page)
2802         m_page->applicationDidEnterBackground();
2803 }
2804
2805 void WebPage::applicationDidFinishSnapshottingAfterEnteringBackground()
2806 {
2807     markLayersVolatile();
2808 }
2809
2810 void WebPage::applicationWillEnterForeground(bool isSuspendedUnderLock)
2811 {
2812     m_isSuspendedUnderLock = false;
2813     cancelMarkLayersVolatile();
2814     setLayerTreeStateIsFrozen(false);
2815
2816     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationWillEnterForegroundNotification object:nil userInfo:@{@"isSuspendedUnderLock": @(isSuspendedUnderLock)}];
2817
2818     if (m_page)
2819         m_page->applicationWillEnterForeground();
2820 }
2821
2822 void WebPage::applicationDidBecomeActive()
2823 {
2824     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationDidBecomeActiveNotification object:nil];
2825     if (m_page)
2826         m_page->applicationDidBecomeActive();
2827 }
2828
2829 static inline void adjustVelocityDataForBoundedScale(double& horizontalVelocity, double& verticalVelocity, double& scaleChangeRate, double exposedRectScale, double minimumScale, double maximumScale)
2830 {
2831     if (scaleChangeRate) {
2832         horizontalVelocity = 0;
2833         verticalVelocity = 0;
2834     }
2835
2836     if (exposedRectScale >= maximumScale || exposedRectScale <= minimumScale)
2837         scaleChangeRate = 0;
2838 }
2839
2840 std::optional<float> WebPage::scaleFromUIProcess(const VisibleContentRectUpdateInfo& visibleContentRectUpdateInfo) const
2841 {
2842     auto transactionIDForLastScaleFromUIProcess = visibleContentRectUpdateInfo.lastLayerTreeTransactionID();
2843     if (m_lastTransactionIDWithScaleChange > transactionIDForLastScaleFromUIProcess)
2844         return std::nullopt;
2845
2846     float scaleFromUIProcess = visibleContentRectUpdateInfo.scale();
2847     float currentScale = m_page->pageScaleFactor();
2848
2849     double scaleNoiseThreshold = 0.005;
2850     if (!m_isInStableState && fabs(scaleFromUIProcess - currentScale) < scaleNoiseThreshold) {
2851         // Tiny changes of scale during interactive zoom cause content to jump by one pixel, creating
2852         // visual noise. We filter those useless updates.
2853         scaleFromUIProcess = currentScale;
2854     }
2855     
2856     scaleFromUIProcess = std::min<float>(m_viewportConfiguration.maximumScale(), std::max<float>(m_viewportConfiguration.minimumScale(), scaleFromUIProcess));
2857     if (areEssentiallyEqualAsFloat(currentScale, scaleFromUIProcess))
2858         return std::nullopt;
2859
2860     return scaleFromUIProcess;
2861 }
2862
2863 static bool selectionIsInsideFixedPositionContainer(Frame& frame)
2864 {
2865     auto& selection = frame.selection().selection();
2866     if (selection.isNone())
2867         return false;
2868
2869     bool isInsideFixedPosition = false;
2870     if (selection.isCaret()) {
2871         frame.selection().absoluteCaretBounds(&isInsideFixedPosition);
2872         return isInsideFixedPosition;
2873     }
2874
2875     selection.visibleStart().absoluteCaretBounds(&isInsideFixedPosition);
2876     if (isInsideFixedPosition)
2877         return true;
2878
2879     selection.visibleEnd().absoluteCaretBounds(&isInsideFixedPosition);
2880     return isInsideFixedPosition;
2881 }
2882
2883 void WebPage::updateVisibleContentRects(const VisibleContentRectUpdateInfo& visibleContentRectUpdateInfo, MonotonicTime oldestTimestamp)
2884 {
2885     LOG_WITH_STREAM(VisibleRects, stream << "\nWebPage " << m_pageID << " updateVisibleContentRects " << visibleContentRectUpdateInfo);
2886
2887     // Skip any VisibleContentRectUpdate that have been queued before DidCommitLoad suppresses the updates in the UIProcess.
2888     if (visibleContentRectUpdateInfo.lastLayerTreeTransactionID() < m_mainFrame->firstLayerTreeTransactionIDAfterDidCommitLoad() && !visibleContentRectUpdateInfo.isFirstUpdateForNewViewSize())
2889         return;
2890
2891     m_hasReceivedVisibleContentRectsAfterDidCommitLoad = true;
2892     m_isInStableState = visibleContentRectUpdateInfo.inStableState();
2893
2894     auto scaleFromUIProcess = this->scaleFromUIProcess(visibleContentRectUpdateInfo);
2895
2896     // Skip progressively redrawing tiles if pinch-zooming while the system is under memory pressure.
2897     if (scaleFromUIProcess && !m_isInStableState && MemoryPressureHandler::singleton().isUnderMemoryPressure())
2898         return;
2899
2900     if (m_isInStableState)
2901         m_hasStablePageScaleFactor = true;
2902     else {
2903         if (!m_oldestNonStableUpdateVisibleContentRectsTimestamp)
2904             m_oldestNonStableUpdateVisibleContentRectsTimestamp = oldestTimestamp;
2905     }
2906
2907     float scaleToUse = scaleFromUIProcess.value_or(m_page->pageScaleFactor());
2908     FloatRect exposedContentRect = visibleContentRectUpdateInfo.exposedContentRect();
2909     FloatRect adjustedExposedContentRect = adjustExposedRectForNewScale(exposedContentRect, visibleContentRectUpdateInfo.scale(), scaleToUse);
2910     m_drawingArea->setExposedContentRect(adjustedExposedContentRect);
2911
2912     IntPoint scrollPosition = roundedIntPoint(visibleContentRectUpdateInfo.unobscuredContentRect().location());
2913
2914     bool hasSetPageScale = false;
2915     if (scaleFromUIProcess) {
2916         m_scaleWasSetByUIProcess = true;
2917         m_hasStablePageScaleFactor = m_isInStableState;
2918
2919         m_dynamicSizeUpdateHistory.clear();
2920
2921         m_page->setPageScaleFactor(scaleFromUIProcess.value(), scrollPosition, m_isInStableState);
2922         hasSetPageScale = true;
2923         send(Messages::WebPageProxy::PageScaleFactorDidChange(scaleFromUIProcess.value()));
2924     }
2925     
2926     if (!hasSetPageScale && m_isInStableState) {
2927         m_page->setPageScaleFactor(scaleToUse, scrollPosition, true);
2928         hasSetPageScale = true;
2929     }
2930
2931     auto& frame = m_page->mainFrame();
2932     FrameView& frameView = *frame.view();
2933     if (scrollPosition != frameView.scrollPosition())
2934         m_dynamicSizeUpdateHistory.clear();
2935
2936     if (m_viewportConfiguration.setCanIgnoreScalingConstraints(m_ignoreViewportScalingConstraints && visibleContentRectUpdateInfo.allowShrinkToFit()))
2937         viewportConfigurationChanged();
2938
2939     frameView.setUnobscuredContentSize(visibleContentRectUpdateInfo.unobscuredContentRect().size());
2940     m_page->setObscuredInsets(visibleContentRectUpdateInfo.obscuredInsets());
2941     m_page->setUnobscuredSafeAreaInsets(visibleContentRectUpdateInfo.unobscuredSafeAreaInsets());
2942     m_page->setEnclosedInScrollableAncestorView(visibleContentRectUpdateInfo.enclosedInScrollableAncestorView());
2943
2944     double horizontalVelocity = visibleContentRectUpdateInfo.horizontalVelocity();
2945     double verticalVelocity = visibleContentRectUpdateInfo.verticalVelocity();
2946     double scaleChangeRate = visibleContentRectUpdateInfo.scaleChangeRate();
2947     adjustVelocityDataForBoundedScale(horizontalVelocity, verticalVelocity, scaleChangeRate, visibleContentRectUpdateInfo.scale(), m_viewportConfiguration.minimumScale(), m_viewportConfiguration.maximumScale());
2948
2949     frameView.setScrollVelocity(horizontalVelocity, verticalVelocity, scaleChangeRate, visibleContentRectUpdateInfo.timestamp());
2950
2951     if (m_isInStableState) {
2952         if (frameView.frame().settings().visualViewportEnabled()) {
2953             if (visibleContentRectUpdateInfo.unobscuredContentRect() != visibleContentRectUpdateInfo.unobscuredContentRectRespectingInputViewBounds())
2954                 frameView.setVisualViewportOverrideRect(LayoutRect(visibleContentRectUpdateInfo.unobscuredContentRectRespectingInputViewBounds()));
2955             else
2956                 frameView.setVisualViewportOverrideRect(std::nullopt);
2957
2958             LOG_WITH_STREAM(VisibleRects, stream << "WebPage::updateVisibleContentRects - setLayoutViewportOverrideRect " << visibleContentRectUpdateInfo.customFixedPositionRect());
2959             frameView.setLayoutViewportOverrideRect(LayoutRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
2960             if (selectionIsInsideFixedPositionContainer(frame)) {
2961                 // Ensure that the next layer tree commit contains up-to-date caret/selection rects.
2962                 frameView.frame().selection().setCaretRectNeedsUpdate();
2963                 sendPartialEditorStateAndSchedulePostLayoutUpdate();
2964             }
2965         } else
2966             frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
2967     }
2968
2969     if (!visibleContentRectUpdateInfo.isChangingObscuredInsetsInteractively())
2970         frameView.setCustomSizeForResizeEvent(expandedIntSize(visibleContentRectUpdateInfo.unobscuredRectInScrollViewCoordinates().size()));
2971
2972     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) {
2973         ViewportRectStability viewportStability = ViewportRectStability::Stable;
2974         ScrollingLayerPositionAction layerAction = ScrollingLayerPositionAction::Sync;
2975         
2976         if (visibleContentRectUpdateInfo.isChangingObscuredInsetsInteractively()) {
2977             viewportStability = ViewportRectStability::ChangingObscuredInsetsInteractively;
2978             layerAction = ScrollingLayerPositionAction::SetApproximate;
2979         } else if (!m_isInStableState) {
2980             viewportStability = ViewportRectStability::Unstable;
2981             layerAction = ScrollingLayerPositionAction::SetApproximate;
2982         }
2983         scrollingCoordinator->reconcileScrollingState(frameView, scrollPosition, visibleContentRectUpdateInfo.customFixedPositionRect(), false, viewportStability, layerAction);
2984     }
2985 }
2986
2987 void WebPage::willStartUserTriggeredZooming()
2988 {
2989     m_page->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::webViewKey(), DiagnosticLoggingKeys::userZoomActionKey(), ShouldSample::No);
2990     m_userHasChangedPageScaleFactor = true;
2991 }
2992
2993 #if ENABLE(WEBGL)
2994 WebCore::WebGLLoadPolicy WebPage::webGLPolicyForURL(WebFrame*, const URL&)
2995 {
2996     return WebGLAllowCreation;
2997 }
2998
2999 WebCore::WebGLLoadPolicy WebPage::resolveWebGLPolicyForURL(WebFrame*, const URL&)
3000 {
3001     return WebGLAllowCreation;
3002 }
3003 #endif
3004
3005 #if ENABLE(IOS_TOUCH_EVENTS)
3006 void WebPage::dispatchAsynchronousTouchEvents(const Vector<WebTouchEvent, 1>& queue)
3007 {
3008     bool ignored;
3009     for (const WebTouchEvent& event : queue)
3010         dispatchTouchEvent(event, ignored);
3011 }
3012 #endif
3013
3014 void WebPage::computePagesForPrintingAndDrawToPDF(uint64_t frameID, const PrintInfo& printInfo, CallbackID callbackID, Messages::WebPage::ComputePagesForPrintingAndDrawToPDF::DelayedReply&& reply)
3015 {
3016     if (printInfo.snapshotFirstPage) {
3017         reply(1);
3018         IntSize snapshotSize { FloatSize { printInfo.availablePaperWidth, printInfo.availablePaperHeight } };
3019         IntRect snapshotRect { {0, 0}, snapshotSize };
3020         auto pdfData = pdfSnapshotAtSize(snapshotRect, snapshotSize, 0);
3021         send(Messages::WebPageProxy::DrawToPDFCallback(IPC::DataReference(CFDataGetBytePtr(pdfData.get()), CFDataGetLength(pdfData.get())), callbackID));
3022         return;
3023     }
3024
3025     Vector<WebCore::IntRect> pageRects;
3026     double totalScaleFactor;
3027     computePagesForPrintingImpl(frameID, printInfo, pageRects, totalScaleFactor);
3028
3029     ASSERT(pageRects.size() >= 1);
3030     std::size_t pageCount = pageRects.size();
3031     ASSERT(pageCount <= std::numeric_limits<uint32_t>::max());
3032     reply(pageCount);
3033
3034     RetainPtr<CFMutableDataRef> pdfPageData;
3035     drawPagesToPDFImpl(frameID, printInfo, 0, pageCount, pdfPageData);
3036     send(Messages::WebPageProxy::DrawToPDFCallback(IPC::DataReference(CFDataGetBytePtr(pdfPageData.get()), CFDataGetLength(pdfPageData.get())), callbackID));
3037
3038     endPrinting();
3039 }
3040
3041 void WebPage::contentSizeCategoryDidChange(const String& contentSizeCategory)
3042 {
3043     RenderThemeIOS::setContentSizeCategory(contentSizeCategory);
3044     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
3045 }
3046
3047 String WebPage::platformUserAgent(const URL&) const
3048 {
3049     return String();
3050 }
3051
3052 #if USE(QUICK_LOOK)
3053 void WebPage::didReceivePasswordForQuickLookDocument(const String& password)
3054 {
3055     WebPreviewLoaderClient::didReceivePassword(password, m_pageID);
3056 }
3057 #endif
3058
3059 bool WebPage::platformPrefersTextLegibilityBasedZoomScaling() const
3060 {
3061 #if PLATFORM(WATCHOS)
3062     return true;
3063 #else
3064     return false;
3065 #endif
3066 }
3067
3068 #if ENABLE(MEDIA_STREAM)
3069 void WebPage::prepareToSendUserMediaPermissionRequest()
3070 {
3071     static std::once_flag once;
3072     std::call_once(once, [] {
3073         if (!canLoadAVSystemController_PIDToInheritApplicationStateFrom())
3074             return;
3075
3076         NSError *error = nil;
3077         [[getAVSystemControllerClass() sharedAVSystemController] setAttribute:@(WebCore::presentingApplicationPID()) forKey:getAVSystemController_PIDToInheritApplicationStateFrom() error:&error];
3078         if (error)
3079             WTFLogAlways("Failed to set up PID proxying: %s", error.localizedDescription.UTF8String);
3080     });
3081 }
3082 #endif
3083
3084 } // namespace WebKit
3085
3086 #endif // PLATFORM(IOS)