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