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