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