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