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