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