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