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