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