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