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