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