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