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