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