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