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