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