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