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