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