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