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