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