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