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