1/* 2 * Copyright (C) 2009, 2010, 2011, 2012, 2013 Research In Motion Limited. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19#include "config.h" 20#include "WebPage.h" 21 22#include "APIShims.h" 23#include "ApplicationCacheStorage.h" 24#include "AuthenticationChallengeManager.h" 25#include "AutofillManager.h" 26#include "BackForwardController.h" 27#include "BackForwardListBlackBerry.h" 28#include "BackingStoreClient.h" 29#include "BackingStore_p.h" 30#if ENABLE(BATTERY_STATUS) 31#include "BatteryClientBlackBerry.h" 32#endif 33#include "CachedImage.h" 34#include "Chrome.h" 35#include "ChromeClientBlackBerry.h" 36#include "CookieManager.h" 37#include "CredentialManager.h" 38#include "CredentialStorage.h" 39#include "CredentialTransformData.h" 40#include "DOMSupport.h" 41#include "DatabaseManager.h" 42#include "DefaultTapHighlight.h" 43#include "DeviceMotionClientBlackBerry.h" 44#include "DeviceOrientationClientBlackBerry.h" 45#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 46#include "DeviceOrientationClientMock.h" 47#endif 48#include "DragClientBlackBerry.h" 49// FIXME: We should be using DumpRenderTreeClient, but I'm not sure where we should 50// create the DRT_BB object. See PR #120355. 51#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 52#include "DumpRenderTreeBlackBerry.h" 53#endif 54#include "EditorClientBlackBerry.h" 55#include "FocusController.h" 56#include "Frame.h" 57#include "FrameLoadRequest.h" 58#include "FrameLoaderClientBlackBerry.h" 59#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 60#include "GeolocationClientMock.h" 61#endif 62#include "GeolocationClientBlackBerry.h" 63#include "GroupSettings.h" 64#include "HTMLAreaElement.h" 65#include "HTMLFrameOwnerElement.h" 66#include "HTMLImageElement.h" 67#include "HTMLInputElement.h" 68#include "HTMLMediaElement.h" 69#include "HTMLNames.h" 70#include "HTMLParserIdioms.h" 71#include "HTTPParsers.h" 72#include "HistoryItem.h" 73#include "IconDatabaseClientBlackBerry.h" 74#include "ImageDocument.h" 75#include "InPageSearchManager.h" 76#include "InRegionScrollableArea.h" 77#include "InRegionScroller_p.h" 78#include "InputHandler.h" 79#include "InspectorBackendDispatcher.h" 80#include "InspectorClientBlackBerry.h" 81#include "InspectorController.h" 82#include "InspectorInstrumentation.h" 83#include "InspectorOverlay.h" 84#include "JavaScriptVariant_p.h" 85#include "LayerWebKitThread.h" 86#include "LocalFileSystem.h" 87#if ENABLE(NETWORK_INFO) 88#include "NetworkInfoClientBlackBerry.h" 89#endif 90#include "NetworkManager.h" 91#include "NodeRenderStyle.h" 92#include "NodeTraversal.h" 93#if ENABLE(NAVIGATOR_CONTENT_UTILS) 94#include "NavigatorContentUtilsClientBlackBerry.h" 95#endif 96#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 97#include "NotificationClientBlackBerry.h" 98#endif 99#include "Page.h" 100#include "PageCache.h" 101#include "PageGroup.h" 102#include "PagePopup.h" 103#include "PagePopupClient.h" 104#include "PlatformTouchEvent.h" 105#include "PlatformWheelEvent.h" 106#include "PluginDatabase.h" 107#include "PluginView.h" 108#include "RenderLayerBacking.h" 109#include "RenderLayerCompositor.h" 110#if ENABLE(FULLSCREEN_API) 111#include "RenderFullScreen.h" 112#endif 113#include "RenderText.h" 114#include "RenderThemeBlackBerry.h" 115#include "RenderTreeAsText.h" 116#include "RenderView.h" 117#include "RenderWidget.h" 118#include "ScriptController.h" 119#include "ScriptSourceCode.h" 120#include "ScriptValue.h" 121#include "ScrollTypes.h" 122#include "SecurityPolicy.h" 123#include "SelectionHandler.h" 124#include "SelectionOverlay.h" 125#include "Settings.h" 126#include "Storage.h" 127#include "StorageNamespace.h" 128#include "SurfacePool.h" 129#include "Text.h" 130#include "ThreadCheck.h" 131#include "TouchEventHandler.h" 132#include "TransformationMatrix.h" 133#if ENABLE(MEDIA_STREAM) 134#include "UserMediaClientImpl.h" 135#endif 136#if ENABLE(VIBRATION) 137#include "VibrationClientBlackBerry.h" 138#endif 139#include "VisiblePosition.h" 140#include "WebCookieJar.h" 141#include "WebKitThreadViewportAccessor.h" 142#include "WebKitVersion.h" 143#include "WebOverlay.h" 144#include "WebOverlay_p.h" 145#include "WebPageClient.h" 146#include "WebSocket.h" 147#include "WebViewportArguments.h" 148#include "npapi.h" 149#include "runtime_root.h" 150 151#if ENABLE(VIDEO) 152#include "MediaPlayer.h" 153#include "MediaPlayerPrivateBlackBerry.h" 154#endif 155 156#if USE(ACCELERATED_COMPOSITING) 157#include "FrameLayers.h" 158#include "WebPageCompositorClient.h" 159#include "WebPageCompositor_p.h" 160#endif 161 162#include <BlackBerryPlatformDeviceInfo.h> 163#include <BlackBerryPlatformExecutableMessage.h> 164#include <BlackBerryPlatformKeyboardEvent.h> 165#include <BlackBerryPlatformMessageClient.h> 166#include <BlackBerryPlatformMouseEvent.h> 167#include <BlackBerryPlatformScreen.h> 168#include <BlackBerryPlatformSettings.h> 169#include <BlackBerryPlatformWebFileSystem.h> 170#include <JavaScriptCore/APICast.h> 171#include <JavaScriptCore/JSContextRef.h> 172#include <JavaScriptCore/JSStringRef.h> 173#include <SharedPointer.h> 174#include <cmath> 175#include <sys/keycodes.h> 176#include <unicode/ustring.h> // platform ICU 177 178#include <wtf/text/CString.h> 179 180#ifndef USER_PROCESSES 181#include <memalloc.h> 182#endif 183 184#if ENABLE(REQUEST_ANIMATION_FRAME) 185#include "PlatformScreen.h" 186#endif 187 188#define DEBUG_TOUCH_EVENTS 0 189#define DEBUG_WEBPAGE_LOAD 0 190#define DEBUG_AC_COMMIT 0 191 192using namespace std; 193using namespace WebCore; 194 195typedef const unsigned short* CUShortPtr; 196 197namespace BlackBerry { 198namespace WebKit { 199 200static Vector<WebPage*>* visibleWebPages() 201{ 202 static Vector<WebPage*>* s_visibleWebPages = 0; // Initially, no web page is visible. 203 if (!s_visibleWebPages) 204 s_visibleWebPages = new Vector<WebPage*>; 205 return s_visibleWebPages; 206} 207 208const unsigned blockZoomMargin = 3; // Add 3 pixel margin on each side. 209static int blockClickRadius = 0; 210static double maximumBlockZoomScale = 3; // This scale can be clamped by the maximumScale set for the page. 211 212const double manualScrollInterval = 0.1; // The time interval during which we associate user action with scrolling. 213 214const IntSize minimumLayoutSize(10, 10); // Needs to be a small size, greater than 0, that we can grow the layout from. 215 216const double minimumExpandingRatio = 0.15; 217 218const double minimumZoomToFitScale = 0.25; 219const double maximumImageDocumentZoomToFitScale = 2; 220 221// Helper function to parse a URL and fill in missing parts. 222static KURL parseUrl(const String& url) 223{ 224 String urlString(url); 225 KURL kurl = KURL(KURL(), urlString); 226 if (kurl.protocol().isEmpty()) { 227 urlString.insert("http://", 0); 228 kurl = KURL(KURL(), urlString); 229 } 230 231 return kurl; 232} 233 234// Helper functions to convert to and from WebCore types. 235static inline WebCore::PlatformEvent::Type toWebCoreMouseEventType(const BlackBerry::Platform::MouseEvent::Type type) 236{ 237 switch (type) { 238 case BlackBerry::Platform::MouseEvent::MouseButtonDown: 239 return WebCore::PlatformEvent::MousePressed; 240 case Platform::MouseEvent::MouseButtonUp: 241 return WebCore::PlatformEvent::MouseReleased; 242 case Platform::MouseEvent::MouseMove: 243 default: 244 return WebCore::PlatformEvent::MouseMoved; 245 } 246} 247 248static inline ResourceRequestCachePolicy toWebCoreCachePolicy(Platform::NetworkRequest::CachePolicy policy) 249{ 250 switch (policy) { 251 case Platform::NetworkRequest::UseProtocolCachePolicy: 252 return UseProtocolCachePolicy; 253 case Platform::NetworkRequest::ReloadIgnoringCacheData: 254 return ReloadIgnoringCacheData; 255 case Platform::NetworkRequest::ReturnCacheDataElseLoad: 256 return ReturnCacheDataElseLoad; 257 case Platform::NetworkRequest::ReturnCacheDataDontLoad: 258 return ReturnCacheDataDontLoad; 259 default: 260 ASSERT_NOT_REACHED(); 261 return UseProtocolCachePolicy; 262 } 263} 264 265#if ENABLE(EVENT_MODE_METATAGS) 266static inline Platform::CursorEventMode toPlatformCursorEventMode(CursorEventMode mode) 267{ 268 switch (mode) { 269 case ProcessedCursorEvents: 270 return Platform::ProcessedCursorEvents; 271 case NativeCursorEvents: 272 return Platform::NativeCursorEvents; 273 default: 274 ASSERT_NOT_REACHED(); 275 return Platform::ProcessedCursorEvents; 276 } 277} 278 279static inline Platform::TouchEventMode toPlatformTouchEventMode(TouchEventMode mode) 280{ 281 switch (mode) { 282 case ProcessedTouchEvents: 283 return Platform::ProcessedTouchEvents; 284 case NativeTouchEvents: 285 return Platform::NativeTouchEvents; 286 case PureTouchEventsWithMouseConversion: 287 return Platform::PureTouchEventsWithMouseConversion; 288 default: 289 ASSERT_NOT_REACHED(); 290 return Platform::ProcessedTouchEvents; 291 } 292} 293#endif 294 295static inline HistoryItem* historyItemFromBackForwardId(WebPage::BackForwardId id) 296{ 297 return reinterpret_cast<HistoryItem*>(id); 298} 299 300static inline WebPage::BackForwardId backForwardIdFromHistoryItem(HistoryItem* item) 301{ 302 return reinterpret_cast<WebPage::BackForwardId>(item); 303} 304 305void WebPage::setUserViewportArguments(const WebViewportArguments& viewportArguments) 306{ 307 d->m_userViewportArguments = *(viewportArguments.d); 308} 309 310void WebPage::resetUserViewportArguments() 311{ 312 d->m_userViewportArguments = ViewportArguments(); 313} 314 315template <bool WebPagePrivate::* isActive> 316class DeferredTask: public WebPagePrivate::DeferredTaskBase { 317public: 318 static void finishOrCancel(WebPagePrivate* webPagePrivate) 319 { 320 webPagePrivate->*isActive = false; 321 } 322protected: 323 DeferredTask(WebPagePrivate* webPagePrivate) 324 : DeferredTaskBase(webPagePrivate, isActive) 325 { 326 } 327 typedef DeferredTask<isActive> DeferredTaskType; 328}; 329 330void WebPage::autofillTextField(const BlackBerry::Platform::String& item) 331{ 332 if (!d->m_webSettings->isFormAutofillEnabled()) 333 return; 334 335 d->m_autofillManager->autofillTextField(item); 336} 337 338BlackBerry::Platform::String WebPage::renderTreeAsText() 339{ 340 return externalRepresentation(d->m_mainFrame); 341} 342 343WebPagePrivate::WebPagePrivate(WebPage* webPage, WebPageClient* client, const IntRect& rect) 344 : m_webPage(webPage) 345 , m_client(client) 346 , m_inspectorClient(0) 347 , m_page(0) // Initialized by init. 348 , m_mainFrame(0) // Initialized by init. 349 , m_currentContextNode(0) 350 , m_webSettings(0) // Initialized by init. 351 , m_cookieJar(0) 352 , m_visible(false) 353 , m_activationState(ActivationActive) 354 , m_shouldResetTilesWhenShown(false) 355 , m_shouldZoomToInitialScaleAfterLoadFinished(false) 356 , m_userScalable(true) 357 , m_userPerformedManualZoom(false) 358 , m_userPerformedManualScroll(false) 359 , m_contentsSizeChanged(false) 360 , m_overflowExceedsContentsSize(false) 361 , m_resetVirtualViewportOnCommitted(true) 362 , m_shouldUseFixedDesktopMode(false) 363 , m_inspectorEnabled(false) 364 , m_preventIdleDimmingCount(0) 365#if ENABLE(TOUCH_EVENTS) 366 , m_preventDefaultOnTouchStart(false) 367#endif 368 , m_nestedLayoutFinishedCount(0) 369 , m_actualVisibleWidth(rect.width()) 370 , m_actualVisibleHeight(rect.height()) 371 , m_defaultLayoutSize(minimumLayoutSize) 372 , m_didRestoreFromPageCache(false) 373 , m_viewMode(WebPagePrivate::Desktop) // Default to Desktop mode for PB. 374 , m_loadState(WebPagePrivate::None) 375 , m_transformationMatrix(new TransformationMatrix()) 376 , m_backingStore(0) // Initialized by init. 377 , m_backingStoreClient(0) // Initialized by init. 378 , m_webkitThreadViewportAccessor(new WebKitThreadViewportAccessor(this)) 379 , m_inPageSearchManager(new InPageSearchManager(this)) 380 , m_inputHandler(new InputHandler(this)) 381 , m_selectionHandler(new SelectionHandler(this)) 382 , m_touchEventHandler(new TouchEventHandler(this)) 383 , m_proximityDetector(new ProximityDetector(this)) 384#if ENABLE(EVENT_MODE_METATAGS) 385 , m_cursorEventMode(ProcessedCursorEvents) 386 , m_touchEventMode(ProcessedTouchEvents) 387#endif 388#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) 389 , m_scaleBeforeFullScreen(-1.0) 390#endif 391 , m_currentCursor(Platform::CursorNone) 392 , m_dumpRenderTree(0) // Lazy initialization. 393 , m_initialScale(-1.0) 394 , m_minimumScale(-1.0) 395 , m_maximumScale(-1.0) 396 , m_forceRespectViewportArguments(false) 397 , m_anchorInNodeRectRatio(-1, -1) 398 , m_currentBlockZoomNode(0) 399 , m_currentBlockZoomAdjustedNode(0) 400 , m_shouldReflowBlock(false) 401 , m_lastUserEventTimestamp(0.0) 402 , m_pluginMouseButtonPressed(false) 403 , m_pluginMayOpenNewTab(false) 404#if USE(ACCELERATED_COMPOSITING) 405 , m_rootLayerCommitTimer(adoptPtr(new Timer<WebPagePrivate>(this, &WebPagePrivate::rootLayerCommitTimerFired))) 406 , m_needsOneShotDrawingSynchronization(false) 407 , m_needsCommit(false) 408 , m_suspendRootLayerCommit(false) 409#endif 410 , m_pendingOrientation(-1) 411 , m_fullscreenNode(0) 412 , m_hasInRegionScrollableAreas(false) 413 , m_updateDelegatedOverlaysDispatched(false) 414 , m_deferredTasksTimer(this, &WebPagePrivate::deferredTasksTimerFired) 415 , m_pagePopup(0) 416 , m_autofillManager(AutofillManager::create(this)) 417 , m_documentStyleRecalcPostponed(false) 418 , m_documentChildNeedsStyleRecalc(false) 419#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 420 , m_notificationManager(this) 421#endif 422 , m_didStartAnimations(false) 423 , m_animationStartTime(0) 424#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) 425 , m_isRunningRefreshAnimationClient(false) 426 , m_animationScheduled(false) 427 , m_previousFrameDone(true) 428 , m_monotonicAnimationStartTime(0) 429#endif 430{ 431 static bool isInitialized = false; 432 if (!isInitialized) { 433 isInitialized = true; 434 BlackBerry::Platform::DeviceInfo::instance(); 435 defaultUserAgent(); 436 } 437 438 AuthenticationChallengeManager::instance()->pageCreated(this); 439 clearCachedHitTestResult(); 440} 441 442WebPage::WebPage(WebPageClient* client, const BlackBerry::Platform::String& pageGroupName, const Platform::IntRect& rect) 443{ 444 globalInitialize(); 445 d = new WebPagePrivate(this, client, rect); 446 d->init(pageGroupName); 447} 448 449WebPagePrivate::~WebPagePrivate() 450{ 451 // Hand the backingstore back to another owner if necessary. 452 m_webPage->setVisible(false); 453 if (BackingStorePrivate::currentBackingStoreOwner() == m_webPage) 454 BackingStorePrivate::setCurrentBackingStoreOwner(0); 455 456#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) 457 stopRefreshAnimationClient(); 458 cancelCallOnMainThread(handleServiceScriptedAnimationsOnMainThread, this); 459#endif 460 461 closePagePopup(); 462 463 delete m_webSettings; 464 m_webSettings = 0; 465 466 delete m_cookieJar; 467 m_cookieJar = 0; 468 469 delete m_webkitThreadViewportAccessor; 470 m_webkitThreadViewportAccessor = 0; 471 472 delete m_backingStoreClient; 473 m_backingStoreClient = 0; 474 m_backingStore = 0; 475 476 delete m_page; 477 m_page = 0; 478 479 delete m_transformationMatrix; 480 m_transformationMatrix = 0; 481 482 delete m_inPageSearchManager; 483 m_inPageSearchManager = 0; 484 485 delete m_selectionHandler; 486 m_selectionHandler = 0; 487 488 delete m_inputHandler; 489 m_inputHandler = 0; 490 491 delete m_touchEventHandler; 492 m_touchEventHandler = 0; 493 494 delete m_proximityDetector; 495 m_proximityDetector = 0; 496 497#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 498 BlackBerry::Platform::deleteGuardedObject(static_cast<DumpRenderTree*>(m_dumpRenderTree)); 499 m_dumpRenderTree = 0; 500#endif 501 502 AuthenticationChallengeManager::instance()->pageDeleted(this); 503} 504 505WebPage::~WebPage() 506{ 507 deleteGuardedObject(d); 508 d = 0; 509} 510 511Page* WebPagePrivate::core(const WebPage* webPage) 512{ 513 return webPage->d->m_page; 514} 515 516void WebPagePrivate::init(const BlackBerry::Platform::String& pageGroupName) 517{ 518 m_webSettings = WebSettings::createFromStandardSettings(); 519 m_webSettings->setUserAgentString(defaultUserAgent()); 520 521 ChromeClientBlackBerry* chromeClient = new ChromeClientBlackBerry(this); 522 EditorClientBlackBerry* editorClient = new EditorClientBlackBerry(this); 523 DragClientBlackBerry* dragClient = 0; 524#if ENABLE(DRAG_SUPPORT) 525 dragClient = new DragClientBlackBerry(); 526#endif 527#if ENABLE(INSPECTOR) 528 m_inspectorClient = new InspectorClientBlackBerry(this); 529#endif 530 531 FrameLoaderClientBlackBerry* frameLoaderClient = new FrameLoaderClientBlackBerry(); 532 533 Page::PageClients pageClients; 534 pageClients.chromeClient = chromeClient; 535 pageClients.editorClient = editorClient; 536 pageClients.dragClient = dragClient; 537 pageClients.inspectorClient = m_inspectorClient; 538 pageClients.backForwardClient = BackForwardListBlackBerry::create(this); 539 540 m_page = new Page(pageClients); 541#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 542 if (isRunningDrt()) { 543 // In case running in DumpRenderTree mode set the controller to mock provider. 544 GeolocationClientMock* mock = new GeolocationClientMock(); 545 WebCore::provideGeolocationTo(m_page, mock); 546 mock->setController(WebCore::GeolocationController::from(m_page)); 547 } else 548#endif 549 WebCore::provideGeolocationTo(m_page, new GeolocationClientBlackBerry(this)); 550#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 551 if (getenv("drtRun")) 552 WebCore::provideDeviceOrientationTo(m_page, new DeviceOrientationClientMock); 553 else 554#endif 555 WebCore::provideDeviceOrientationTo(m_page, new DeviceOrientationClientBlackBerry(this)); 556 557 WebCore::provideDeviceMotionTo(m_page, new DeviceMotionClientBlackBerry(this)); 558#if ENABLE(VIBRATION) 559 WebCore::provideVibrationTo(m_page, new VibrationClientBlackBerry()); 560#endif 561 562#if ENABLE(BATTERY_STATUS) 563 WebCore::provideBatteryTo(m_page, new WebCore::BatteryClientBlackBerry(this)); 564#endif 565 566#if ENABLE(MEDIA_STREAM) 567 WebCore::provideUserMediaTo(m_page, new UserMediaClientImpl(m_webPage)); 568#endif 569 570#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 571 WebCore::provideNotification(m_page, new NotificationClientBlackBerry(this)); 572#endif 573 574#if ENABLE(NAVIGATOR_CONTENT_UTILS) 575 m_navigatorContentUtilsClient = adoptPtr(new NavigatorContentUtilsClientBlackBerry(this)); 576 WebCore::provideNavigatorContentUtilsTo(m_page, m_navigatorContentUtilsClient.get()); 577#endif 578 579#if ENABLE(NETWORK_INFO) 580 WebCore::provideNetworkInfoTo(m_page, new WebCore::NetworkInfoClientBlackBerry(this)); 581#endif 582 583 m_page->setDeviceScaleFactor(m_webSettings->devicePixelRatio()); 584 585 m_page->addLayoutMilestones(DidFirstVisuallyNonEmptyLayout); 586 587#if USE(ACCELERATED_COMPOSITING) 588 m_tapHighlight = DefaultTapHighlight::create(this); 589 m_selectionHighlight = DefaultTapHighlight::create(this); 590 m_selectionOverlay = SelectionOverlay::create(this); 591 m_page->settings()->setAcceleratedCompositingForFixedPositionEnabled(true); 592#endif 593 594 // FIXME: We explicitly call setDelegate() instead of passing ourself in createFromStandardSettings() 595 // so that we only get one didChangeSettings() callback when we set the page group name. This causes us 596 // to make a copy of the WebSettings since some WebSettings method make use of the page group name. 597 // Instead, we shouldn't be storing the page group name in WebSettings. 598 m_webSettings->setPageGroupName(pageGroupName); 599 m_webSettings->setDelegate(this); 600 didChangeSettings(m_webSettings); 601 602 RefPtr<Frame> newFrame = Frame::create(m_page, /* HTMLFrameOwnerElement* */ 0, frameLoaderClient); 603 604 m_mainFrame = newFrame.get(); 605 frameLoaderClient->setFrame(m_mainFrame, this); 606 m_mainFrame->init(); 607 608 m_inRegionScroller = adoptPtr(new InRegionScroller(this)); 609 610#if ENABLE(WEBGL) 611 m_page->settings()->setWebGLEnabled(true); 612#endif 613#if ENABLE(ACCELERATED_2D_CANVAS) 614 m_page->settings()->setCanvasUsesAcceleratedDrawing(true); 615 m_page->settings()->setAccelerated2dCanvasEnabled(true); 616#endif 617 618 m_page->settings()->setInteractiveFormValidationEnabled(true); 619 m_page->settings()->setAllowUniversalAccessFromFileURLs(false); 620 m_page->settings()->setAllowFileAccessFromFileURLs(false); 621 m_page->settings()->setFixedPositionCreatesStackingContext(true); 622 m_page->settings()->setWantsBalancedSetDefersLoadingBehavior(true); 623 624 m_backingStoreClient = BackingStoreClient::create(m_mainFrame, /* parent frame */ 0, m_webPage); 625 // The direct access to BackingStore is left here for convenience since it 626 // is owned by BackingStoreClient and then deleted by its destructor. 627 m_backingStore = m_backingStoreClient->backingStore(); 628 629 blockClickRadius = int(roundf(0.35 * Platform::Graphics::Screen::primaryScreen()->pixelsPerInch(0).width())); // The clicked rectangle area should be a fixed unit of measurement. 630 631 m_page->settings()->setDelegateSelectionPaint(true); 632 633#if ENABLE(REQUEST_ANIMATION_FRAME) 634 m_page->windowScreenDidChange((PlatformDisplayID)0); 635#endif 636 637#if ENABLE(FILE_SYSTEM) 638 static bool localFileSystemInitialized = false; 639 if (!localFileSystemInitialized) { 640 localFileSystemInitialized = true; 641 WebCore::LocalFileSystem::initializeLocalFileSystem("/"); 642 } 643#endif 644 645#if USE(ACCELERATED_COMPOSITING) 646 // The compositor will be needed for overlay rendering, so create it 647 // unconditionally. It will allocate OpenGL objects lazily, so this incurs 648 // no overhead in the unlikely case where the compositor is not needed. 649 Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage( 650 createMethodCallMessage(&WebPagePrivate::createCompositor, this)); 651#endif 652} 653 654class DeferredTaskLoadManualScript: public DeferredTask<&WebPagePrivate::m_wouldLoadManualScript> { 655public: 656 explicit DeferredTaskLoadManualScript(WebPagePrivate* webPagePrivate, const KURL& url) 657 : DeferredTaskType(webPagePrivate) 658 { 659 webPagePrivate->m_cachedManualScript = url; 660 } 661private: 662 virtual void performInternal(WebPagePrivate* webPagePrivate) 663 { 664 webPagePrivate->m_mainFrame->script()->executeIfJavaScriptURL(webPagePrivate->m_cachedManualScript, DoNotReplaceDocumentIfJavaScriptURL); 665 } 666}; 667 668void WebPagePrivate::load(const Platform::NetworkRequest& netReq, bool needReferer) 669{ 670 stopCurrentLoad(); 671 DeferredTaskLoadManualScript::finishOrCancel(this); 672 673 String urlString(netReq.getUrlRef()); 674 if (urlString.startsWith("vs:", false)) { 675 urlString = urlString.substring(3); 676 m_mainFrame->setInViewSourceMode(true); 677 } else 678 m_mainFrame->setInViewSourceMode(false); 679 680 KURL kurl = parseUrl(urlString); 681 if (protocolIs(kurl, "javascript")) { 682 // Never run javascript while loading is deferred. 683 if (m_page->defersLoading()) 684 m_deferredTasks.append(adoptPtr(new DeferredTaskLoadManualScript(this, kurl))); 685 else 686 m_mainFrame->script()->executeIfJavaScriptURL(kurl, DoNotReplaceDocumentIfJavaScriptURL); 687 return; 688 } 689 690 ResourceRequest request(kurl); 691 request.setHTTPMethod(netReq.getMethodRef()); 692 request.setCachePolicy(toWebCoreCachePolicy(netReq.getCachePolicy())); 693 if (!netReq.getOverrideContentType().empty()) 694 request.setOverrideContentType(netReq.getOverrideContentType()); 695 696 Platform::NetworkRequest::HeaderList& list = netReq.getHeaderListRef(); 697 if (!list.empty()) { 698 for (unsigned i = 0; i < list.size(); i++) 699 request.addHTTPHeaderField(list[i].first.c_str(), list[i].second.c_str()); 700 } 701 702 if (needReferer && focusedOrMainFrame() && focusedOrMainFrame()->document()) 703 request.addHTTPHeaderField("Referer", focusedOrMainFrame()->document()->url().string().utf8().data()); 704 705 if (Platform::NetworkRequest::TargetIsDownload == netReq.getTargetType()) 706 request.setForceDownload(true); 707 if (!netReq.getSuggestedSaveName().empty()) 708 request.setSuggestedSaveName(netReq.getSuggestedSaveName()); 709 710 m_mainFrame->loader()->load(FrameLoadRequest(m_mainFrame, request)); 711} 712 713void WebPage::loadFile(const BlackBerry::Platform::String& path, const BlackBerry::Platform::String& overrideContentType) 714{ 715 STATIC_LOCAL_STRING(s_filePrefix, "file://"); 716 STATIC_LOCAL_STRING(s_fileRootPrefix, "file:///"); 717 BlackBerry::Platform::String fileUrl(path); 718 if (fileUrl.startsWith('/')) 719 fileUrl = s_filePrefix + fileUrl; 720 else if (!fileUrl.startsWith(s_fileRootPrefix)) 721 return; 722 723 Platform::NetworkRequest netRequest; 724 netRequest.setRequestUrl(fileUrl); 725 netRequest.setOverrideContentType(overrideContentType); 726 d->load(netRequest, false); 727} 728 729void WebPage::load(const Platform::NetworkRequest& request, bool needReferer) 730{ 731 d->load(request, needReferer); 732} 733 734void WebPagePrivate::loadString(const BlackBerry::Platform::String& string, const BlackBerry::Platform::String& baseURL, const BlackBerry::Platform::String& contentType, const BlackBerry::Platform::String& failingURL) 735{ 736 KURL kurl = parseUrl(baseURL); 737 ResourceRequest request(kurl); 738 WTF::RefPtr<SharedBuffer> buffer 739 = SharedBuffer::create(string.c_str(), string.length()); 740 SubstituteData substituteData(buffer, 741 extractMIMETypeFromMediaType(contentType), 742 extractCharsetFromMediaType(contentType), 743 !failingURL.empty() ? parseUrl(failingURL) : KURL()); 744 m_mainFrame->loader()->load(FrameLoadRequest(m_mainFrame, request, substituteData)); 745} 746 747void WebPage::loadString(const BlackBerry::Platform::String& string, const BlackBerry::Platform::String& baseURL, const BlackBerry::Platform::String& mimeType, const BlackBerry::Platform::String& failingURL) 748{ 749 d->loadString(string, baseURL, mimeType, failingURL); 750} 751 752bool WebPagePrivate::executeJavaScript(const BlackBerry::Platform::String& scriptUTF8, JavaScriptDataType& returnType, BlackBerry::Platform::String& returnValue) 753{ 754 BLACKBERRY_ASSERT(scriptUTF8.isUtf8()); 755 String script = scriptUTF8; 756 757 if (script.isNull()) { 758 returnType = JSException; 759 return false; 760 } 761 762 if (script.isEmpty()) { 763 returnType = JSUndefined; 764 return true; 765 } 766 767 ScriptValue result = m_mainFrame->script()->executeScript(script, false); 768 JSC::JSValue value = result.jsValue(); 769 if (!value) { 770 returnType = JSException; 771 return false; 772 } 773 774 if (value.isUndefined()) 775 returnType = JSUndefined; 776 else if (value.isNull()) 777 returnType = JSNull; 778 else if (value.isBoolean()) 779 returnType = JSBoolean; 780 else if (value.isNumber()) 781 returnType = JSNumber; 782 else if (value.isString()) 783 returnType = JSString; 784 else if (value.isObject()) 785 returnType = JSObject; 786 else 787 returnType = JSUndefined; 788 789 if (returnType == JSBoolean || returnType == JSNumber || returnType == JSString || returnType == JSObject) { 790 JSC::ExecState* exec = m_mainFrame->script()->globalObject(mainThreadNormalWorld())->globalExec(); 791 returnValue = result.toString(exec); 792 } 793 794 return true; 795} 796 797bool WebPage::executeJavaScript(const BlackBerry::Platform::String& script, JavaScriptDataType& returnType, BlackBerry::Platform::String& returnValue) 798{ 799 return d->executeJavaScript(script, returnType, returnValue); 800} 801 802bool WebPagePrivate::executeJavaScriptInIsolatedWorld(const ScriptSourceCode& sourceCode, JavaScriptDataType& returnType, BlackBerry::Platform::String& returnValue) 803{ 804 if (!m_isolatedWorld) 805 m_isolatedWorld = m_mainFrame->script()->createWorld(); 806 807 // Use evaluateInWorld to avoid canExecuteScripts check. 808 ScriptValue result = m_mainFrame->script()->evaluateInWorld(sourceCode, m_isolatedWorld.get()); 809 JSC::JSValue value = result.jsValue(); 810 if (!value) { 811 returnType = JSException; 812 return false; 813 } 814 815 if (value.isUndefined()) 816 returnType = JSUndefined; 817 else if (value.isNull()) 818 returnType = JSNull; 819 else if (value.isBoolean()) 820 returnType = JSBoolean; 821 else if (value.isNumber()) 822 returnType = JSNumber; 823 else if (value.isString()) 824 returnType = JSString; 825 else if (value.isObject()) 826 returnType = JSObject; 827 else 828 returnType = JSUndefined; 829 830 if (returnType == JSBoolean || returnType == JSNumber || returnType == JSString || returnType == JSObject) { 831 JSC::ExecState* exec = m_mainFrame->script()->globalObject(mainThreadNormalWorld())->globalExec(); 832 returnValue = result.toString(exec); 833 } 834 835 return true; 836} 837 838bool WebPage::executeJavaScriptInIsolatedWorld(const std::wstring& script, JavaScriptDataType& returnType, BlackBerry::Platform::String& returnValue) 839{ 840 // On our platform wchar_t is unsigned and UChar is unsigned short 841 // so we have to convert using ICU conversion function 842 int lengthCopied = 0; 843 UErrorCode error = U_ZERO_ERROR; 844 const int length = script.length() + 1 /*null termination char*/; 845 UChar data[length]; 846 847 // FIXME: PR 138162 is giving U_INVALID_CHAR_FOUND error. 848 u_strFromUTF32(data, length, &lengthCopied, reinterpret_cast<const UChar32*>(script.c_str()), script.length(), &error); 849 BLACKBERRY_ASSERT(error == U_ZERO_ERROR); 850 if (error != U_ZERO_ERROR) { 851 Platform::logAlways(Platform::LogLevelCritical, "WebPage::executeJavaScriptInIsolatedWorld failed to convert UTF16 to JavaScript!"); 852 return false; 853 } 854 String str = String(data, lengthCopied); 855 ScriptSourceCode sourceCode(str, KURL()); 856 return d->executeJavaScriptInIsolatedWorld(sourceCode, returnType, returnValue); 857} 858 859bool WebPage::executeJavaScriptInIsolatedWorld(const BlackBerry::Platform::String& scriptUTF8, JavaScriptDataType& returnType, BlackBerry::Platform::String& returnValue) 860{ 861 BLACKBERRY_ASSERT(scriptUTF8.isUtf8()); 862 ScriptSourceCode sourceCode(scriptUTF8, KURL()); 863 return d->executeJavaScriptInIsolatedWorld(sourceCode, returnType, returnValue); 864} 865 866void WebPage::executeJavaScriptFunction(const std::vector<BlackBerry::Platform::String> &function, const std::vector<JavaScriptVariant> &args, JavaScriptVariant& returnValue) 867{ 868 if (!d->m_mainFrame) { 869 returnValue.setType(JavaScriptVariant::Exception); 870 return; 871 } 872 873 JSC::Bindings::RootObject* root = d->m_mainFrame->script()->bindingRootObject(); 874 if (!root) { 875 returnValue.setType(JavaScriptVariant::Exception); 876 return; 877 } 878 879 JSC::ExecState* exec = root->globalObject()->globalExec(); 880 JSGlobalContextRef ctx = toGlobalRef(exec); 881 882 JSC::APIEntryShim shim(exec); 883 WTF::Vector<JSValueRef> argListRef(args.size()); 884 for (unsigned i = 0; i < args.size(); ++i) 885 argListRef[i] = BlackBerryJavaScriptVariantToJSValueRef(ctx, args[i]); 886 887 JSValueRef windowObjectValue = toRef(d->m_mainFrame->script()->globalObject(mainThreadNormalWorld())); 888 JSObjectRef obj = JSValueToObject(ctx, windowObjectValue, 0); 889 JSObjectRef thisObject = obj; 890 for (unsigned i = 0; i < function.size(); ++i) { 891 JSStringRef str = JSStringCreateWithUTF8CString(function[i].c_str()); 892 thisObject = obj; 893 obj = JSValueToObject(ctx, JSObjectGetProperty(ctx, obj, str, 0), 0); 894 JSStringRelease(str); 895 if (!obj) 896 break; 897 } 898 899 JSObjectRef functionObject = obj; 900 JSValueRef result = 0; 901 if (functionObject && thisObject) 902 result = JSObjectCallAsFunction(ctx, functionObject, thisObject, args.size(), argListRef.data(), 0); 903 904 if (!result) { 905 returnValue.setType(JavaScriptVariant::Exception); 906 return; 907 } 908 909 returnValue = JSValueRefToBlackBerryJavaScriptVariant(ctx, result); 910} 911 912void WebPagePrivate::stopCurrentLoad() 913{ 914 // This function should contain all common code triggered by WebPage::load 915 // (which stops any load in progress before starting the new load) and 916 // WebPage::stoploading (the entry point for the client to stop the load 917 // explicitly). If it should only be done while stopping the load 918 // explicitly, it goes in WebPage::stopLoading, not here. 919 m_mainFrame->loader()->stopAllLoaders(); 920 921 // Cancel any deferred script that hasn't been processed yet. 922 DeferredTaskLoadManualScript::finishOrCancel(this); 923} 924 925void WebPage::stopLoading() 926{ 927 d->stopCurrentLoad(); 928} 929 930static void closeURLRecursively(Frame* frame) 931{ 932 // Do not create more frame please. 933 FrameLoaderClientBlackBerry* frameLoaderClient = static_cast<FrameLoaderClientBlackBerry*>(frame->loader()->client()); 934 frameLoaderClient->suppressChildFrameCreation(); 935 936 frame->loader()->closeURL(); 937 938 Vector<RefPtr<Frame>, 10> childFrames; 939 940 for (RefPtr<Frame> childFrame = frame->tree()->firstChild(); childFrame; childFrame = childFrame->tree()->nextSibling()) 941 childFrames.append(childFrame); 942 943 unsigned size = childFrames.size(); 944 for (unsigned i = 0; i < size; i++) 945 closeURLRecursively(childFrames[i].get()); 946} 947 948void WebPagePrivate::prepareToDestroy() 949{ 950 // Before the client starts tearing itself down, dispatch the unload event 951 // so it can take effect while all the client's state (e.g. scroll position) 952 // is still present. 953 closeURLRecursively(m_mainFrame); 954} 955 956void WebPage::prepareToDestroy() 957{ 958 d->prepareToDestroy(); 959} 960 961bool WebPage::dispatchBeforeUnloadEvent() 962{ 963 return d->m_page->mainFrame()->loader()->shouldClose(); 964} 965 966static void enableCrossSiteXHRRecursively(Frame* frame) 967{ 968 frame->document()->securityOrigin()->grantUniversalAccess(); 969 970 Vector<RefPtr<Frame>, 10> childFrames; 971 for (RefPtr<Frame> childFrame = frame->tree()->firstChild(); childFrame; childFrame = childFrame->tree()->nextSibling()) 972 childFrames.append(childFrame); 973 974 unsigned size = childFrames.size(); 975 for (unsigned i = 0; i < size; i++) 976 enableCrossSiteXHRRecursively(childFrames[i].get()); 977} 978 979void WebPagePrivate::enableCrossSiteXHR() 980{ 981 enableCrossSiteXHRRecursively(m_mainFrame); 982} 983 984void WebPage::enableCrossSiteXHR() 985{ 986 d->enableCrossSiteXHR(); 987} 988 989void WebPagePrivate::addOriginAccessWhitelistEntry(const BlackBerry::Platform::String& sourceOrigin, const BlackBerry::Platform::String& destinationOrigin, bool allowDestinationSubdomains) 990{ 991 RefPtr<SecurityOrigin> source = SecurityOrigin::createFromString(sourceOrigin); 992 if (source->isUnique()) 993 return; 994 995 KURL destination(KURL(), destinationOrigin); 996 SecurityPolicy::addOriginAccessWhitelistEntry(*source, destination.protocol(), destination.host(), allowDestinationSubdomains); 997} 998 999void WebPage::addOriginAccessWhitelistEntry(const BlackBerry::Platform::String& sourceOrigin, const BlackBerry::Platform::String& destinationOrigin, bool allowDestinationSubdomains) 1000{ 1001 d->addOriginAccessWhitelistEntry(sourceOrigin, destinationOrigin, allowDestinationSubdomains); 1002} 1003 1004void WebPagePrivate::removeOriginAccessWhitelistEntry(const BlackBerry::Platform::String& sourceOrigin, const BlackBerry::Platform::String& destinationOrigin, bool allowDestinationSubdomains) 1005{ 1006 RefPtr<SecurityOrigin> source = SecurityOrigin::createFromString(sourceOrigin); 1007 if (source->isUnique()) 1008 return; 1009 1010 KURL destination(KURL(), destinationOrigin); 1011 SecurityPolicy::removeOriginAccessWhitelistEntry(*source, destination.protocol(), destination.host(), allowDestinationSubdomains); 1012} 1013 1014void WebPage::removeOriginAccessWhitelistEntry(const BlackBerry::Platform::String& sourceOrigin, const BlackBerry::Platform::String& destinationOrigin, bool allowDestinationSubdomains) 1015{ 1016 d->removeOriginAccessWhitelistEntry(sourceOrigin, destinationOrigin, allowDestinationSubdomains); 1017} 1018 1019void WebPagePrivate::setLoadState(LoadState state) 1020{ 1021 if (m_loadState == state) 1022 return; 1023 1024 bool isFirstLoad = m_loadState == None; 1025 1026 // See RIM Bug #1068. 1027 if (state == Finished && m_mainFrame && m_mainFrame->document()) 1028 m_mainFrame->document()->updateStyleIfNeeded(); 1029 1030 // Dispatch the backingstore background color at important state changes. 1031 m_backingStore->d->setWebPageBackgroundColor(documentBackgroundColor()); 1032 1033 m_loadState = state; 1034 1035#if DEBUG_WEBPAGE_LOAD 1036 Platform::logAlways(Platform::LogLevelInfo, "WebPagePrivate::setLoadState %d", state); 1037#endif 1038 1039 switch (m_loadState) { 1040 case Provisional: 1041 if (isFirstLoad) { 1042 // Paints the visible backingstore as settings()->backgroundColor() 1043 // to prevent initial checkerboard on the first blit. 1044 m_backingStore->d->renderAndBlitVisibleContentsImmediately(); 1045 } 1046 break; 1047 case Committed: 1048 { 1049#if USE(ACCELERATED_COMPOSITING) 1050 releaseLayerResources(); 1051#endif 1052 1053 // Suspend screen update to avoid ui thread blitting while resetting backingstore. 1054 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 1055 m_backingStore->d->suspendBackingStoreUpdates(); 1056 m_backingStore->d->suspendScreenUpdates(); 1057 1058 m_previousContentsSize = IntSize(); 1059 m_backingStore->d->resetRenderQueue(); 1060 m_backingStore->d->resetTiles(); 1061 m_backingStore->d->setScrollingOrZooming(false, false /* shouldBlit */); 1062 m_shouldZoomToInitialScaleAfterLoadFinished = false; 1063 m_userPerformedManualZoom = false; 1064 m_userPerformedManualScroll = false; 1065 m_shouldUseFixedDesktopMode = false; 1066 m_forceRespectViewportArguments = false; 1067 if (m_resetVirtualViewportOnCommitted) // For DRT. 1068 m_virtualViewportSize = IntSize(); 1069 if (m_webSettings->viewportWidth() > 0) 1070 m_virtualViewportSize = IntSize(m_webSettings->viewportWidth(), m_defaultLayoutSize.height()); 1071 1072 // Check if we have already process the meta viewport tag, this only happens on history navigation. 1073 // For back/forward history navigation, we should only keep these previous values if the document 1074 // has the meta viewport tag when the state is Committed in setLoadState. 1075 static ViewportArguments defaultViewportArguments; 1076 bool documentHasViewportArguments = false; 1077 if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->viewportArguments() != defaultViewportArguments) 1078 documentHasViewportArguments = true; 1079 if (!(m_didRestoreFromPageCache && documentHasViewportArguments)) { 1080 m_viewportArguments = ViewportArguments(); 1081 m_userScalable = m_webSettings->isUserScalable(); 1082 resetScales(); 1083 1084 // At the moment we commit a new load, set the viewport arguments 1085 // to any fallback values. If there is a meta viewport in the 1086 // content it will overwrite the fallback arguments soon. 1087 dispatchViewportPropertiesDidChange(m_userViewportArguments); 1088 if (m_userViewportArguments != defaultViewportArguments) 1089 m_forceRespectViewportArguments = true; 1090 } else { 1091 Platform::IntSize virtualViewport = recomputeVirtualViewportFromViewportArguments(); 1092 m_webPage->setVirtualViewportSize(virtualViewport); 1093 } 1094 1095 if (m_shouldUseFixedDesktopMode) 1096 setViewMode(FixedDesktop); 1097 else 1098 setViewMode(Desktop); 1099 1100#if ENABLE(EVENT_MODE_METATAGS) 1101 didReceiveCursorEventMode(ProcessedCursorEvents); 1102 didReceiveTouchEventMode(ProcessedTouchEvents); 1103#endif 1104 1105 // Reset block zoom and reflow. 1106 resetBlockZoom(); 1107#if ENABLE(VIEWPORT_REFLOW) 1108 toggleTextReflowIfEnabledForBlockZoomOnly(); 1109#endif 1110 1111 // Notify InputHandler of state change. 1112 m_inputHandler->setInputModeEnabled(false); 1113 1114 // Set the scroll to origin here and notify the client since we'll be 1115 // zooming below without any real contents yet thus the contents size 1116 // we report to the client could make our current scroll position invalid. 1117 setScrollPosition(IntPoint::zero()); 1118 notifyTransformedScrollChanged(); 1119 1120 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 1121 m_backingStore->d->resumeBackingStoreUpdates(); 1122 // Paints the visible backingstore as white. Note it is important we do 1123 // this strictly after re-setting the scroll position to origin and resetting 1124 // the scales otherwise the visible contents calculation is wrong and we 1125 // can end up blitting artifacts instead. See: RIM Bug #401. 1126 m_backingStore->d->resumeScreenUpdates(BackingStore::RenderAndBlit); 1127 1128 // Update cursor status. 1129 updateCursor(); 1130 1131 break; 1132 } 1133 case Finished: 1134 case Failed: 1135 // Notify client of the initial zoom change. 1136 m_client->scaleChanged(); 1137 m_backingStore->d->updateTiles(true /* updateVisible */, false /* immediate */); 1138 break; 1139 default: 1140 break; 1141 } 1142} 1143 1144double WebPagePrivate::clampedScale(double scale) const 1145{ 1146 if (scale < minimumScale()) 1147 return minimumScale(); 1148 if (scale > maximumScale()) 1149 return maximumScale(); 1150 return scale; 1151} 1152 1153bool WebPagePrivate::shouldZoomAboutPoint(double scale, const FloatPoint&, bool enforceScaleClamping, double* clampedScale) 1154{ 1155 if (!m_mainFrame || !m_mainFrame->view()) 1156 return false; 1157 1158 if (enforceScaleClamping) 1159 scale = this->clampedScale(scale); 1160 1161 ASSERT(clampedScale); 1162 *clampedScale = scale; 1163 1164 if (currentScale() == scale) 1165 return false; 1166 1167 return true; 1168} 1169 1170bool WebPagePrivate::zoomAboutPoint(double unclampedScale, const FloatPoint& anchor, bool enforceScaleClamping, bool forceRendering, bool isRestoringZoomLevel) 1171{ 1172 if (!isRestoringZoomLevel) { 1173 // Clear any existing block zoom. (If we are restoring a saved zoom level on page load, 1174 // there is guaranteed to be no existing block zoom and we don't want to clear m_shouldReflowBlock.) 1175 resetBlockZoom(); 1176 } 1177 1178 // The reflow and block zoom stuff here needs to happen regardless of 1179 // whether we shouldZoomAboutPoint. 1180#if ENABLE(VIEWPORT_REFLOW) 1181 toggleTextReflowIfEnabledForBlockZoomOnly(m_shouldReflowBlock); 1182 if (m_page->settings()->isTextReflowEnabled() && m_mainFrame->view()) 1183 setNeedsLayout(); 1184#endif 1185 1186 double scale; 1187 if (!shouldZoomAboutPoint(unclampedScale, anchor, enforceScaleClamping, &scale)) { 1188 if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled) { 1189 m_currentPinchZoomNode = 0; 1190 m_anchorInNodeRectRatio = FloatPoint(-1, -1); 1191 } 1192 return false; 1193 } 1194 TransformationMatrix zoom; 1195 zoom.scale(scale); 1196 1197#if DEBUG_WEBPAGE_LOAD 1198 if (loadState() < Finished) { 1199 Platform::logAlways(Platform::LogLevelInfo, 1200 "WebPagePrivate::zoomAboutPoint scale %f anchor %s", 1201 scale, Platform::FloatPoint(anchor).toString().c_str()); 1202 } 1203#endif 1204 1205 // Our current scroll position in float. 1206 FloatPoint scrollPosition = this->scrollPosition(); 1207 1208 // Anchor offset from scroll position in float. 1209 FloatPoint anchorOffset(anchor.x() - scrollPosition.x(), anchor.y() - scrollPosition.y()); 1210 1211 // The horizontal scaling factor and vertical scaling factor should be equal 1212 // to preserve aspect ratio of content. 1213 ASSERT(m_transformationMatrix->m11() == m_transformationMatrix->m22()); 1214 1215 // Need to invert the previous transform to anchor the viewport. 1216 double inverseScale = scale / m_transformationMatrix->m11(); 1217 1218 // Actual zoom. 1219 *m_transformationMatrix = zoom; 1220 1221 // Suspend all screen updates to the backingstore. 1222 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 1223 m_backingStore->d->suspendBackingStoreUpdates(); 1224 m_backingStore->d->suspendScreenUpdates(); 1225 1226 updateViewportSize(); 1227 1228 IntPoint newScrollPosition(IntPoint(max(0, static_cast<int>(roundf(anchor.x() - anchorOffset.x() / inverseScale))), 1229 max(0, static_cast<int>(roundf(anchor.y() - anchorOffset.y() / inverseScale))))); 1230 1231 if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled) { 1232 // This is a hack for email which has reflow always turned on. 1233 setNeedsLayout(); 1234 layoutIfNeeded(); 1235 if (m_currentPinchZoomNode) 1236 newScrollPosition = calculateReflowedScrollPosition(anchorOffset, scale == minimumScale() ? 1 : inverseScale); 1237 m_currentPinchZoomNode = 0; 1238 m_anchorInNodeRectRatio = FloatPoint(-1, -1); 1239 } 1240 1241 setScrollPosition(newScrollPosition); 1242 1243 notifyTransformChanged(); 1244 1245 bool isLoading = this->isLoading(); 1246 1247 // We need to invalidate all tiles both visible and non-visible if we're loading. 1248 m_backingStore->d->updateTiles(isLoading /* updateVisible */, false /* immediate */); 1249 1250 bool shouldRender = !isLoading || m_userPerformedManualZoom || forceRendering; 1251 1252 m_client->scaleChanged(); 1253 1254 if (m_pendingOrientation != -1) 1255 m_client->updateInteractionViews(); 1256 1257 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 1258 m_backingStore->d->resumeBackingStoreUpdates(); 1259 1260 // Clear window to make sure there are no artifacts. 1261 if (shouldRender) { 1262 // Resume all screen updates to the backingstore and render+blit visible contents to screen. 1263 m_backingStore->d->resumeScreenUpdates(BackingStore::RenderAndBlit); 1264 } else { 1265 // Resume all screen updates to the backingstore but do not blit to the screen because we not rendering. 1266 m_backingStore->d->resumeScreenUpdates(BackingStore::None); 1267 } 1268 1269 return true; 1270} 1271 1272IntPoint WebPagePrivate::calculateReflowedScrollPosition(const FloatPoint& anchorOffset, double inverseScale) 1273{ 1274 // Should only be invoked when text reflow is enabled. 1275 ASSERT(m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled); 1276 1277 int offsetY = 0; 1278 int offsetX = 0; 1279 1280 IntRect nodeRect = rectForNode(m_currentPinchZoomNode.get()); 1281 1282 if (m_currentPinchZoomNode->renderer() && m_anchorInNodeRectRatio.y() >= 0) { 1283 offsetY = nodeRect.height() * m_anchorInNodeRectRatio.y(); 1284 if (m_currentPinchZoomNode->renderer()->isImage() && m_anchorInNodeRectRatio.x() > 0) 1285 offsetX = nodeRect.width() * m_anchorInNodeRectRatio.x() - anchorOffset.x() / inverseScale; 1286 } 1287 1288 IntRect reflowedRect = adjustRectOffsetForFrameOffset(nodeRect, m_currentPinchZoomNode.get()); 1289 1290 return IntPoint(max(0, static_cast<int>(roundf(reflowedRect.x() + offsetX))), 1291 max(0, static_cast<int>(roundf(reflowedRect.y() + offsetY - anchorOffset.y() / inverseScale)))); 1292} 1293 1294void WebPagePrivate::setNeedsLayout() 1295{ 1296 FrameView* view = m_mainFrame->view(); 1297 ASSERT(view); 1298 view->setNeedsLayout(); 1299} 1300 1301void WebPagePrivate::updateLayoutAndStyleIfNeededRecursive() const 1302{ 1303 FrameView* view = m_mainFrame->view(); 1304 ASSERT(view); 1305 view->updateLayoutAndStyleIfNeededRecursive(); 1306 ASSERT(!view->needsLayout()); 1307} 1308 1309void WebPagePrivate::layoutIfNeeded() const 1310{ 1311 FrameView* view = m_mainFrame->view(); 1312 ASSERT(view); 1313 if (view->needsLayout()) 1314 view->layout(); 1315} 1316 1317IntPoint WebPagePrivate::scrollPosition() const 1318{ 1319 if (!m_backingStoreClient) 1320 return IntPoint(); 1321 1322 return m_backingStoreClient->scrollPosition(); 1323} 1324 1325IntPoint WebPagePrivate::maximumScrollPosition() const 1326{ 1327 if (!m_backingStoreClient) 1328 return IntPoint(); 1329 1330 return m_backingStoreClient->maximumScrollPosition(); 1331} 1332 1333void WebPagePrivate::setScrollPosition(const IntPoint& pos) 1334{ 1335 ASSERT(m_backingStoreClient); 1336 m_backingStoreClient->setScrollPosition(pos); 1337} 1338 1339// Setting the scroll position is in transformed coordinates. 1340void WebPage::setDocumentScrollPosition(const Platform::IntPoint& documentScrollPosition) 1341{ 1342 WebCore::IntPoint scrollPosition = documentScrollPosition; 1343 if (scrollPosition == d->scrollPosition()) 1344 return; 1345 1346 // If the user recently performed an event, this new scroll position 1347 // could possibly be a result of that. Or not, this is just a heuristic. 1348 if (currentTime() - d->m_lastUserEventTimestamp < manualScrollInterval) 1349 d->m_userPerformedManualScroll = true; 1350 1351 d->m_backingStoreClient->setIsClientGeneratedScroll(true); 1352 1353 // UI thread can call BackingStorePrivate::setScrollingOrZooming(false) before WebKit thread calls WebPage::setScrollPosition(), 1354 // in which case it will set ScrollableArea::m_constrainsScrollingToContentEdge to true earlier. 1355 // We can cache ScrollableArea::m_constrainsScrollingToContentEdge and always set it to false before we set scroll position in 1356 // WebKit thread to avoid scroll position clamping during scrolling, and restore it to what it was after that. 1357 bool constrainsScrollingToContentEdge = d->m_mainFrame->view()->constrainsScrollingToContentEdge(); 1358 d->m_mainFrame->view()->setConstrainsScrollingToContentEdge(false); 1359 d->setScrollPosition(scrollPosition); 1360 d->m_mainFrame->view()->setConstrainsScrollingToContentEdge(constrainsScrollingToContentEdge); 1361 1362 d->m_backingStoreClient->setIsClientGeneratedScroll(false); 1363} 1364 1365bool WebPagePrivate::shouldSendResizeEvent() 1366{ 1367 if (!m_mainFrame || !m_mainFrame->document()) 1368 return false; 1369 1370 // PR#96865 : Provide an option to always send resize events, regardless of the loading 1371 // status. The scenario for this are Sapphire applications which tend to 1372 // maintain an open GET request to the server. This open GET results in 1373 // webkit thinking that content is still arriving when at the application 1374 // level it is considered fully loaded. 1375 // 1376 // NOTE: Care must be exercised in the use of this option, as it bypasses 1377 // the sanity provided in 'isLoadingInAPISense()' below. 1378 // 1379 static const bool unrestrictedResizeEvents = Platform::Settings::instance()->unrestrictedResizeEvents(); 1380 if (unrestrictedResizeEvents) 1381 return true; 1382 1383 // Don't send the resize event if the document is loading. Some pages automatically reload 1384 // when the window is resized; Safari on iPhone often resizes the window while setting up its 1385 // viewport. This obviously can cause problems. 1386 DocumentLoader* documentLoader = m_mainFrame->loader()->documentLoader(); 1387 if (documentLoader && documentLoader->isLoadingInAPISense()) 1388 return false; 1389 1390 return true; 1391} 1392 1393void WebPagePrivate::willDeferLoading() 1394{ 1395 m_deferredTasksTimer.stop(); 1396 m_client->willDeferLoading(); 1397} 1398 1399void WebPagePrivate::didResumeLoading() 1400{ 1401 if (!m_deferredTasks.isEmpty()) 1402 m_deferredTasksTimer.startOneShot(0); 1403 m_client->didResumeLoading(); 1404} 1405 1406void WebPagePrivate::deferredTasksTimerFired(WebCore::Timer<WebPagePrivate>*) 1407{ 1408 ASSERT(!m_deferredTasks.isEmpty()); 1409 if (m_deferredTasks.isEmpty()) 1410 return; 1411 1412 OwnPtr<DeferredTaskBase> task = m_deferredTasks[0].release(); 1413 m_deferredTasks.remove(0); 1414 1415 if (!m_deferredTasks.isEmpty()) 1416 m_deferredTasksTimer.startOneShot(0); 1417 1418 task->perform(this); 1419} 1420 1421void WebPagePrivate::notifyInRegionScrollStopped() 1422{ 1423 if (m_inRegionScroller->d->isActive()) 1424 m_inRegionScroller->d->reset(); 1425} 1426 1427void WebPage::notifyInRegionScrollStopped() 1428{ 1429 d->notifyInRegionScrollStopped(); 1430} 1431 1432void WebPagePrivate::setHasInRegionScrollableAreas(bool b) 1433{ 1434 if (b != m_hasInRegionScrollableAreas) 1435 m_hasInRegionScrollableAreas = b; 1436} 1437 1438IntSize WebPagePrivate::viewportSize() const 1439{ 1440 return m_webkitThreadViewportAccessor->roundToDocumentFromPixelContents(Platform::IntRect(Platform::IntPoint::zero(), transformedViewportSize())).size(); 1441} 1442 1443IntSize WebPagePrivate::actualVisibleSize() const 1444{ 1445 return m_webkitThreadViewportAccessor->documentViewportSize(); 1446} 1447 1448bool WebPagePrivate::hasVirtualViewport() const 1449{ 1450 return !m_virtualViewportSize.isEmpty(); 1451} 1452 1453void WebPagePrivate::updateViewportSize(bool setFixedReportedSize, bool sendResizeEvent) 1454{ 1455 // This checks to make sure we're not calling updateViewportSize 1456 // during WebPagePrivate::init(). 1457 if (!m_backingStore) 1458 return; 1459 ASSERT(m_mainFrame->view()); 1460 IntSize visibleSize = actualVisibleSize(); 1461 if (setFixedReportedSize) 1462 m_mainFrame->view()->setFixedReportedSize(visibleSize); 1463 1464 IntRect frameRect = IntRect(scrollPosition(), visibleSize); 1465 if (frameRect != m_mainFrame->view()->frameRect()) { 1466 m_mainFrame->view()->setFrameRect(frameRect); 1467 m_mainFrame->view()->adjustViewSize(); 1468 1469#if ENABLE(FULLSCREEN_API) 1470 adjustFullScreenElementDimensionsIfNeeded(); 1471#endif 1472 } 1473 1474 // We're going to need to send a resize event to JavaScript because 1475 // innerWidth and innerHeight depend on fixed reported size. 1476 // This is how we support mobile pages where JavaScript resizes 1477 // the page in order to get around the fixed layout size, e.g. 1478 // google maps when it detects a mobile user agent. 1479 if (sendResizeEvent && shouldSendResizeEvent()) 1480 m_mainFrame->eventHandler()->sendResizeEvent(); 1481 1482 // When the actual visible size changes, we also 1483 // need to reposition fixed elements. 1484 m_mainFrame->view()->repaintFixedElementsAfterScrolling(); 1485} 1486 1487FloatPoint WebPagePrivate::centerOfVisibleContentsRect() const 1488{ 1489 // The visible contents rect in float. 1490 FloatRect visibleContentsRect = this->visibleContentsRect(); 1491 1492 // The center of the visible contents rect in float. 1493 return FloatPoint(visibleContentsRect.x() + visibleContentsRect.width() / 2.0, 1494 visibleContentsRect.y() + visibleContentsRect.height() / 2.0); 1495} 1496 1497IntRect WebPagePrivate::visibleContentsRect() const 1498{ 1499 return m_backingStoreClient->visibleContentsRect(); 1500} 1501 1502IntSize WebPagePrivate::contentsSize() const 1503{ 1504 if (!m_mainFrame || !m_mainFrame->view()) 1505 return IntSize(); 1506 1507 return m_backingStoreClient->contentsSize(); 1508} 1509 1510IntSize WebPagePrivate::absoluteVisibleOverflowSize() const 1511{ 1512 if (!m_mainFrame || !m_mainFrame->contentRenderer()) 1513 return IntSize(); 1514 1515 return IntSize(m_mainFrame->contentRenderer()->rightAbsoluteVisibleOverflow(), m_mainFrame->contentRenderer()->bottomAbsoluteVisibleOverflow()); 1516} 1517 1518void WebPagePrivate::contentsSizeChanged(const IntSize& contentsSize) 1519{ 1520 if (m_previousContentsSize == contentsSize) 1521 return; 1522 1523 // This should only occur in the middle of layout so we set a flag here and 1524 // handle it at the end of the layout. 1525 m_contentsSizeChanged = true; 1526 1527#if DEBUG_WEBPAGE_LOAD 1528 Platform::logAlways(Platform::LogLevelInfo, "WebPagePrivate::contentsSizeChanged %s", Platform::IntSize(contentsSize).toString().c_str()); 1529#endif 1530} 1531 1532void WebPagePrivate::overflowExceedsContentsSize() 1533{ 1534 m_overflowExceedsContentsSize = true; 1535 if (absoluteVisibleOverflowSize().width() < DEFAULT_MAX_LAYOUT_WIDTH && !hasVirtualViewport()) { 1536 if (setViewMode(viewMode())) { 1537 setNeedsLayout(); 1538 layoutIfNeeded(); 1539 } 1540 } 1541} 1542 1543void WebPagePrivate::layoutFinished() 1544{ 1545 // If a layout change has occurred, we need to invalidate any current spellcheck requests and trigger a new run. 1546 m_inputHandler->stopPendingSpellCheckRequests(true /* isRestartRequired */); 1547 1548 if (!m_contentsSizeChanged && !m_overflowExceedsContentsSize) 1549 return; 1550 1551 m_contentsSizeChanged = false; // Toggle to turn off notification again. 1552 m_overflowExceedsContentsSize = false; 1553 1554 if (contentsSize().isEmpty()) 1555 return; 1556 1557 // The call to zoomToInitialScaleOnLoad can cause recursive layout when called from 1558 // the middle of a layout, but the recursion is limited by detection code in 1559 // setViewMode() and mitigation code in fixedLayoutSize(). 1560 if (didLayoutExceedMaximumIterations()) { 1561 notifyTransformedContentsSizeChanged(); 1562 return; 1563 } 1564 1565 // Temporarily save the m_previousContentsSize here before updating it (in 1566 // notifyTransformedContentsSizeChanged()) so we can compare if our contents 1567 // shrunk afterwards. 1568 IntSize previousContentsSize = m_previousContentsSize; 1569 1570 m_nestedLayoutFinishedCount++; 1571 1572 if (shouldZoomToInitialScaleOnLoad()) { 1573 zoomToInitialScaleOnLoad(); 1574 m_shouldZoomToInitialScaleAfterLoadFinished = false; 1575 } else if (loadState() != None) 1576 notifyTransformedContentsSizeChanged(); 1577 1578 m_nestedLayoutFinishedCount--; 1579 1580 if (!m_nestedLayoutFinishedCount) { 1581 // When the contents shrinks, there is a risk that we 1582 // will be left at a scroll position that lies outside of the 1583 // contents rect. Since we allow overscrolling and neglect 1584 // to clamp overscroll in order to retain input focus (RIM Bug #414) 1585 // we need to clamp somewhere, and this is where we know the 1586 // contents size has changed. 1587 1588 if (contentsSize() != previousContentsSize) { 1589 1590 IntPoint newScrollPosition = scrollPosition(); 1591 1592 if (contentsSize().height() < previousContentsSize.height()) { 1593 IntPoint scrollPositionWithHeightShrunk = IntPoint(newScrollPosition.x(), maximumScrollPosition().y()); 1594 newScrollPosition = newScrollPosition.shrunkTo(scrollPositionWithHeightShrunk); 1595 } 1596 1597 if (contentsSize().width() < previousContentsSize.width()) { 1598 IntPoint scrollPositionWithWidthShrunk = IntPoint(maximumScrollPosition().x(), newScrollPosition.y()); 1599 newScrollPosition = newScrollPosition.shrunkTo(scrollPositionWithWidthShrunk); 1600 } 1601 1602 if (newScrollPosition != scrollPosition()) { 1603 setScrollPosition(newScrollPosition); 1604 notifyTransformedScrollChanged(); 1605 } 1606 1607 const Platform::IntSize pixelContentsSize = m_webkitThreadViewportAccessor->pixelContentsSize(); 1608 1609 // If the content size is too small, zoom it to fit the viewport. 1610 if ((loadState() == Finished || loadState() == Committed) && (pixelContentsSize.width() < m_actualVisibleWidth || pixelContentsSize.height() < m_actualVisibleHeight)) 1611 zoomAboutPoint(initialScale(), newScrollPosition); 1612 } 1613 } 1614} 1615 1616void WebPagePrivate::zoomToInitialScaleOnLoad() 1617{ 1618#if DEBUG_WEBPAGE_LOAD 1619 Platform::logAlways(Platform::LogLevelInfo, "WebPagePrivate::zoomToInitialScaleOnLoad"); 1620#endif 1621 1622 bool needsLayout = false; 1623 1624 // If the contents width exceeds the viewport width set to desktop mode. 1625 if (m_shouldUseFixedDesktopMode) 1626 needsLayout = setViewMode(FixedDesktop); 1627 else 1628 needsLayout = setViewMode(Desktop); 1629 1630 if (needsLayout) { 1631 // This can cause recursive layout... 1632 setNeedsLayout(); 1633 } 1634 1635 if (contentsSize().isEmpty()) { 1636#if DEBUG_WEBPAGE_LOAD 1637 Platform::logAlways(Platform::LogLevelInfo, "WebPagePrivate::zoomToInitialScaleOnLoad content is empty!"); 1638#endif 1639 layoutIfNeeded(); 1640 notifyTransformedContentsSizeChanged(); 1641 return; 1642 } 1643 1644 bool performedZoom = false; 1645 bool shouldZoom = !m_userPerformedManualZoom; 1646 1647 // If this is a back/forward type navigation, don't zoom to initial scale 1648 // but instead let the HistoryItem's saved viewport reign supreme. 1649 if (m_mainFrame && m_mainFrame->loader() && isBackForwardLoadType(m_mainFrame->loader()->loadType())) 1650 shouldZoom = false; 1651 1652 if (shouldZoom && shouldZoomToInitialScaleOnLoad()) { 1653 // Preserve at top and at left position, to avoid scrolling 1654 // to a non top-left position for web page with viewport meta tag 1655 // that specifies an initial-scale that is zoomed in. 1656 FloatPoint anchor = centerOfVisibleContentsRect(); 1657 if (!scrollPosition().x()) 1658 anchor.setX(0); 1659 if (!scrollPosition().y()) 1660 anchor.setY(0); 1661 performedZoom = zoomAboutPoint(initialScale(), anchor); 1662 } 1663 1664 // zoomAboutPoint above can also toggle setNeedsLayout and cause recursive layout... 1665 layoutIfNeeded(); 1666 1667 if (!performedZoom) { 1668 // We only notify if we didn't perform zoom, because zoom will notify on 1669 // its own... 1670 notifyTransformedContentsSizeChanged(); 1671 } 1672} 1673 1674double WebPagePrivate::zoomToFitScale() const 1675{ 1676 int contentWidth = contentsSize().width(); 1677 1678 // For image document, zoom to fit the screen based on the actual image width 1679 // instead of the contents width within a maximum scale . 1680 Document* doc = m_page->mainFrame()->document(); 1681 bool isImageDocument = doc && doc->isImageDocument(); 1682 if (isImageDocument) 1683 contentWidth = static_cast<ImageDocument*>(doc)->imageSize().width(); 1684 1685 double zoomToFitScale = contentWidth > 0.0 ? static_cast<double>(m_actualVisibleWidth) / contentWidth : 1.0; 1686 int contentHeight = contentsSize().height(); 1687 if (contentHeight * zoomToFitScale < static_cast<double>(m_defaultLayoutSize.height())) 1688 zoomToFitScale = contentHeight > 0 ? static_cast<double>(m_defaultLayoutSize.height()) / contentHeight : 1.0; 1689 zoomToFitScale = std::max(zoomToFitScale, minimumZoomToFitScale); 1690 1691 if (!isImageDocument) 1692 return zoomToFitScale; 1693 1694 return std::min(zoomToFitScale, maximumImageDocumentZoomToFitScale); 1695} 1696 1697bool WebPagePrivate::respectViewport() const 1698{ 1699 return m_forceRespectViewportArguments || contentsSize().width() <= m_virtualViewportSize.width() + 1; 1700} 1701 1702double WebPagePrivate::initialScale() const 1703{ 1704 1705 if (m_initialScale > 0.0 && respectViewport()) 1706 return m_initialScale; 1707 1708 if (m_webSettings->isZoomToFitOnLoad()) 1709 return zoomToFitScale(); 1710 1711 return 1.0; 1712} 1713 1714double WebPage::initialScale() const 1715{ 1716 return d->initialScale(); 1717} 1718 1719void WebPage::initializeIconDataBase() 1720{ 1721 IconDatabaseClientBlackBerry::instance()->initIconDatabase(d->m_webSettings); 1722} 1723 1724bool WebPage::isUserScalable() const 1725{ 1726 return d->isUserScalable(); 1727} 1728 1729void WebPage::setUserScalable(bool userScalable) 1730{ 1731 d->setUserScalable(userScalable); 1732} 1733 1734double WebPage::currentScale() const 1735{ 1736 return d->currentScale(); 1737} 1738 1739void WebPage::setInitialScale(double initialScale) 1740{ 1741 d->setInitialScale(initialScale); 1742} 1743 1744double WebPage::minimumScale() const 1745{ 1746 return d->minimumScale(); 1747} 1748 1749void WebPage::setMinimumScale(double minimumScale) 1750{ 1751 d->setMinimumScale(minimumScale); 1752} 1753 1754void WebPage::setMaximumScale(double maximumScale) 1755{ 1756 d->setMaximumScale(maximumScale); 1757} 1758 1759double WebPagePrivate::maximumScale() const 1760{ 1761 double zoomToFitScale = this->zoomToFitScale(); 1762 if (m_maximumScale >= m_minimumScale && respectViewport()) 1763 return std::max(zoomToFitScale, m_maximumScale); 1764 1765 return hasVirtualViewport() ? std::max<double>(zoomToFitScale, 4.0) : 4.0; 1766} 1767 1768double WebPage::maximumScale() const 1769{ 1770 return d->maximumScale(); 1771} 1772 1773void WebPagePrivate::resetScales() 1774{ 1775 TransformationMatrix identity; 1776 *m_transformationMatrix = identity; 1777 m_initialScale = m_webSettings->initialScale() > 0 ? m_webSettings->initialScale() : -1.0; 1778 m_minimumScale = -1.0; 1779 m_maximumScale = -1.0; 1780 m_client->scaleChanged(); 1781 1782 // We have to let WebCore know about updated framerect now that we've 1783 // reset our scales. See: RIM Bug #401. 1784 updateViewportSize(); 1785} 1786 1787IntPoint WebPagePrivate::transformedScrollPosition() const 1788{ 1789 return m_backingStoreClient->transformedScrollPosition(); 1790} 1791 1792IntPoint WebPagePrivate::transformedMaximumScrollPosition() const 1793{ 1794 return m_backingStoreClient->transformedMaximumScrollPosition(); 1795} 1796 1797IntSize WebPagePrivate::transformedActualVisibleSize() const 1798{ 1799 return IntSize(m_actualVisibleWidth, m_actualVisibleHeight); 1800} 1801 1802Platform::ViewportAccessor* WebPage::webkitThreadViewportAccessor() const 1803{ 1804 return d->m_webkitThreadViewportAccessor; 1805} 1806 1807Platform::IntSize WebPage::viewportSize() const 1808{ 1809 return d->transformedActualVisibleSize(); 1810} 1811 1812IntSize WebPagePrivate::transformedViewportSize() const 1813{ 1814 return BlackBerry::Platform::Settings::instance()->applicationSize(); 1815} 1816 1817void WebPagePrivate::notifyTransformChanged() 1818{ 1819 notifyTransformedContentsSizeChanged(); 1820 notifyTransformedScrollChanged(); 1821 1822 m_backingStore->d->transformChanged(); 1823} 1824 1825void WebPagePrivate::notifyTransformedContentsSizeChanged() 1826{ 1827 // We mark here as the last reported content size we sent to the client. 1828 m_previousContentsSize = contentsSize(); 1829 1830 const IntSize size = m_webkitThreadViewportAccessor->pixelContentsSize(); 1831 m_backingStore->d->contentsSizeChanged(size); 1832 m_client->contentsSizeChanged(); 1833} 1834 1835void WebPagePrivate::notifyTransformedScrollChanged() 1836{ 1837 const IntPoint pos = transformedScrollPosition(); 1838 m_backingStore->d->scrollChanged(pos); 1839 m_client->scrollChanged(); 1840 1841#if ENABLE(FULLSCREEN_API) 1842 adjustFullScreenElementDimensionsIfNeeded(); 1843#endif 1844} 1845 1846bool WebPagePrivate::setViewMode(ViewMode mode) 1847{ 1848 if (!m_mainFrame || !m_mainFrame->view()) 1849 return false; 1850 1851 m_viewMode = mode; 1852 1853 // If we're in the middle of a nested layout with a recursion count above 1854 // some maximum threshold, then our algorithm for finding the minimum content 1855 // width of a given page has become dependent on the visible width. 1856 // 1857 // We need to find some method to ensure that we don't experience excessive 1858 // and even infinite recursion. This can even happen with valid html. The 1859 // former can happen when we run into inline text with few candidates for line 1860 // break. The latter can happen for instance if the page has a negative margin 1861 // set against the right border. Note: this is valid by spec and can lead to 1862 // a situation where there is no value for which the content width will ensure 1863 // no horizontal scrollbar. 1864 // Example: LayoutTests/css1/box_properties/margin.html 1865 // 1866 // In order to address such situations when we detect a recursion above some 1867 // maximum threshold we snap our fixed layout size to a defined quantum increment. 1868 // Eventually, either the content width will be satisfied to ensure no horizontal 1869 // scrollbar or this increment will run into the maximum layout size and the 1870 // recursion will necessarily end. 1871 bool snapToIncrement = didLayoutExceedMaximumIterations(); 1872 1873 IntSize currentSize = m_mainFrame->view()->fixedLayoutSize(); 1874 IntSize newSize = fixedLayoutSize(snapToIncrement); 1875 if (currentSize == newSize) 1876 return false; 1877 1878 // FIXME: Temp solution. We'll get back to this. 1879 if (m_nestedLayoutFinishedCount) { 1880 double widthChange = fabs(double(newSize.width() - currentSize.width()) / currentSize.width()); 1881 double heightChange = fabs(double(newSize.height() - currentSize.height()) / currentSize.height()); 1882 if (widthChange < 0.05 && heightChange < 0.05) 1883 return false; 1884 } 1885 1886 m_mainFrame->view()->setUseFixedLayout(useFixedLayout()); 1887 m_mainFrame->view()->setFixedLayoutSize(newSize); 1888 return true; // Needs re-layout! 1889} 1890 1891int WebPagePrivate::playerID() const 1892{ 1893 return m_client ? m_client->getInstanceId() : 0; 1894} 1895 1896void WebPagePrivate::setCursor(PlatformCursor handle) 1897{ 1898 if (m_currentCursor.type() != handle.type()) { 1899 m_currentCursor = handle; 1900 m_client->cursorChanged(handle.type(), handle.url().c_str(), handle.hotspot()); 1901 } 1902} 1903 1904Platform::NetworkStreamFactory* WebPagePrivate::networkStreamFactory() 1905{ 1906 return m_client->networkStreamFactory(); 1907} 1908 1909Platform::Graphics::Window* WebPagePrivate::platformWindow() const 1910{ 1911 return m_client->window(); 1912} 1913 1914void WebPagePrivate::setPreventsScreenDimming(bool keepAwake) 1915{ 1916 if (keepAwake) { 1917 if (!m_preventIdleDimmingCount) 1918 m_client->setPreventsScreenIdleDimming(true); 1919 m_preventIdleDimmingCount++; 1920 } else if (m_preventIdleDimmingCount > 0) { 1921 m_preventIdleDimmingCount--; 1922 if (!m_preventIdleDimmingCount) 1923 m_client->setPreventsScreenIdleDimming(false); 1924 } else 1925 ASSERT_NOT_REACHED(); // SetPreventsScreenIdleDimming(false) called too many times. 1926} 1927 1928void WebPagePrivate::showVirtualKeyboard(bool showKeyboard) 1929{ 1930 m_client->showVirtualKeyboard(showKeyboard); 1931} 1932 1933void WebPagePrivate::ensureContentVisible(bool centerInView) 1934{ 1935 m_inputHandler->ensureFocusElementVisible(centerInView); 1936} 1937 1938void WebPagePrivate::zoomToContentRect(const IntRect& rect) 1939{ 1940 // Don't scale if the user is not supposed to scale. 1941 if (!isUserScalable()) 1942 return; 1943 1944 FloatPoint anchor = FloatPoint(rect.width() / 2.0 + rect.x(), rect.height() / 2.0 + rect.y()); 1945 IntSize viewSize = viewportSize(); 1946 1947 // Calculate the scale required to scale that dimension to fit. 1948 double scaleH = (double)viewSize.width() / (double)rect.width(); 1949 double scaleV = (double)viewSize.height() / (double)rect.height(); 1950 1951 // Choose the smaller scale factor so that all of the content is visible. 1952 zoomAboutPoint(min(scaleH, scaleV), anchor); 1953} 1954 1955void WebPagePrivate::registerPlugin(PluginView* plugin, bool shouldRegister) 1956{ 1957 if (shouldRegister) 1958 m_pluginViews.add(plugin); 1959 else 1960 m_pluginViews.remove(plugin); 1961} 1962 1963#define FOR_EACH_PLUGINVIEW(pluginViews) \ 1964 HashSet<PluginView*>::const_iterator it = pluginViews.begin(); \ 1965 HashSet<PluginView*>::const_iterator last = pluginViews.end(); \ 1966 for (; it != last; ++it) 1967 1968void WebPagePrivate::notifyPageOnLoad() 1969{ 1970 FOR_EACH_PLUGINVIEW(m_pluginViews) 1971 (*it)->handleOnLoadEvent(); 1972} 1973 1974bool WebPagePrivate::shouldPluginEnterFullScreen(PluginView*, const char*) 1975{ 1976 return m_client->shouldPluginEnterFullScreen(); 1977} 1978 1979void WebPagePrivate::didPluginEnterFullScreen(PluginView* plugin, const char* windowUniquePrefix) 1980{ 1981 m_fullScreenPluginView = plugin; 1982 m_client->didPluginEnterFullScreen(); 1983 1984 if (!m_client->window()) 1985 return; 1986 1987 Platform::Graphics::Window::setTransparencyDiscardFilter(windowUniquePrefix); 1988 m_client->window()->setSensitivityFullscreenOverride(true); 1989} 1990 1991void WebPagePrivate::didPluginExitFullScreen(PluginView*, const char*) 1992{ 1993 m_fullScreenPluginView = 0; 1994 m_client->didPluginExitFullScreen(); 1995 1996 if (!m_client->window()) 1997 return; 1998 1999 Platform::Graphics::Window::setTransparencyDiscardFilter(0); 2000 m_client->window()->setSensitivityFullscreenOverride(false); 2001} 2002 2003void WebPagePrivate::onPluginStartBackgroundPlay(PluginView*, const char*) 2004{ 2005 m_client->onPluginStartBackgroundPlay(); 2006} 2007 2008void WebPagePrivate::onPluginStopBackgroundPlay(PluginView*, const char*) 2009{ 2010 m_client->onPluginStopBackgroundPlay(); 2011} 2012 2013bool WebPagePrivate::lockOrientation(bool landscape) 2014{ 2015 return m_client->lockOrientation(landscape); 2016} 2017 2018void WebPagePrivate::unlockOrientation() 2019{ 2020 return m_client->unlockOrientation(); 2021} 2022 2023int WebPagePrivate::orientation() const 2024{ 2025#if ENABLE(ORIENTATION_EVENTS) 2026 return m_mainFrame->orientation(); 2027#else 2028#error ORIENTATION_EVENTS must be defined. 2029// Or a copy of the orientation value will have to be stored in these objects. 2030#endif 2031} 2032 2033double WebPagePrivate::currentZoomFactor() const 2034{ 2035 return currentScale(); 2036} 2037 2038int WebPagePrivate::showAlertDialog(WebPageClient::AlertType atype) 2039{ 2040 return m_client->showAlertDialog(atype); 2041} 2042 2043bool WebPagePrivate::isActive() const 2044{ 2045 return m_client->isActive(); 2046} 2047 2048void WebPagePrivate::authenticationChallenge(const KURL& url, const ProtectionSpace& protectionSpace, const Credential& inputCredential) 2049{ 2050 AuthenticationChallengeManager* authmgr = AuthenticationChallengeManager::instance(); 2051 BlackBerry::Platform::String username; 2052 BlackBerry::Platform::String password; 2053 BlackBerry::Platform::String requestURL(url.string()); 2054 2055#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 2056 if (m_dumpRenderTree) { 2057 Credential credential(inputCredential, inputCredential.persistence()); 2058 if (m_dumpRenderTree->didReceiveAuthenticationChallenge(credential)) 2059 authmgr->notifyChallengeResult(url, protectionSpace, AuthenticationChallengeSuccess, credential); 2060 else 2061 authmgr->notifyChallengeResult(url, protectionSpace, AuthenticationChallengeCancelled, inputCredential); 2062 return; 2063 } 2064#endif 2065 2066#if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST) 2067 if (m_webSettings->isCredentialAutofillEnabled() && !m_webSettings->isPrivateBrowsingEnabled()) 2068 credentialManager().autofillAuthenticationChallenge(protectionSpace, username, password); 2069#endif 2070 2071 bool isConfirmed = m_client->authenticationChallenge(protectionSpace.realm().characters(), protectionSpace.realm().length(), username, password, requestURL, protectionSpace.isProxy()); 2072 2073#if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST) 2074 Credential credential(username, password, CredentialPersistencePermanent); 2075 if (m_webSettings->isCredentialAutofillEnabled() && !m_webSettings->isPrivateBrowsingEnabled() && isConfirmed) 2076 credentialManager().saveCredentialIfConfirmed(this, CredentialTransformData(protectionSpace, credential)); 2077#else 2078 Credential credential(username, password, CredentialPersistenceNone); 2079#endif 2080 2081 if (isConfirmed) 2082 authmgr->notifyChallengeResult(url, protectionSpace, AuthenticationChallengeSuccess, credential); 2083 else 2084 authmgr->notifyChallengeResult(url, protectionSpace, AuthenticationChallengeCancelled, inputCredential); 2085} 2086 2087PageClientBlackBerry::SaveCredentialType WebPagePrivate::notifyShouldSaveCredential(bool isNew) 2088{ 2089 return static_cast<PageClientBlackBerry::SaveCredentialType>(m_client->notifyShouldSaveCredential(isNew)); 2090} 2091 2092void WebPagePrivate::syncProxyCredential(const WebCore::Credential& credential) 2093{ 2094 m_client->syncProxyCredential(credential.user(), credential.password()); 2095} 2096 2097void WebPagePrivate::notifyPopupAutofillDialog(const Vector<String>& candidates) 2098{ 2099 vector<BlackBerry::Platform::String> textItems; 2100 for (size_t i = 0; i < candidates.size(); i++) 2101 textItems.push_back(candidates[i]); 2102 m_client->notifyPopupAutofillDialog(textItems); 2103} 2104 2105void WebPagePrivate::notifyDismissAutofillDialog() 2106{ 2107 m_client->notifyDismissAutofillDialog(); 2108} 2109 2110bool WebPagePrivate::useFixedLayout() const 2111{ 2112 return true; 2113} 2114 2115Platform::WebContext WebPagePrivate::webContext(TargetDetectionStrategy strategy) 2116{ 2117 Platform::WebContext context; 2118 2119 RefPtr<Node> node = contextNode(strategy); 2120 m_currentContextNode = node; 2121 if (!m_currentContextNode) 2122 return context; 2123 2124 // Send an onContextMenu event to the current context ndoe and get the result. Since we've already figured out 2125 // which node we want, we can send it directly to the node and not do a hit test. The onContextMenu event doesn't require 2126 // mouse positions so we just set the position at (0,0) 2127 PlatformMouseEvent mouseEvent(IntPoint(), IntPoint(), PlatformEvent::MouseMoved, 0, NoButton, false, false, false, TouchScreen); 2128 if (!m_currentContextNode->dispatchMouseEvent(mouseEvent, eventNames().contextmenuEvent, 0)) { 2129 context.setFlag(Platform::WebContext::IsOnContextMenuPrevented); 2130 return context; 2131 } 2132 2133 layoutIfNeeded(); 2134 2135 bool nodeAllowSelectionOverride = false; 2136 bool nodeIsImage = node->isHTMLElement() && node->hasTagName(HTMLNames::imgTag); 2137 Node* linkNode = node->enclosingLinkEventParentOrSelf(); 2138 // Set link url only when the node is linked image, or text inside anchor. Prevent CCM popup when long press non-link element(eg. button) inside an anchor. 2139 if (linkNode && (node == linkNode || node->isTextNode() || nodeIsImage)) { 2140 KURL href; 2141 if (linkNode->isLink() && linkNode->hasAttributes()) { 2142 if (const Attribute* attribute = toElement(linkNode)->getAttributeItem(HTMLNames::hrefAttr)) 2143 href = linkNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(attribute->value())); 2144 } 2145 2146 String pattern = findPatternStringForUrl(href); 2147 if (!pattern.isEmpty()) 2148 context.setPattern(pattern); 2149 2150 if (!href.string().isEmpty()) { 2151 context.setUrl(href.string()); 2152 2153 // Links are non-selectable by default, but selection should be allowed 2154 // providing the page is selectable, use the parent to determine it. 2155 if (linkNode->parentNode() && linkNode->parentNode()->canStartSelection()) 2156 nodeAllowSelectionOverride = true; 2157 } 2158 } 2159 2160 if (node->isHTMLElement()) { 2161 HTMLImageElement* imageElement = 0; 2162 HTMLMediaElement* mediaElement = 0; 2163 2164 if (node->hasTagName(HTMLNames::imgTag)) 2165 imageElement = static_cast<HTMLImageElement*>(node.get()); 2166 else if (node->hasTagName(HTMLNames::areaTag)) 2167 imageElement = static_cast<HTMLAreaElement*>(node.get())->imageElement(); 2168 2169 if (static_cast<HTMLElement*>(node.get())->isMediaElement()) 2170 mediaElement = static_cast<HTMLMediaElement*>(node.get()); 2171 2172 if (imageElement && imageElement->renderer()) { 2173 context.setFlag(Platform::WebContext::IsImage); 2174 // FIXME: At the mean time, we only show "Save Image" when the image data is available. 2175 if (CachedImage* cachedImage = imageElement->cachedImage()) { 2176 if (cachedImage->isLoaded() && cachedImage->resourceBuffer()) { 2177 String url = stripLeadingAndTrailingHTMLSpaces(imageElement->getAttribute(HTMLNames::srcAttr).string()); 2178 context.setSrc(node->document()->completeURL(url).string()); 2179 2180 String mimeType = cachedImage->response().mimeType(); 2181 if (mimeType.isEmpty()) { 2182 StringBuilder builder; 2183 String extension = cachedImage->image()->filenameExtension(); 2184 builder.append("image/"); 2185 if (extension.isEmpty()) 2186 builder.append("unknown"); 2187 else if (extension == "jpg") 2188 builder.append("jpeg"); 2189 else 2190 builder.append(extension); 2191 mimeType = builder.toString(); 2192 } 2193 context.setMimeType(mimeType); 2194 } 2195 } 2196 String alt = imageElement->altText(); 2197 if (!alt.isNull()) 2198 context.setAlt(alt); 2199 } 2200 2201 if (mediaElement) { 2202 if (mediaElement->hasAudio()) 2203 context.setFlag(Platform::WebContext::IsAudio); 2204 if (mediaElement->hasVideo()) 2205 context.setFlag(Platform::WebContext::IsVideo); 2206 2207 String src = stripLeadingAndTrailingHTMLSpaces(mediaElement->getAttribute(HTMLNames::srcAttr).string()); 2208 context.setSrc(node->document()->completeURL(src).string()); 2209 } 2210 } 2211 2212 if (node->isTextNode()) { 2213 Text* curText = toText(node.get()); 2214 if (!curText->wholeText().isEmpty()) 2215 context.setText(curText->wholeText()); 2216 } 2217 2218 bool canStartSelection = node->canStartSelection(); 2219 2220 if (node->isElementNode()) { 2221 Element* element = toElement(node->deprecatedShadowAncestorNode()); 2222 2223 if (DOMSupport::isTextBasedContentEditableElement(element)) { 2224 if (!canStartSelection) { 2225 // Input fields host node is by spec non-editable unless the field itself has content editable enabled. 2226 // Enable selection if the shadow tree for the input field is selectable. 2227 Node* nodeUnderFinger = m_touchEventHandler->lastFatFingersResult().isValid() ? m_touchEventHandler->lastFatFingersResult().node(FatFingersResult::ShadowContentAllowed) : 0; 2228 if (nodeUnderFinger) 2229 canStartSelection = nodeUnderFinger->canStartSelection(); 2230 } 2231 context.setFlag(Platform::WebContext::IsInput); 2232 if (element->hasTagName(HTMLNames::inputTag)) 2233 context.setFlag(Platform::WebContext::IsSingleLine); 2234 if (DOMSupport::isPasswordElement(element)) 2235 context.setFlag(Platform::WebContext::IsPassword); 2236 2237 String elementText(DOMSupport::inputElementText(element)); 2238 if (!elementText.stripWhiteSpace().isEmpty()) 2239 context.setText(elementText); 2240 else if (!node->focused() && m_touchEventHandler->lastFatFingersResult().isValid() && strategy == RectBased) { 2241 // If an input field is empty and not focused send a mouse click so that it gets a cursor and we can paste into it. 2242 m_touchEventHandler->sendClickAtFatFingersPoint(); 2243 } 2244 } 2245 } 2246 2247 if (!nodeAllowSelectionOverride && !canStartSelection) 2248 context.resetFlag(Platform::WebContext::IsSelectable); 2249 2250 if (node->isFocusable()) 2251 context.setFlag(Platform::WebContext::IsFocusable); 2252 2253 // Walk up the node tree looking for our custom webworks context attribute. 2254 while (node) { 2255 if (node->isElementNode()) { 2256 Element* element = toElement(node->deprecatedShadowAncestorNode()); 2257 String webWorksContext(DOMSupport::webWorksContext(element)); 2258 if (!webWorksContext.stripWhiteSpace().isEmpty()) { 2259 context.setFlag(Platform::WebContext::IsWebWorksContext); 2260 context.setWebWorksContext(webWorksContext); 2261 break; 2262 } 2263 } 2264 node = node->parentNode(); 2265 } 2266 2267 return context; 2268} 2269 2270Platform::WebContext WebPage::webContext(TargetDetectionStrategy strategy) const 2271{ 2272 return d->webContext(strategy); 2273} 2274 2275void WebPagePrivate::updateCursor() 2276{ 2277 int buttonMask = 0; 2278 if (m_lastMouseEvent.button() == LeftButton) 2279 buttonMask = Platform::MouseEvent::ScreenLeftMouseButton; 2280 else if (m_lastMouseEvent.button() == MiddleButton) 2281 buttonMask = Platform::MouseEvent::ScreenMiddleMouseButton; 2282 else if (m_lastMouseEvent.button() == RightButton) 2283 buttonMask = Platform::MouseEvent::ScreenRightMouseButton; 2284 2285 unsigned modifiers = m_lastMouseEvent.shiftKey() ? 0 : KEYMOD_SHIFT | 2286 m_lastMouseEvent.ctrlKey() ? 0 : KEYMOD_CTRL | 2287 m_lastMouseEvent.altKey() ? 0 : KEYMOD_ALT; 2288 2289 const Platform::ViewportAccessor* viewportAccessor = m_webkitThreadViewportAccessor; 2290 2291 BlackBerry::Platform::MouseEvent event(buttonMask, buttonMask, 2292 viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatPoint(m_lastMouseEvent.position())), 2293 viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatPoint(m_lastMouseEvent.globalPosition())), 2294 0, modifiers, 0); 2295 2296 // We have added document viewport position and document content position as members of the mouse event. When we create the event, we should initialize them as well. 2297 event.populateDocumentPosition(m_lastMouseEvent.position(), viewportAccessor->documentContentsFromViewport(m_lastMouseEvent.position())); 2298 2299 m_webPage->mouseEvent(event); 2300} 2301 2302IntSize WebPagePrivate::fixedLayoutSize(bool snapToIncrement) const 2303{ 2304 if (hasVirtualViewport()) 2305 return m_virtualViewportSize; 2306 2307 const int defaultLayoutWidth = m_defaultLayoutSize.width(); 2308 const int defaultLayoutHeight = m_defaultLayoutSize.height(); 2309 2310 int minWidth = defaultLayoutWidth; 2311 int maxWidth = DEFAULT_MAX_LAYOUT_WIDTH; 2312 int maxHeight = DEFAULT_MAX_LAYOUT_HEIGHT; 2313 2314 // If the load state is none then we haven't actually got anything yet, but we need to layout 2315 // the entire page so that the user sees the entire page (unrendered) instead of just part of it. 2316 // If the load state is Provisional, it is possible that absoluteVisibleOverflowSize() and 2317 // contentsSize() are based on the old page and cause inconsistent fixedLayoutSize, so layout the 2318 // new page based on the defaultLayoutSize as well. 2319 if (m_loadState == None || m_loadState == Provisional) 2320 return IntSize(defaultLayoutWidth, defaultLayoutHeight); 2321 2322 if (m_viewMode == FixedDesktop) { 2323 int width = maxWidth; 2324 // if the defaultLayoutHeight is at minimum, it probably was set as 0 2325 // and clamped, meaning it's effectively not set. (Even if it happened 2326 // to be set exactly to the minimum, it's too small to be useful.) So 2327 // ignore it. 2328 int height; 2329 if (defaultLayoutHeight <= minimumLayoutSize.height()) 2330 height = maxHeight; 2331 else 2332 height = ceilf(static_cast<float>(width) / static_cast<float>(defaultLayoutWidth) * static_cast<float>(defaultLayoutHeight)); 2333 return IntSize(width, height); 2334 } 2335 2336 if (m_viewMode == Desktop) { 2337 // If we detect an overflow larger than the contents size then use that instead since 2338 // it'll still be clamped by the maxWidth below... 2339 int width = std::max(absoluteVisibleOverflowSize().width(), contentsSize().width()); 2340 if (m_pendingOrientation != -1 && !m_nestedLayoutFinishedCount && !m_overflowExceedsContentsSize) 2341 width = 0; 2342 2343 if (snapToIncrement) { 2344 // Snap to increments of defaultLayoutWidth / 2.0. 2345 float factor = static_cast<float>(width) / (defaultLayoutWidth / 2.0); 2346 factor = ceilf(factor); 2347 width = (defaultLayoutWidth / 2.0) * factor; 2348 } 2349 2350 if (width < minWidth) 2351 width = minWidth; 2352 if (width > maxWidth) 2353 width = maxWidth; 2354 int height = ceilf(static_cast<float>(width) / static_cast<float>(defaultLayoutWidth) * static_cast<float>(defaultLayoutHeight)); 2355 return IntSize(width, height); 2356 } 2357 2358 ASSERT_NOT_REACHED(); 2359 return IntSize(defaultLayoutWidth, defaultLayoutHeight); 2360} 2361 2362BackingStoreClient* WebPagePrivate::backingStoreClient() const 2363{ 2364 return m_backingStoreClient; 2365} 2366 2367void WebPagePrivate::clearDocumentData(const Document* documentGoingAway) 2368{ 2369 ASSERT(documentGoingAway); 2370 if (m_currentContextNode && m_currentContextNode->document() == documentGoingAway) 2371 m_currentContextNode = 0; 2372 2373 if (m_currentPinchZoomNode && m_currentPinchZoomNode->document() == documentGoingAway) 2374 m_currentPinchZoomNode = 0; 2375 2376 if (m_currentBlockZoomAdjustedNode && m_currentBlockZoomAdjustedNode->document() == documentGoingAway) 2377 m_currentBlockZoomAdjustedNode = 0; 2378 2379 if (m_inRegionScroller->d->isActive()) 2380 m_inRegionScroller->d->clearDocumentData(documentGoingAway); 2381 2382 if (documentGoingAway->frame()) 2383 m_inputHandler->frameUnloaded(documentGoingAway->frame()); 2384 2385 Node* nodeUnderFatFinger = m_touchEventHandler->lastFatFingersResult().node(); 2386 if (nodeUnderFatFinger && nodeUnderFatFinger->document() == documentGoingAway) 2387 m_touchEventHandler->resetLastFatFingersResult(); 2388 2389 // NOTE: m_fullscreenNode, m_fullScreenPluginView and m_pluginViews 2390 // are cleared in other methods already. 2391} 2392 2393typedef bool (*PredicateFunction)(RenderLayer*); 2394static bool isPositionedContainer(RenderLayer* layer) 2395{ 2396 RenderObject* o = layer->renderer(); 2397 return o->isRenderView() || o->isOutOfFlowPositioned() || o->isRelPositioned() || layer->hasTransform(); 2398} 2399 2400static bool isFixedPositionedContainer(RenderLayer* layer) 2401{ 2402 RenderObject* o = layer->renderer(); 2403 return o->isRenderView() || (o->isOutOfFlowPositioned() && o->style()->position() == FixedPosition); 2404} 2405 2406static RenderLayer* findAncestorOrSelfNotMatching(PredicateFunction predicate, RenderLayer* layer) 2407{ 2408 RenderLayer* curr = layer; 2409 while (curr && !predicate(curr)) 2410 curr = curr->parent(); 2411 2412 return curr; 2413} 2414 2415RenderLayer* WebPagePrivate::enclosingFixedPositionedAncestorOrSelfIfFixedPositioned(RenderLayer* layer) 2416{ 2417 return findAncestorOrSelfNotMatching(&isFixedPositionedContainer, layer); 2418} 2419 2420RenderLayer* WebPagePrivate::enclosingPositionedAncestorOrSelfIfPositioned(RenderLayer* layer) 2421{ 2422 return findAncestorOrSelfNotMatching(&isPositionedContainer, layer); 2423} 2424 2425static inline Frame* frameForNode(Node* node) 2426{ 2427 Node* origNode = node; 2428 for (; node; node = node->parentNode()) { 2429 if (RenderObject* renderer = node->renderer()) { 2430 if (renderer->isRenderView()) { 2431 if (FrameView* view = toRenderView(renderer)->frameView()) { 2432 if (Frame* frame = view->frame()) 2433 return frame; 2434 } 2435 } 2436 if (renderer->isWidget()) { 2437 Widget* widget = toRenderWidget(renderer)->widget(); 2438 if (widget && widget->isFrameView()) { 2439 if (Frame* frame = toFrameView(widget)->frame()) 2440 return frame; 2441 } 2442 } 2443 } 2444 } 2445 2446 for (node = origNode; node; node = node->parentNode()) { 2447 if (Document* doc = node->document()) { 2448 if (Frame* frame = doc->frame()) 2449 return frame; 2450 } 2451 } 2452 2453 return 0; 2454} 2455 2456static IntRect getNodeWindowRect(Node* node) 2457{ 2458 if (Frame* frame = frameForNode(node)) { 2459 if (FrameView* view = frame->view()) 2460 return view->contentsToWindow(node->getRect()); 2461 } 2462 ASSERT_NOT_REACHED(); 2463 return IntRect(); 2464} 2465 2466IntRect WebPagePrivate::getRecursiveVisibleWindowRect(ScrollView* view, bool noClipOfMainFrame) 2467{ 2468 ASSERT(m_mainFrame); 2469 2470 // Don't call this function asking to not clip the main frame providing only 2471 // the main frame. All that can be returned is the content rect which 2472 // isn't what this function is for. 2473 if (noClipOfMainFrame && view == m_mainFrame->view()) { 2474 ASSERT_NOT_REACHED(); 2475 return IntRect(IntPoint::zero(), view->contentsSize()); 2476 } 2477 2478 IntRect visibleWindowRect(view->contentsToWindow(view->visibleContentRect())); 2479 if (view->parent() && !(noClipOfMainFrame && view->parent() == m_mainFrame->view())) { 2480 // Intersect with parent visible rect. 2481 visibleWindowRect.intersect(getRecursiveVisibleWindowRect(view->parent(), noClipOfMainFrame)); 2482 } 2483 return visibleWindowRect; 2484} 2485 2486void WebPagePrivate::assignFocus(Platform::FocusDirection direction) 2487{ 2488 ASSERT((int) Platform::FocusDirectionNone == (int) FocusDirectionNone); 2489 ASSERT((int) Platform::FocusDirectionForward == (int) FocusDirectionForward); 2490 ASSERT((int) Platform::FocusDirectionBackward == (int) FocusDirectionBackward); 2491 2492 // First we clear the focus, since we want to focus either initial or the last 2493 // focusable element in the webpage (according to the TABINDEX), or simply clear 2494 // the focus. 2495 clearFocusNode(); 2496 2497 switch (direction) { 2498 case FocusDirectionForward: 2499 case FocusDirectionBackward: 2500 m_page->focusController()->setInitialFocus((FocusDirection) direction, 0); 2501 break; 2502 case FocusDirectionNone: 2503 break; 2504 default: 2505 ASSERT_NOT_REACHED(); 2506 } 2507} 2508 2509void WebPage::assignFocus(Platform::FocusDirection direction) 2510{ 2511 if (d->m_page->defersLoading()) 2512 return; 2513 d->assignFocus(direction); 2514} 2515 2516void WebPage::focusNextField() 2517{ 2518 d->m_inputHandler->focusNextField(); 2519} 2520 2521void WebPage::focusPreviousField() 2522{ 2523 d->m_inputHandler->focusPreviousField(); 2524} 2525 2526void WebPage::submitForm() 2527{ 2528 d->m_inputHandler->submitForm(); 2529} 2530 2531Platform::IntRect WebPagePrivate::focusNodeRect() 2532{ 2533 Frame* frame = focusedOrMainFrame(); 2534 if (!frame) 2535 return Platform::IntRect(); 2536 2537 Document* doc = frame->document(); 2538 FrameView* view = frame->view(); 2539 if (!doc || !view || view->needsLayout()) 2540 return Platform::IntRect(); 2541 2542 const Platform::ViewportAccessor* viewportAccessor = m_webkitThreadViewportAccessor; 2543 2544 IntRect focusRect = rectForNode(doc->focusedElement()); 2545 focusRect = adjustRectOffsetForFrameOffset(focusRect, doc->focusedElement()); 2546 focusRect = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(focusRect)); 2547 focusRect.intersect(viewportAccessor->pixelContentsRect()); 2548 return focusRect; 2549} 2550 2551PassRefPtr<Node> WebPagePrivate::contextNode(TargetDetectionStrategy strategy) 2552{ 2553 EventHandler* eventHandler = focusedOrMainFrame()->eventHandler(); 2554 const FatFingersResult lastFatFingersResult = m_touchEventHandler->lastFatFingersResult(); 2555 bool isTouching = lastFatFingersResult.isValid() && strategy == RectBased; 2556 2557 // Check if we're using LinkToLink and the user is not touching the screen. 2558 if (m_webSettings->doesGetFocusNodeContext() && !isTouching) { 2559 RefPtr<Node> node; 2560 node = m_page->focusController()->focusedOrMainFrame()->document()->focusedElement(); 2561 if (node) { 2562 IntRect visibleRect = IntRect(IntPoint(), actualVisibleSize()); 2563 if (!visibleRect.intersects(getNodeWindowRect(node.get()))) 2564 return 0; 2565 } 2566 return node.release(); 2567 } 2568 2569 // Check for text input. 2570 if (isTouching && lastFatFingersResult.isTextInput()) 2571 return lastFatFingersResult.node(FatFingersResult::ShadowContentNotAllowed); 2572 2573 if (strategy == RectBased) { 2574 FatFingersResult result = FatFingers(this, lastFatFingersResult.adjustedPosition(), FatFingers::Text).findBestPoint(); 2575 // Cache text result for later use. 2576 m_touchEventHandler->cacheTextResult(result); 2577 return result.node(FatFingersResult::ShadowContentNotAllowed); 2578 } 2579 if (strategy == FocusBased) 2580 return m_inputHandler->currentFocusElement(); 2581 2582 IntPoint contentPos; 2583 if (isTouching) 2584 contentPos = lastFatFingersResult.adjustedPosition(); 2585 else 2586 contentPos = m_webkitThreadViewportAccessor->documentContentsFromViewport(m_lastMouseEvent.position()); 2587 2588 HitTestResult result = eventHandler->hitTestResultAtPoint(contentPos); 2589 return result.innerNode(); 2590} 2591 2592static inline int distanceBetweenPoints(IntPoint p1, IntPoint p2) 2593{ 2594 // Change int to double, because (dy * dy) can cause int overflow in reality, e.g, (-46709 * -46709). 2595 double dx = static_cast<double>(p1.x() - p2.x()); 2596 double dy = static_cast<double>(p1.y() - p2.y()); 2597 return sqrt((dx * dx) + (dy * dy)); 2598} 2599 2600Node* WebPagePrivate::bestNodeForZoomUnderPoint(const IntPoint& documentPoint) 2601{ 2602 IntRect clickRect(documentPoint.x() - blockClickRadius, documentPoint.y() - blockClickRadius, 2 * blockClickRadius, 2 * blockClickRadius); 2603 Node* originalNode = nodeForZoomUnderPoint(documentPoint); 2604 if (!originalNode) 2605 return 0; 2606 Node* node = bestChildNodeForClickRect(originalNode, clickRect); 2607 return node ? adjustedBlockZoomNodeForZoomAndExpandingRatioLimits(node) : adjustedBlockZoomNodeForZoomAndExpandingRatioLimits(originalNode); 2608} 2609 2610Node* WebPagePrivate::bestChildNodeForClickRect(Node* parentNode, const IntRect& clickRect) 2611{ 2612 if (!parentNode) 2613 return 0; 2614 2615 int bestDistance = std::numeric_limits<int>::max(); 2616 2617 Node* node = parentNode->firstChild(); 2618 Node* bestNode = 0; 2619 for (; node; node = node->nextSibling()) { 2620 IntRect rect = rectForNode(node); 2621 if (!clickRect.intersects(rect)) 2622 continue; 2623 2624 int distance = distanceBetweenPoints(rect.center(), clickRect.center()); 2625 Node* bestChildNode = bestChildNodeForClickRect(node, clickRect); 2626 if (bestChildNode) { 2627 IntRect bestChildRect = rectForNode(bestChildNode); 2628 int bestChildDistance = distanceBetweenPoints(bestChildRect.center(), clickRect.center()); 2629 if (bestChildDistance < distance && bestChildDistance < bestDistance) { 2630 bestNode = bestChildNode; 2631 bestDistance = bestChildDistance; 2632 } else { 2633 if (distance < bestDistance) { 2634 bestNode = node; 2635 bestDistance = distance; 2636 } 2637 } 2638 } else { 2639 if (distance < bestDistance) { 2640 bestNode = node; 2641 bestDistance = distance; 2642 } 2643 } 2644 } 2645 2646 return bestNode; 2647} 2648 2649double WebPagePrivate::maxBlockZoomScale() const 2650{ 2651 return std::min(maximumBlockZoomScale, maximumScale()); 2652} 2653 2654Node* WebPagePrivate::nodeForZoomUnderPoint(const IntPoint& documentPoint) 2655{ 2656 if (!m_mainFrame) 2657 return 0; 2658 2659 HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(documentPoint); 2660 2661 Node* node = result.innerNonSharedNode(); 2662 2663 if (!node) 2664 return 0; 2665 2666 RenderObject* renderer = node->renderer(); 2667 while (!renderer) { 2668 node = node->parentNode(); 2669 renderer = node->renderer(); 2670 } 2671 2672 return node; 2673} 2674 2675Node* WebPagePrivate::adjustedBlockZoomNodeForZoomAndExpandingRatioLimits(Node* node) 2676{ 2677 Node* initialNode = node; 2678 RenderObject* renderer = node->renderer(); 2679 bool acceptableNodeSize = newScaleForBlockZoomRect(rectForNode(node), 1.0, 0) < maxBlockZoomScale(); 2680 IntSize actualVisibleSize = this->actualVisibleSize(); 2681 2682 while (!renderer || !acceptableNodeSize) { 2683 node = node->parentNode(); 2684 IntRect nodeRect = rectForNode(node); 2685 2686 // Don't choose a node if the width of the node size is very close to the width of the actual visible size, 2687 // as block zoom can do nothing on such kind of node. 2688 if (!node || static_cast<double>(actualVisibleSize.width() - nodeRect.width()) / actualVisibleSize.width() < minimumExpandingRatio) 2689 return initialNode; 2690 2691 renderer = node->renderer(); 2692 acceptableNodeSize = newScaleForBlockZoomRect(rectForNode(node), 1.0, 0) < maxBlockZoomScale(); 2693 } 2694 2695 return node; 2696} 2697 2698bool WebPagePrivate::compareNodesForBlockZoom(Node* n1, Node* n2) 2699{ 2700 if (!n1 || !n2) 2701 return false; 2702 2703 return (n2 == n1) || n2->isDescendantOf(n1); 2704} 2705 2706double WebPagePrivate::newScaleForBlockZoomRect(const IntRect& rect, double oldScale, double margin) 2707{ 2708 if (rect.isEmpty()) 2709 return std::numeric_limits<double>::max(); 2710 2711 ASSERT(rect.width() + margin); 2712 2713 double newScale = oldScale * static_cast<double>(transformedActualVisibleSize().width()) / (rect.width() + margin); 2714 2715 return newScale; 2716} 2717 2718IntRect WebPagePrivate::rectForNode(Node* node) 2719{ 2720 if (!node) 2721 return IntRect(); 2722 2723 RenderObject* renderer = node->renderer(); 2724 2725 if (!renderer) 2726 return IntRect(); 2727 2728 // Return rect in un-transformed content coordinates. 2729 IntRect blockRect; 2730 2731 // FIXME: Ensure this works with iframes. 2732 if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled && renderer->isText()) { 2733 RenderBlock* renderBlock = renderer->containingBlock(); 2734 int xOffset = 0; 2735 int yOffset = 0; 2736 while (!renderBlock->isRoot()) { 2737 xOffset += renderBlock->x().toInt(); 2738 yOffset += renderBlock->y().toInt(); 2739 renderBlock = renderBlock->containingBlock(); 2740 } 2741 const RenderText* renderText = toRenderText(renderer); 2742 IntRect linesBox = renderText->linesBoundingBox(); 2743 blockRect = IntRect(xOffset + linesBox.x(), yOffset + linesBox.y(), linesBox.width(), linesBox.height()); 2744 } else 2745 blockRect = IntRect(renderer->absoluteClippedOverflowRect()); 2746 2747 if (renderer->isText()) { 2748 RenderBlock* rb = renderer->containingBlock(); 2749 2750 // Inefficient? Way to find width when floats intersect a block. 2751 int blockWidth = 0; 2752 int lineCount = rb->lineCount(); 2753 for (int i = 0; i < lineCount; i++) 2754 blockWidth = max(blockWidth, rb->availableLogicalWidthForLine(i, false).toInt()); 2755 2756 blockRect.setWidth(blockWidth); 2757 blockRect.setX(blockRect.x() + rb->logicalLeftOffsetForLine(1, false)); 2758 } 2759 2760 // Strip off padding. 2761 if (renderer->style()->hasPadding()) { 2762 blockRect.setX(blockRect.x() + renderer->style()->paddingLeft().value()); 2763 blockRect.setY(blockRect.y() + renderer->style()->paddingTop().value()); 2764 blockRect.setWidth(blockRect.width() - renderer->style()->paddingRight().value()); 2765 blockRect.setHeight(blockRect.height() - renderer->style()->paddingBottom().value()); 2766 } 2767 2768 return blockRect; 2769} 2770 2771IntPoint WebPagePrivate::frameOffset(const Frame* frame) const 2772{ 2773 ASSERT(frame); 2774 2775 // FIXME: This function can be called when page is being destroyed and JS triggers selection change. 2776 // We could break the call chain at upper levels, but I think it is better to check the frame pointer 2777 // here because the pointer is explicitly cleared in WebPage::destroy(). 2778 if (!mainFrame()) 2779 return IntPoint(); 2780 2781 // Convert 0,0 in the frame's coordinate system to window coordinates to 2782 // get the frame's global position, and return this position in the main 2783 // frame's coordinates. (So the main frame's coordinates will be 0,0.) 2784 return mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint::zero())); 2785} 2786 2787IntRect WebPagePrivate::adjustRectOffsetForFrameOffset(const IntRect& rect, const Node* node) 2788{ 2789 if (!node) 2790 return rect; 2791 2792 // Adjust the offset of the rect if it is in an iFrame/frame or set of iFrames/frames. 2793 // FIXME: can we just use frameOffset instead of this big routine? 2794 const Node* tnode = node; 2795 IntRect adjustedRect = rect; 2796 do { 2797 Frame* frame = tnode->document()->frame(); 2798 if (!frame) 2799 continue; 2800 2801 Node* ownerNode = static_cast<Node*>(frame->ownerElement()); 2802 tnode = ownerNode; 2803 if (ownerNode && (ownerNode->hasTagName(HTMLNames::iframeTag) || ownerNode->hasTagName(HTMLNames::frameTag))) { 2804 IntRect iFrameRect; 2805 do { 2806 iFrameRect = rectForNode(ownerNode); 2807 adjustedRect.move(iFrameRect.x(), iFrameRect.y()); 2808 adjustedRect.intersect(iFrameRect); 2809 ownerNode = ownerNode->parentNode(); 2810 } while (iFrameRect.isEmpty() && ownerNode); 2811 } else 2812 break; 2813 } while ((tnode = tnode->parentNode())); 2814 2815 return adjustedRect; 2816} 2817 2818IntRect WebPagePrivate::blockZoomRectForNode(Node* node) 2819{ 2820 if (!node || contentsSize().isEmpty()) 2821 return IntRect(); 2822 2823 Node* tnode = node; 2824 m_currentBlockZoomAdjustedNode = tnode; 2825 2826 IntRect blockRect = rectForNode(tnode); 2827 IntRect originalRect = blockRect; 2828 2829 int originalArea = originalRect.width() * originalRect.height(); 2830 int pageArea = contentsSize().width() * contentsSize().height(); 2831 double blockToPageRatio = static_cast<double>(pageArea - originalArea) / pageArea; 2832 double blockExpansionRatio = 5.0 * blockToPageRatio * blockToPageRatio; 2833 2834 if (!tnode->hasTagName(HTMLNames::imgTag) && !tnode->hasTagName(HTMLNames::inputTag) && !tnode->hasTagName(HTMLNames::textareaTag)) { 2835 while ((tnode = tnode->parentNode())) { 2836 ASSERT(tnode); 2837 IntRect tRect = rectForNode(tnode); 2838 int tempBlockArea = tRect.width() * tRect.height(); 2839 // Don't expand the block if it will be too large relative to the content. 2840 if (static_cast<double>(pageArea - tempBlockArea) / pageArea < minimumExpandingRatio) 2841 break; 2842 if (tRect.isEmpty()) 2843 continue; // No renderer. 2844 if (tempBlockArea < 1.1 * originalArea) 2845 continue; // The size of this parent is very close to the child, no need to go to this parent. 2846 // Don't expand the block if the parent node size is already almost the size of actual visible size. 2847 IntSize actualSize = actualVisibleSize(); 2848 if (static_cast<double>(actualSize.width() - tRect.width()) / actualSize.width() < minimumExpandingRatio) 2849 break; 2850 if (tempBlockArea < blockExpansionRatio * originalArea) { 2851 blockRect = tRect; 2852 m_currentBlockZoomAdjustedNode = tnode; 2853 } else 2854 break; 2855 } 2856 } 2857 2858 const Platform::ViewportAccessor* viewportAccessor = m_webkitThreadViewportAccessor; 2859 2860 blockRect = adjustRectOffsetForFrameOffset(blockRect, node); 2861 blockRect = viewportAccessor->roundToPixelFromDocumentContents(WebCore::FloatRect(blockRect)); 2862 blockRect.intersect(viewportAccessor->pixelContentsRect()); 2863 2864 return blockRect; 2865} 2866 2867// This function should not be called directly. 2868// It is called after the animation ends (see above). 2869void WebPagePrivate::zoomAnimationFinished(double finalAnimationScale, const WebCore::FloatPoint& finalAnimationDocumentScrollPosition, bool shouldConstrainScrollingToContentEdge) 2870{ 2871 if (!m_mainFrame) 2872 return; 2873 2874 const Platform::ViewportAccessor* viewportAccessor = m_webkitThreadViewportAccessor; 2875 2876 IntPoint anchor(viewportAccessor->roundedDocumentContents(finalAnimationDocumentScrollPosition)); 2877 bool willUseTextReflow = false; 2878 2879#if ENABLE(VIEWPORT_REFLOW) 2880 willUseTextReflow = m_webPage->settings()->textReflowMode() != WebSettings::TextReflowDisabled; 2881 toggleTextReflowIfEnabledForBlockZoomOnly(m_shouldReflowBlock); 2882 setNeedsLayout(); 2883#endif 2884 2885 TransformationMatrix zoom; 2886 zoom.scale(finalAnimationScale); 2887 *m_transformationMatrix = zoom; 2888 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 2889 m_backingStore->d->suspendBackingStoreUpdates(); 2890 m_backingStore->d->suspendScreenUpdates(); 2891 updateViewportSize(); 2892 2893 FrameView* mainFrameView = m_mainFrame->view(); 2894 bool constrainsScrollingToContentEdge = true; 2895 if (mainFrameView) { 2896 constrainsScrollingToContentEdge = mainFrameView->constrainsScrollingToContentEdge(); 2897 mainFrameView->setConstrainsScrollingToContentEdge(shouldConstrainScrollingToContentEdge); 2898 } 2899 2900#if ENABLE(VIEWPORT_REFLOW) 2901 layoutIfNeeded(); 2902 if (willUseTextReflow && m_shouldReflowBlock) { 2903 Platform::IntPoint roundedReflowOffset( 2904 std::floorf(m_finalAnimationDocumentScrollPositionReflowOffset.x()), 2905 std::floorf(m_finalAnimationDocumentScrollPositionReflowOffset.y())); 2906 2907 IntRect reflowedRect = rectForNode(m_currentBlockZoomAdjustedNode.get()); 2908 reflowedRect = adjustRectOffsetForFrameOffset(reflowedRect, m_currentBlockZoomAdjustedNode.get()); 2909 reflowedRect.move(roundedReflowOffset.x(), roundedReflowOffset.y()); 2910 2911 RenderObject* renderer = m_currentBlockZoomAdjustedNode->renderer(); 2912 IntPoint topLeftPoint(reflowedRect.location()); 2913 if (renderer && renderer->isText()) { 2914 ETextAlign textAlign = renderer->style()->textAlign(); 2915 IntPoint textAnchor; 2916 switch (textAlign) { 2917 case CENTER: 2918 case WEBKIT_CENTER: 2919 textAnchor = IntPoint(reflowedRect.x() + (reflowedRect.width() - actualVisibleSize().width()) / 2, topLeftPoint.y()); 2920 break; 2921 case LEFT: 2922 case WEBKIT_LEFT: 2923 textAnchor = topLeftPoint; 2924 break; 2925 case RIGHT: 2926 case WEBKIT_RIGHT: 2927 textAnchor = IntPoint(reflowedRect.x() + reflowedRect.width() - actualVisibleSize().width(), topLeftPoint.y()); 2928 break; 2929 case TASTART: 2930 case JUSTIFY: 2931 default: 2932 if (renderer->style()->isLeftToRightDirection()) 2933 textAnchor = topLeftPoint; 2934 else 2935 textAnchor = IntPoint(reflowedRect.x() + reflowedRect.width() - actualVisibleSize().width(), topLeftPoint.y()); 2936 break; 2937 } 2938 setScrollPosition(textAnchor); 2939 } else { 2940 renderer->style()->isLeftToRightDirection() 2941 ? setScrollPosition(topLeftPoint) 2942 : setScrollPosition(IntPoint(reflowedRect.x() + reflowedRect.width() - actualVisibleSize().width(), topLeftPoint.y())); 2943 } 2944 } else if (willUseTextReflow) { 2945 IntRect finalRect = rectForNode(m_currentBlockZoomAdjustedNode.get()); 2946 finalRect = adjustRectOffsetForFrameOffset(finalRect, m_currentBlockZoomAdjustedNode.get()); 2947 setScrollPosition(IntPoint(0, finalRect.y() + m_finalAnimationDocumentScrollPositionReflowOffset.y())); 2948 resetBlockZoom(); 2949 } 2950#endif 2951 if (!willUseTextReflow) { 2952 setScrollPosition(anchor); 2953 if (!m_shouldReflowBlock) 2954 resetBlockZoom(); 2955 } 2956 2957 notifyTransformChanged(); 2958 m_client->scaleChanged(); 2959 2960 if (mainFrameView) 2961 mainFrameView->setConstrainsScrollingToContentEdge(constrainsScrollingToContentEdge); 2962 2963 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 2964 m_backingStore->d->resumeBackingStoreUpdates(); 2965 m_backingStore->d->resumeScreenUpdates(BackingStore::RenderAndBlit); 2966} 2967 2968void WebPage::zoomAnimationFinished(double finalScale, const Platform::FloatPoint& finalDocumentScrollPosition, bool shouldConstrainScrollingToContentEdge) 2969{ 2970 d->zoomAnimationFinished(finalScale, finalDocumentScrollPosition, shouldConstrainScrollingToContentEdge); 2971} 2972 2973void WebPage::resetBlockZoom() 2974{ 2975 d->resetBlockZoom(); 2976} 2977 2978void WebPagePrivate::resetBlockZoom() 2979{ 2980 m_currentBlockZoomNode = 0; 2981 m_currentBlockZoomAdjustedNode = 0; 2982 m_shouldReflowBlock = false; 2983} 2984 2985void WebPage::destroyWebPageCompositor() 2986{ 2987#if USE(ACCELERATED_COMPOSITING) 2988 // Destroy the layer renderer in a sync command before we destroy the backing store, 2989 // to flush any pending compositing messages on the compositing thread. 2990 // The backing store is indirectly deleted by the 'detachFromParent' call below. 2991 d->syncDestroyCompositorOnCompositingThread(); 2992#endif 2993} 2994 2995void WebPage::destroy() 2996{ 2997 // TODO: need to verify if this call needs to be made before calling 2998 // Close the Inspector to resume the JS engine if it's paused. 2999 disableWebInspector(); 3000 3001 // WebPage::destroyWebPageCompositor() 3002 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 3003 d->m_backingStore->d->suspendBackingStoreUpdates(); 3004 d->m_backingStore->d->suspendScreenUpdates(); 3005 3006 // Close the backforward list and release the cached pages. 3007 d->m_page->backForward()->close(); 3008 3009 FrameLoader* loader = d->m_mainFrame->loader(); 3010 3011 // Set m_mainFrame to 0 to avoid calls back in to the backingstore during webpage deletion. 3012 d->m_mainFrame = 0; 3013 if (loader) 3014 loader->detachFromParent(); 3015 3016 deleteGuardedObject(this); 3017} 3018 3019WebPageClient* WebPage::client() const 3020{ 3021 return d->m_client; 3022} 3023 3024int WebPage::backForwardListLength() const 3025{ 3026 return d->m_page->getHistoryLength(); 3027} 3028 3029bool WebPage::canGoBackOrForward(int delta) const 3030{ 3031 return d->m_page->canGoBackOrForward(delta); 3032} 3033 3034bool WebPage::goBackOrForward(int delta) 3035{ 3036 if (d->m_page->canGoBackOrForward(delta)) { 3037 d->m_userPerformedManualZoom = false; 3038 d->m_userPerformedManualScroll = false; 3039 d->m_backingStore->d->suspendScreenUpdates(); 3040 d->m_page->goBackOrForward(delta); 3041 d->m_backingStore->d->resumeScreenUpdates(BackingStore::None); 3042 return true; 3043 } 3044 return false; 3045} 3046 3047void WebPage::goToBackForwardEntry(BackForwardId id) 3048{ 3049 HistoryItem* item = historyItemFromBackForwardId(id); 3050 ASSERT(item); 3051 d->m_page->goToItem(item, FrameLoadTypeIndexedBackForward); 3052} 3053 3054void WebPage::reload() 3055{ 3056 d->m_mainFrame->loader()->reload(/* bypassCache */ true); 3057} 3058 3059void WebPage::reloadFromCache() 3060{ 3061 d->m_mainFrame->loader()->reload(/* bypassCache */ false); 3062} 3063 3064WebSettings* WebPage::settings() const 3065{ 3066 return d->m_webSettings; 3067} 3068 3069WebCookieJar* WebPage::cookieJar() const 3070{ 3071 if (!d->m_cookieJar) 3072 d->m_cookieJar = new WebCookieJar(); 3073 3074 return d->m_cookieJar; 3075} 3076 3077bool WebPage::isLoading() const 3078{ 3079 return d->isLoading(); 3080} 3081 3082bool WebPage::isVisible() const 3083{ 3084 return d->m_visible; 3085} 3086 3087#if ENABLE(PAGE_VISIBILITY_API) 3088class DeferredTaskSetPageVisibilityState: public DeferredTask<&WebPagePrivate::m_wouldSetPageVisibilityState> { 3089public: 3090 explicit DeferredTaskSetPageVisibilityState(WebPagePrivate* webPagePrivate) 3091 : DeferredTaskType(webPagePrivate) 3092 { 3093 } 3094private: 3095 virtual void performInternal(WebPagePrivate* webPagePrivate) 3096 { 3097 webPagePrivate->setPageVisibilityState(); 3098 } 3099}; 3100 3101void WebPagePrivate::setPageVisibilityState() 3102{ 3103 if (m_page->defersLoading()) 3104 m_deferredTasks.append(adoptPtr(new DeferredTaskSetPageVisibilityState(this))); 3105 else { 3106 DeferredTaskSetPageVisibilityState::finishOrCancel(this); 3107 3108 static bool s_initialVisibilityState = true; 3109 3110 m_page->setVisibilityState(m_visible && m_activationState == ActivationActive ? PageVisibilityStateVisible : PageVisibilityStateHidden, s_initialVisibilityState); 3111 s_initialVisibilityState = false; 3112 } 3113} 3114#endif 3115 3116void WebPagePrivate::setVisible(bool visible) 3117{ 3118 if (visible != m_visible) { 3119 if (visible) { 3120 if (m_mainFrame) 3121 m_mainFrame->animation()->resumeAnimations(); 3122 if (m_page->scriptedAnimationsSuspended()) 3123 m_page->resumeScriptedAnimations(); 3124 } else { 3125 if (m_mainFrame) 3126 m_mainFrame->animation()->suspendAnimations(); 3127 if (!m_page->scriptedAnimationsSuspended()) 3128 m_page->suspendScriptedAnimations(); 3129 3130 closePagePopup(); 3131 } 3132 3133 m_visible = visible; 3134 m_backingStore->d->updateSuspendScreenUpdateState(); 3135 } 3136 3137#if ENABLE(PAGE_VISIBILITY_API) 3138 setPageVisibilityState(); 3139#endif 3140} 3141 3142void WebPage::setVisible(bool visible) 3143{ 3144 if (d->m_visible == visible) 3145 return; 3146 3147 d->setVisible(visible); 3148 AuthenticationChallengeManager::instance()->pageVisibilityChanged(d, visible); 3149 3150 if (!visible) { 3151 d->suspendBackingStore(); 3152 3153 // Remove this WebPage from the visible pages list. 3154 size_t foundIndex = visibleWebPages()->find(this); 3155 if (foundIndex != WTF::notFound) 3156 visibleWebPages()->remove(foundIndex); 3157 3158 // Return the backing store to the last visible WebPage. 3159 if (BackingStorePrivate::currentBackingStoreOwner() == this && !visibleWebPages()->isEmpty()) 3160 visibleWebPages()->last()->d->resumeBackingStore(); 3161 3162#if USE(ACCELERATED_COMPOSITING) 3163 // Root layer commit is not necessary for invisible tabs. 3164 // And release layer resources can reduce memory consumption. 3165 d->suspendRootLayerCommit(); 3166#endif 3167 3168 return; 3169 } 3170 3171#if USE(ACCELERATED_COMPOSITING) 3172 d->resumeRootLayerCommit(); 3173#endif 3174 3175 // We want to become visible but not get backing store ownership. 3176 if (d->m_backingStore->d->isOpenGLCompositing() && !d->m_webSettings->isBackingStoreEnabled()) { 3177 d->setCompositorDrawsRootLayer(true); 3178#if USE(ACCELERATED_COMPOSITING) 3179 d->setNeedsOneShotDrawingSynchronization(); 3180#endif 3181 d->setShouldResetTilesWhenShown(true); 3182 return; 3183 } 3184 3185 // Push this WebPage to the top of the visible pages list. 3186 if (!visibleWebPages()->isEmpty() && visibleWebPages()->last() != this) { 3187 size_t foundIndex = visibleWebPages()->find(this); 3188 if (foundIndex != WTF::notFound) 3189 visibleWebPages()->remove(foundIndex); 3190 } 3191 visibleWebPages()->append(this); 3192 3193 if (BackingStorePrivate::currentBackingStoreOwner() 3194 && BackingStorePrivate::currentBackingStoreOwner() != this) 3195 BackingStorePrivate::currentBackingStoreOwner()->d->suspendBackingStore(); 3196 3197 // resumeBackingStore will set the current owner to this webpage. 3198 // If we set the owner prematurely, then the tiles will not be reset. 3199 d->resumeBackingStore(); 3200} 3201 3202void WebPagePrivate::selectionChanged(Frame* frame) 3203{ 3204 m_inputHandler->selectionChanged(); 3205 3206 // FIXME: This is a hack! 3207 // To ensure the selection being changed has its frame 'focused', lets 3208 // set it as focused ourselves (PR #104724). 3209 m_page->focusController()->setFocusedFrame(frame); 3210} 3211 3212void WebPagePrivate::updateSelectionScrollView(const Node* node) 3213{ 3214 m_inRegionScroller->d->updateSelectionScrollView(node); 3215} 3216 3217void WebPagePrivate::updateDelegatedOverlays(bool dispatched) 3218{ 3219 // Track a dispatched message, we don't want to flood the webkit thread. 3220 // There can be as many as one more message enqued as needed but never less. 3221 if (dispatched) 3222 m_updateDelegatedOverlaysDispatched = false; 3223 else if (m_updateDelegatedOverlaysDispatched) { 3224 // Early return if there is message already pending on the webkit thread. 3225 return; 3226 } 3227 3228 if (Platform::webKitThreadMessageClient()->isCurrentThread()) { 3229 // Must be called on the WebKit thread. 3230 if (m_selectionHandler->isSelectionActive()) 3231 m_selectionHandler->selectionPositionChanged(); 3232 if (m_inspectorOverlay) 3233 m_inspectorOverlay->update(); 3234 3235 } else if (m_selectionHandler->isSelectionActive()) { 3236 // Don't bother dispatching to webkit thread if selection and tap highlight are not active. 3237 m_updateDelegatedOverlaysDispatched = true; 3238 Platform::webKitThreadMessageClient()->dispatchMessage(Platform::createMethodCallMessage(&WebPagePrivate::updateDelegatedOverlays, this, true /*dispatched*/)); 3239 } 3240} 3241 3242void WebPage::setCaretHighlightStyle(Platform::CaretHighlightStyle) 3243{ 3244} 3245 3246bool WebPage::setBatchEditingActive(bool active) 3247{ 3248 return d->m_inputHandler->setBatchEditingActive(active); 3249} 3250 3251bool WebPage::setInputSelection(unsigned start, unsigned end) 3252{ 3253 if (d->m_page->defersLoading()) 3254 return false; 3255 return d->m_inputHandler->setSelection(start, end); 3256} 3257 3258int WebPage::inputCaretPosition() const 3259{ 3260 return d->m_inputHandler->caretPosition(); 3261} 3262 3263class DeferredTaskPopupListSelectMultiple: public DeferredTask<&WebPagePrivate::m_wouldPopupListSelectMultiple> { 3264public: 3265 DeferredTaskPopupListSelectMultiple(WebPagePrivate* webPagePrivate, int size, const bool* selecteds) 3266 : DeferredTaskType(webPagePrivate) 3267 { 3268 webPagePrivate->m_cachedPopupListSelecteds.append(selecteds, size); 3269 } 3270private: 3271 virtual void performInternal(WebPagePrivate* webPagePrivate) 3272 { 3273 webPagePrivate->m_webPage->popupListClosed(webPagePrivate->m_cachedPopupListSelecteds.size(), webPagePrivate->m_cachedPopupListSelecteds.data()); 3274 } 3275}; 3276 3277class DeferredTaskPopupListSelectSingle: public DeferredTask<&WebPagePrivate::m_wouldPopupListSelectSingle> { 3278public: 3279 explicit DeferredTaskPopupListSelectSingle(WebPagePrivate* webPagePrivate, int index) 3280 : DeferredTaskType(webPagePrivate) 3281 { 3282 webPagePrivate->m_cachedPopupListSelectedIndex = index; 3283 } 3284private: 3285 virtual void performInternal(WebPagePrivate* webPagePrivate) 3286 { 3287 webPagePrivate->m_webPage->popupListClosed(webPagePrivate->m_cachedPopupListSelectedIndex); 3288 } 3289}; 3290 3291void WebPage::popupListClosed(int size, const bool* selecteds) 3292{ 3293 DeferredTaskPopupListSelectSingle::finishOrCancel(d); 3294 if (d->m_page->defersLoading()) { 3295 d->m_deferredTasks.append(adoptPtr(new DeferredTaskPopupListSelectMultiple(d, size, selecteds))); 3296 return; 3297 } 3298 DeferredTaskPopupListSelectMultiple::finishOrCancel(d); 3299 d->m_inputHandler->setPopupListIndexes(size, selecteds); 3300} 3301 3302void WebPage::popupListClosed(int index) 3303{ 3304 DeferredTaskPopupListSelectMultiple::finishOrCancel(d); 3305 if (d->m_page->defersLoading()) { 3306 d->m_deferredTasks.append(adoptPtr(new DeferredTaskPopupListSelectSingle(d, index))); 3307 return; 3308 } 3309 DeferredTaskPopupListSelectSingle::finishOrCancel(d); 3310 d->m_inputHandler->setPopupListIndex(index); 3311} 3312 3313class DeferredTaskSetDateTimeInput: public DeferredTask<&WebPagePrivate::m_wouldSetDateTimeInput> { 3314public: 3315 explicit DeferredTaskSetDateTimeInput(WebPagePrivate* webPagePrivate, BlackBerry::Platform::String value) 3316 : DeferredTaskType(webPagePrivate) 3317 { 3318 webPagePrivate->m_cachedDateTimeInput = value; 3319 } 3320private: 3321 virtual void performInternal(WebPagePrivate* webPagePrivate) 3322 { 3323 webPagePrivate->m_webPage->setDateTimeInput(webPagePrivate->m_cachedDateTimeInput); 3324 } 3325}; 3326 3327void WebPage::setDateTimeInput(const BlackBerry::Platform::String& value) 3328{ 3329 if (d->m_page->defersLoading()) { 3330 d->m_deferredTasks.append(adoptPtr(new DeferredTaskSetDateTimeInput(d, value))); 3331 return; 3332 } 3333 DeferredTaskSetDateTimeInput::finishOrCancel(d); 3334 d->m_inputHandler->setInputValue(value); 3335} 3336 3337class DeferredTaskSetColorInput: public DeferredTask<&WebPagePrivate::m_wouldSetColorInput> { 3338public: 3339 explicit DeferredTaskSetColorInput(WebPagePrivate* webPagePrivate, BlackBerry::Platform::String value) 3340 : DeferredTaskType(webPagePrivate) 3341 { 3342 webPagePrivate->m_cachedColorInput = value; 3343 } 3344private: 3345 virtual void performInternal(WebPagePrivate* webPagePrivate) 3346 { 3347 webPagePrivate->m_webPage->setColorInput(webPagePrivate->m_cachedColorInput); 3348 } 3349}; 3350 3351void WebPage::setColorInput(const BlackBerry::Platform::String& value) 3352{ 3353 if (d->m_page->defersLoading()) { 3354 d->m_deferredTasks.append(adoptPtr(new DeferredTaskSetColorInput(d, value))); 3355 return; 3356 } 3357 DeferredTaskSetColorInput::finishOrCancel(d); 3358 d->m_inputHandler->setInputValue(value); 3359} 3360 3361void WebPage::setVirtualViewportSize(const Platform::IntSize& size) 3362{ 3363 d->m_virtualViewportSize = WebCore::IntSize(size); 3364} 3365 3366void WebPage::resetVirtualViewportOnCommitted(bool reset) 3367{ 3368 d->m_resetVirtualViewportOnCommitted = reset; 3369} 3370 3371Platform::IntSize WebPagePrivate::recomputeVirtualViewportFromViewportArguments() 3372{ 3373 static const ViewportArguments defaultViewportArguments; 3374 if (m_viewportArguments == defaultViewportArguments) 3375 return IntSize(); 3376 3377 int desktopWidth = DEFAULT_MAX_LAYOUT_WIDTH; 3378 int deviceWidth = Platform::Graphics::Screen::primaryScreen()->width(); 3379 int deviceHeight = Platform::Graphics::Screen::primaryScreen()->height(); 3380 float devicePixelRatio = m_webSettings->devicePixelRatio(); 3381 ViewportAttributes result = computeViewportAttributes(m_viewportArguments, desktopWidth, deviceWidth, deviceHeight, devicePixelRatio, m_defaultLayoutSize); 3382 m_page->setDeviceScaleFactor(devicePixelRatio); 3383 3384 setUserScalable(m_webSettings->isUserScalable() && result.userScalable); 3385 if (result.initialScale > 0) 3386 setInitialScale(result.initialScale * devicePixelRatio); 3387 if (result.minimumScale > 0) 3388 setMinimumScale(result.minimumScale * devicePixelRatio); 3389 if (result.maximumScale > 0) 3390 setMaximumScale(result.maximumScale * devicePixelRatio); 3391 3392 return Platform::IntSize(result.layoutSize.width(), result.layoutSize.height()); 3393} 3394 3395#if ENABLE(EVENT_MODE_METATAGS) 3396void WebPagePrivate::didReceiveCursorEventMode(CursorEventMode mode) 3397{ 3398 if (mode != m_cursorEventMode) 3399 m_client->cursorEventModeChanged(toPlatformCursorEventMode(mode)); 3400 m_cursorEventMode = mode; 3401} 3402 3403void WebPagePrivate::didReceiveTouchEventMode(TouchEventMode mode) 3404{ 3405 if (mode != m_touchEventMode) 3406 m_client->touchEventModeChanged(toPlatformTouchEventMode(mode)); 3407 m_touchEventMode = mode; 3408} 3409#endif 3410 3411void WebPagePrivate::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) 3412{ 3413 if (arguments == m_viewportArguments) 3414 return; 3415 3416 // If the caller is trying to reset to default arguments, use the user supplied ones instead. 3417 static const ViewportArguments defaultViewportArguments; 3418 if (arguments == defaultViewportArguments) { 3419 m_viewportArguments = m_userViewportArguments; 3420 m_forceRespectViewportArguments = m_userViewportArguments != defaultViewportArguments; 3421 } else 3422 m_viewportArguments = arguments; 3423 3424 // 0 width or height in viewport arguments makes no sense, and results in a very large initial scale. 3425 // In real world, a 0 width or height is usually caused by a syntax error in "content" field of viewport 3426 // meta tag, for example, using semicolon instead of comma as separator ("width=device-width; initial-scale=1.0"). 3427 // We don't have a plan to tolerate the semicolon separator, but we can avoid applying 0 width/height. 3428 // I default it to ValueDeviceWidth rather than ValueAuto because in more cases the web site wants "device-width" 3429 // when they specify the viewport width. 3430 if (!m_viewportArguments.width) 3431 m_viewportArguments.width = ViewportArguments::ValueDeviceWidth; 3432 if (!m_viewportArguments.height) 3433 m_viewportArguments.height = ViewportArguments::ValueDeviceHeight; 3434 3435 Platform::IntSize virtualViewport = recomputeVirtualViewportFromViewportArguments(); 3436 m_webPage->setVirtualViewportSize(virtualViewport); 3437 3438 // Reset m_userPerformedManualZoom and enable m_shouldZoomToInitialScaleAfterLoadFinished so that we can relayout 3439 // the page and zoom it to fit the screen when we dynamically change the meta viewport after the load is finished. 3440 bool isLoadFinished = loadState() == Finished; 3441 if (isLoadFinished) { 3442 m_userPerformedManualZoom = false; 3443 setShouldZoomToInitialScaleAfterLoadFinished(true); 3444 } 3445 if (loadState() == Committed || isLoadFinished) 3446 zoomToInitialScaleOnLoad(); 3447} 3448 3449void WebPagePrivate::suspendBackingStore() 3450{ 3451#if USE(ACCELERATED_COMPOSITING) 3452 if (m_backingStore->d->isOpenGLCompositing()) { 3453 // A visible web page's backing store can be suspended when another web 3454 // page is taking over the backing store. 3455 if (m_visible) 3456 setCompositorDrawsRootLayer(true); 3457 3458 return; 3459 } 3460#endif 3461} 3462 3463void WebPagePrivate::resumeBackingStore() 3464{ 3465 ASSERT(m_webPage->isVisible()); 3466 3467 if (!m_backingStore->d->isActive() || shouldResetTilesWhenShown()) { 3468 BackingStorePrivate::setCurrentBackingStoreOwner(m_webPage); 3469 3470 // If we're OpenGL compositing, switching to accel comp drawing of the root layer 3471 // is a good substitute for backingstore blitting. 3472 if (m_backingStore->d->isOpenGLCompositing()) 3473 setCompositorDrawsRootLayer(!m_backingStore->d->isActive()); 3474 3475 m_backingStore->d->orientationChanged(); // Updates tile geometry and creates visible tile buffer. 3476 m_backingStore->d->resetTiles(); 3477 m_backingStore->d->updateTiles(false /* updateVisible */, false /* immediate */); 3478 3479#if USE(ACCELERATED_COMPOSITING) 3480 setNeedsOneShotDrawingSynchronization(); 3481#endif 3482 3483 m_backingStore->d->renderAndBlitVisibleContentsImmediately(); 3484 } else { 3485 if (m_backingStore->d->isOpenGLCompositing()) 3486 setCompositorDrawsRootLayer(false); 3487 3488 // Rendering was disabled while we were hidden, so we need to update all tiles. 3489 m_backingStore->d->updateTiles(true /* updateVisible */, false /* immediate */); 3490#if USE(ACCELERATED_COMPOSITING) 3491 setNeedsOneShotDrawingSynchronization(); 3492#endif 3493 } 3494 3495 setShouldResetTilesWhenShown(false); 3496} 3497 3498void WebPagePrivate::setScreenOrientation(int orientation) 3499{ 3500 FOR_EACH_PLUGINVIEW(m_pluginViews) 3501 (*it)->handleOrientationEvent(orientation); 3502 3503 m_pendingOrientation = -1; 3504 3505#if ENABLE(ORIENTATION_EVENTS) 3506 if (m_mainFrame->orientation() == orientation) 3507 return; 3508 for (RefPtr<Frame> frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) 3509 frame->sendOrientationChangeEvent(orientation); 3510#endif 3511} 3512 3513void WebPage::setScreenOrientation(int orientation) 3514{ 3515 d->m_pendingOrientation = orientation; 3516} 3517 3518void WebPage::applyPendingOrientationIfNeeded() 3519{ 3520 if (d->m_pendingOrientation != -1) 3521 d->setScreenOrientation(d->m_pendingOrientation); 3522 3523 // After rotation, we should redraw the dialog box instead of just moving it since rotation dismisses all dialogs. 3524 d->m_inputHandler->redrawSpellCheckDialogIfRequired(false /* shouldMoveDialog */); 3525} 3526 3527bool WebPagePrivate::setViewportSize(const IntSize& transformedActualVisibleSize, const IntSize& defaultLayoutSize, bool ensureFocusElementVisible) 3528{ 3529 if (m_pendingOrientation == -1 && transformedActualVisibleSize == this->transformedActualVisibleSize()) 3530 return false; 3531 3532 // Suspend all screen updates to the backingstore to make sure no-one tries to blit 3533 // while the window surface and the BackingStore are out of sync. 3534 BackingStore::ResumeUpdateOperation screenResumeOperation = BackingStore::Blit; 3535 m_backingStore->d->suspendScreenUpdates(); 3536 m_backingStore->d->suspendBackingStoreUpdates(); 3537 3538 // The screen rotation is a major state transition that in this case is not properly 3539 // communicated to the backing store, since it does early return in most methods when 3540 // not visible. 3541 if (!m_visible || !m_backingStore->d->isActive()) 3542 setShouldResetTilesWhenShown(true); 3543 3544 bool hasPendingOrientation = m_pendingOrientation != -1; 3545 3546 IntSize viewportSizeBefore = actualVisibleSize(); 3547 FloatPoint centerOfVisibleContentsRect = this->centerOfVisibleContentsRect(); 3548 bool newVisibleRectContainsOldVisibleRect = (m_actualVisibleHeight <= transformedActualVisibleSize.height()) 3549 && (m_actualVisibleWidth <= transformedActualVisibleSize.width()); 3550 3551 bool atInitialScale = m_webPage->isAtInitialZoom(); 3552 bool atTop = !scrollPosition().y(); 3553 bool atLeft = !scrollPosition().x(); 3554 3555 setDefaultLayoutSize(defaultLayoutSize); 3556 3557 // Recompute our virtual viewport. 3558 bool needsLayout = false; 3559 Platform::IntSize newVirtualViewport = recomputeVirtualViewportFromViewportArguments(); 3560 if (!newVirtualViewport.isEmpty()) { 3561 m_webPage->setVirtualViewportSize(newVirtualViewport); 3562 m_mainFrame->view()->setUseFixedLayout(useFixedLayout()); 3563 m_mainFrame->view()->setFixedLayoutSize(fixedLayoutSize()); 3564 needsLayout = true; 3565 } 3566 3567 // We switch this strictly after recomputing our virtual viewport as zoomToFitScale is dependent 3568 // upon these values and so is the virtual viewport recalculation. 3569 m_actualVisibleWidth = transformedActualVisibleSize.width(); 3570 m_actualVisibleHeight = transformedActualVisibleSize.height(); 3571 3572 IntSize viewportSizeAfter = actualVisibleSize(); 3573 3574 IntSize offset; 3575 if (hasPendingOrientation && !m_fullscreenNode) { 3576 offset = IntSize(roundf((viewportSizeBefore.width() - viewportSizeAfter.width()) / 2.0), 3577 roundf((viewportSizeBefore.height() - viewportSizeAfter.height()) / 2.0)); 3578 } 3579 3580 // As a special case, if we were anchored to the top left position at 3581 // the beginning of the rotation then preserve that anchor. 3582 if (atTop) 3583 offset.setHeight(0); 3584 if (atLeft) 3585 offset.setWidth(0); 3586 3587 // If we're about to overscroll, cap the offset to valid content. 3588 IntPoint bottomRight( 3589 scrollPosition().x() + viewportSizeAfter.width(), 3590 scrollPosition().y() + viewportSizeAfter.height()); 3591 3592 if (bottomRight.x() + offset.width() > contentsSize().width()) 3593 offset.setWidth(contentsSize().width() - bottomRight.x()); 3594 if (bottomRight.y() + offset.height() > contentsSize().height()) 3595 offset.setHeight(contentsSize().height() - bottomRight.y()); 3596 if (scrollPosition().x() + offset.width() < 0) 3597 offset.setWidth(-scrollPosition().x()); 3598 if (scrollPosition().y() + offset.height() < 0) 3599 offset.setHeight(-scrollPosition().y()); 3600 3601 // ...before scrolling, because the backing store will align its 3602 // tile matrix with the viewport as reported by the ScrollView. 3603 setScrollPosition(scrollPosition() + offset); 3604 notifyTransformedScrollChanged(); 3605 3606 m_backingStore->d->orientationChanged(); 3607 m_backingStore->d->actualVisibleSizeChanged(transformedActualVisibleSize); 3608 3609 // Update view mode only after we have updated the actual 3610 // visible size and reset the contents rect if necessary. 3611 if (setViewMode(viewMode())) 3612 needsLayout = true; 3613 3614 bool needsLayoutToFindContentSize = hasPendingOrientation; 3615 3616 // We need to update the viewport size of the WebCore::ScrollView... 3617 updateViewportSize(!needsLayoutToFindContentSize /* setFixedReportedSize */, false /* sendResizeEvent */); 3618 notifyTransformedContentsSizeChanged(); 3619 3620 // If automatic zooming is disabled, prevent zooming below. 3621 if (!m_webSettings->isZoomToFitOnLoad()) { 3622 atInitialScale = false; 3623 3624 // Normally, if the contents size is smaller than the layout width, 3625 // we would zoom in. If zoom is disabled, we need to do something else, 3626 // or there will be artifacts due to non-rendered areas outside of the 3627 // contents size. If there is a virtual viewport, we are not allowed 3628 // to modify the fixed layout size, however. 3629 if (!hasVirtualViewport() && contentsSize().width() < m_defaultLayoutSize.width()) { 3630 m_mainFrame->view()->setUseFixedLayout(useFixedLayout()); 3631 m_mainFrame->view()->setFixedLayoutSize(m_defaultLayoutSize); 3632 needsLayout = true; 3633 } 3634 } 3635 3636 // Need to resume so that the backingstore will start recording the invalidated 3637 // rects from below. 3638 m_backingStore->d->resumeBackingStoreUpdates(); 3639 3640 // We might need to layout here to get a correct contentsSize so that zoomToFit 3641 // is calculated correctly. 3642 bool stillNeedsLayout = needsLayout; 3643 while (stillNeedsLayout) { 3644 setNeedsLayout(); 3645 layoutIfNeeded(); 3646 stillNeedsLayout = false; 3647 3648 // Emulate the zoomToFitWidthOnLoad algorithm if we're rotating. 3649 ++m_nestedLayoutFinishedCount; 3650 if (needsLayoutToFindContentSize) { 3651 if (setViewMode(viewMode())) 3652 stillNeedsLayout = true; 3653 } 3654 } 3655 m_nestedLayoutFinishedCount = 0; 3656 3657 // As a special case if we were zoomed to the initial scale at the beginning 3658 // of the rotation then preserve that zoom level even when it is zoomToFit. 3659 double scale = atInitialScale ? initialScale() : currentScale(); 3660 3661 // Do our own clamping. 3662 scale = clampedScale(scale); 3663 3664 if (needsLayoutToFindContentSize) { 3665 // Set the fixed reported size here so that innerWidth|innerHeight works 3666 // with this new scale. 3667 TransformationMatrix rotationMatrix; 3668 rotationMatrix.scale(scale); 3669 IntRect viewportRect = IntRect(IntPoint::zero(), transformedActualVisibleSize); 3670 IntRect actualVisibleRect = enclosingIntRect(rotationMatrix.inverse().mapRect(FloatRect(viewportRect))); 3671 m_mainFrame->view()->setFixedReportedSize(actualVisibleRect.size()); 3672 m_mainFrame->view()->repaintFixedElementsAfterScrolling(); 3673 layoutIfNeeded(); 3674 m_mainFrame->view()->updateFixedElementsAfterScrolling(); 3675 } 3676 3677 // We're going to need to send a resize event to JavaScript because 3678 // innerWidth and innerHeight depend on fixed reported size. 3679 // This is how we support mobile pages where JavaScript resizes 3680 // the page in order to get around the fixed layout size, e.g. 3681 // google maps when it detects a mobile user agent. 3682 if (shouldSendResizeEvent()) 3683 m_mainFrame->eventHandler()->sendResizeEvent(); 3684 3685 // As a special case if we were anchored to the top left position at the beginning 3686 // of the rotation then preserve that anchor. 3687 FloatPoint anchor = centerOfVisibleContentsRect; 3688 if (atTop) 3689 anchor.setY(0); 3690 if (atLeft) 3691 anchor.setX(0); 3692 3693 // Try and zoom here with clamping on. 3694 // FIXME: Determine why the above comment says "clamping on", yet we 3695 // don't set enforceScaleClamping to true. 3696 if (zoomAboutPoint(scale, anchor, false /*enforceScaleClamping*/, true /*forceRendering*/)) { 3697 if (ensureFocusElementVisible) 3698 ensureContentVisible(!newVisibleRectContainsOldVisibleRect); 3699 } else { 3700 // Suspend all updates to the backingstore. 3701 m_backingStore->d->suspendBackingStoreUpdates(); 3702 3703 // If the zoom failed, then we should still preserve the special case of scroll position. 3704 IntPoint scrollPosition = this->scrollPosition(); 3705 if (atTop) 3706 scrollPosition.setY(0); 3707 if (atLeft) 3708 scrollPosition.setX(0); 3709 setScrollPosition(scrollPosition); 3710 3711 // These might have been altered even if we didn't zoom so notify the client. 3712 notifyTransformedContentsSizeChanged(); 3713 notifyTransformedScrollChanged(); 3714 3715 if (!needsLayout) { 3716 // The visible tiles for scroll must be up-to-date before we blit since we are not performing a layout. 3717 m_backingStore->d->updateTilesForScrollOrNotRenderedRegion(); 3718 } 3719 3720 if (ensureFocusElementVisible) 3721 ensureContentVisible(!newVisibleRectContainsOldVisibleRect); 3722 3723 if (needsLayout) { 3724 m_backingStore->d->resetTiles(); 3725 m_backingStore->d->updateTiles(false /* updateVisible */, false /* immediate */); 3726 screenResumeOperation = BackingStore::RenderAndBlit; 3727 } 3728 3729 // If we need layout then render and blit, otherwise just blit as our viewport has changed. 3730 m_backingStore->d->resumeBackingStoreUpdates(); 3731 } 3732 3733#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) 3734 // When leaving fullscreen mode, restore the scale and scroll position if needed. 3735 // We also need to make sure the scale and scroll position won't cause over scale or over scroll. 3736 if (m_scaleBeforeFullScreen > 0 && !m_fullscreenNode) { 3737 // Restore the scale when leaving fullscreen. We can't use TransformationMatrix::scale(double) here, 3738 // as it will multiply the scale rather than set the scale. 3739 // FIXME: We can refactor this into setCurrentScale(double) if it is useful in the future. 3740 if (m_orientationBeforeFullScreen % 180 != orientation() % 180) { // Orientation changed 3741 if (m_actualVisibleWidth > contentsSize().width() * m_scaleBeforeFullScreen) { 3742 // Cached scale need to be adjusted after rotation. 3743 m_scaleBeforeFullScreen = double(m_actualVisibleWidth) / contentsSize().width(); 3744 } 3745 if (m_scaleBeforeFullScreen * contentsSize().height() < m_actualVisibleHeight) { 3746 // Use zoom to fit height scale in order to cover the screen height. 3747 m_scaleBeforeFullScreen = double(m_actualVisibleHeight) / contentsSize().height(); 3748 } 3749 3750 if (m_actualVisibleWidth > m_scaleBeforeFullScreen * (contentsSize().width() - m_scrollPositionBeforeFullScreen.x())) { 3751 // Cached scroll position over scrolls horizontally after rotation. 3752 m_scrollPositionBeforeFullScreen.setX(contentsSize().width() - m_actualVisibleWidth / m_scaleBeforeFullScreen); 3753 } 3754 if (m_actualVisibleHeight > m_scaleBeforeFullScreen * (contentsSize().height() - m_scrollPositionBeforeFullScreen.y())) { 3755 // Cached scroll position over scrolls vertically after rotation. 3756 m_scrollPositionBeforeFullScreen.setY(contentsSize().height() - m_actualVisibleHeight / m_scaleBeforeFullScreen); 3757 } 3758 } 3759 3760 m_transformationMatrix->setM11(m_scaleBeforeFullScreen); 3761 m_transformationMatrix->setM22(m_scaleBeforeFullScreen); 3762 m_scaleBeforeFullScreen = -1.0; 3763 3764 setScrollPosition(m_scrollPositionBeforeFullScreen); 3765 notifyTransformChanged(); 3766 m_client->scaleChanged(); 3767 } 3768#endif 3769 3770 m_backingStore->d->resumeScreenUpdates(screenResumeOperation); 3771 m_inputHandler->redrawSpellCheckDialogIfRequired(); 3772 3773 return true; 3774} 3775 3776void WebPage::setViewportSize(const Platform::IntSize& viewportSize, const Platform::IntSize& defaultLayoutSize, bool ensureFocusElementVisible) 3777{ 3778 if (!d->setViewportSize(viewportSize, defaultLayoutSize, ensureFocusElementVisible)) { 3779 // If the viewport didn't change, try to apply only the new default layout size. 3780 setDefaultLayoutSize(defaultLayoutSize); 3781 } 3782} 3783 3784void WebPagePrivate::setDefaultLayoutSize(const IntSize& size) 3785{ 3786 IntSize screenSize = Platform::Settings::instance()->applicationSize(); 3787 ASSERT(size.width() <= screenSize.width() && size.height() <= screenSize.height()); 3788 m_defaultLayoutSize = size.expandedTo(minimumLayoutSize).shrunkTo(screenSize); 3789} 3790 3791Platform::IntSize WebPage::defaultLayoutSize() const 3792{ 3793 return d->m_defaultLayoutSize; 3794} 3795 3796void WebPage::setDefaultLayoutSize(const Platform::IntSize& platformSize) 3797{ 3798 bool needsLayout = false; 3799 WebCore::IntSize size = platformSize; 3800 if (size == d->m_defaultLayoutSize) 3801 return; 3802 3803 d->setDefaultLayoutSize(size); 3804 3805 // The default layout size affects interpretation of any viewport arguments present. 3806 Platform::IntSize virtualViewportSize = d->recomputeVirtualViewportFromViewportArguments(); 3807 if (!virtualViewportSize.isEmpty()) { 3808 setVirtualViewportSize(virtualViewportSize); 3809 needsLayout = true; 3810 } 3811 3812 if (d->setViewMode(d->viewMode())) 3813 needsLayout = true; 3814 3815 if (needsLayout) { 3816 d->setNeedsLayout(); 3817 if (!d->isLoading()) 3818 d->layoutIfNeeded(); 3819 } 3820} 3821 3822bool WebPage::mouseEvent(const Platform::MouseEvent& mouseEvent, bool* wheelDeltaAccepted) 3823{ 3824 if (!d->m_mainFrame->view()) 3825 return false; 3826 3827 if (d->m_page->defersLoading()) 3828 return false; 3829 3830 PluginView* pluginView = d->m_fullScreenPluginView.get(); 3831 if (pluginView) 3832 return d->dispatchMouseEventToFullScreenPlugin(pluginView, mouseEvent); 3833 3834 if (mouseEvent.type() == Platform::MouseEvent::MouseAborted) { 3835 d->m_mainFrame->eventHandler()->setMousePressed(false); 3836 return false; 3837 } 3838 3839 d->m_pluginMayOpenNewTab = true; 3840 3841 d->m_lastUserEventTimestamp = currentTime(); 3842 int clickCount = (d->m_selectionHandler->isSelectionActive() || mouseEvent.type() != Platform::MouseEvent::MouseMove) ? 1 : 0; 3843 3844 // Set the button type. 3845 MouseButton buttonType = NoButton; 3846 if (mouseEvent.isLeftButton()) 3847 buttonType = LeftButton; 3848 else if (mouseEvent.isRightButton()) 3849 buttonType = RightButton; 3850 else if (mouseEvent.isMiddleButton()) 3851 buttonType = MiddleButton; 3852 3853 // Create our event. 3854 PlatformMouseEvent platformMouseEvent(mouseEvent.documentViewportPosition(), mouseEvent.screenPosition(), 3855 toWebCoreMouseEventType(mouseEvent.type()), clickCount, buttonType, 3856 mouseEvent.shiftActive(), mouseEvent.ctrlActive(), mouseEvent.altActive(), PointingDevice); 3857 d->m_lastMouseEvent = platformMouseEvent; 3858 bool success = d->handleMouseEvent(platformMouseEvent); 3859 3860 if (mouseEvent.wheelTicks()) { 3861 PlatformWheelEvent wheelEvent(mouseEvent.documentViewportPosition(), mouseEvent.screenPosition(), 3862 0, -mouseEvent.wheelDelta(), 3863 0, -mouseEvent.wheelTicks(), 3864 ScrollByPixelWheelEvent, 3865 mouseEvent.shiftActive(), mouseEvent.ctrlActive(), 3866 mouseEvent.altActive(), false /* metaKey */); 3867 if (wheelDeltaAccepted) 3868 *wheelDeltaAccepted = d->handleWheelEvent(wheelEvent); 3869 } else if (wheelDeltaAccepted) 3870 *wheelDeltaAccepted = false; 3871 3872 return success; 3873} 3874 3875bool WebPagePrivate::dispatchMouseEventToFullScreenPlugin(PluginView* plugin, const Platform::MouseEvent& event) 3876{ 3877 NPEvent npEvent; 3878 NPMouseEvent mouseEvent; 3879 3880 mouseEvent.x = event.screenPosition().x(); 3881 mouseEvent.y = event.screenPosition().y(); 3882 3883 switch (event.type()) { 3884 case Platform::MouseEvent::MouseButtonDown: 3885 mouseEvent.type = MOUSE_BUTTON_DOWN; 3886 m_pluginMouseButtonPressed = true; 3887 break; 3888 case Platform::MouseEvent::MouseButtonUp: 3889 mouseEvent.type = MOUSE_BUTTON_UP; 3890 m_pluginMouseButtonPressed = false; 3891 break; 3892 case Platform::MouseEvent::MouseMove: 3893 mouseEvent.type = MOUSE_MOTION; 3894 break; 3895 default: 3896 return false; 3897 } 3898 3899 mouseEvent.flags = 0; 3900 mouseEvent.button = m_pluginMouseButtonPressed; 3901 3902 npEvent.type = NP_MouseEvent; 3903 npEvent.data = &mouseEvent; 3904 3905 return plugin->dispatchFullScreenNPEvent(npEvent); 3906} 3907 3908bool WebPagePrivate::handleMouseEvent(PlatformMouseEvent& mouseEvent) 3909{ 3910 EventHandler* eventHandler = m_mainFrame->eventHandler(); 3911 3912 if (mouseEvent.type() == WebCore::PlatformEvent::MouseMoved) 3913 return eventHandler->mouseMoved(mouseEvent); 3914 3915 if (mouseEvent.type() == WebCore::PlatformEvent::MouseScroll) 3916 return true; 3917 3918 Node* node = 0; 3919 if (mouseEvent.inputMethod() == TouchScreen) { 3920 const FatFingersResult lastFatFingersResult = m_touchEventHandler->lastFatFingersResult(); 3921 3922 // Fat fingers can deal with shadow content. 3923 node = lastFatFingersResult.node(FatFingersResult::ShadowContentNotAllowed); 3924 3925 // Save mouse event state for later. This allows us to know why some responses have occurred, namely selection changes. 3926 m_touchEventHandler->m_userTriggeredTouchPressOnTextInput = mouseEvent.type() == WebCore::PlatformEvent::MousePressed && lastFatFingersResult.isTextInput(); 3927 } 3928 3929 if (!node) { 3930 IntPoint documentContentsPoint = m_webkitThreadViewportAccessor->documentContentsFromViewport(mouseEvent.position()); 3931 HitTestResult result = eventHandler->hitTestResultAtPoint(documentContentsPoint); 3932 node = result.innerNode(); 3933 } 3934 3935 if (mouseEvent.type() == WebCore::PlatformEvent::MousePressed) { 3936 m_inputHandler->setInputModeEnabled(); 3937 if (m_inputHandler->willOpenPopupForNode(node)) { 3938 // Do not allow any human generated mouse or keyboard events to select <option>s in the list box 3939 // because we use a pop up dialog to handle the actual selections. This prevents options from 3940 // being selected prior to displaying the pop up dialog. The contents of the listbox are for 3941 // display only. 3942 3943 // We do focus <select>/<option> on mouse down so that a Focus event is fired and have the 3944 // element painted in its focus state on repaint. 3945 ASSERT_WITH_SECURITY_IMPLICATION(node->isElementNode()); 3946 if (node->isElementNode()) { 3947 Element* element = toElement(node); 3948 element->focus(); 3949 } 3950 } else 3951 eventHandler->handleMousePressEvent(mouseEvent); 3952 } else if (mouseEvent.type() == WebCore::PlatformEvent::MouseReleased) { 3953 // Do not send the mouse event if this is a popup field as the mouse down has been 3954 // suppressed and symmetry should be maintained. 3955 if (!m_inputHandler->didNodeOpenPopup(node)) 3956 eventHandler->handleMouseReleaseEvent(mouseEvent); 3957 } 3958 3959 return true; 3960} 3961 3962bool WebPagePrivate::handleWheelEvent(PlatformWheelEvent& wheelEvent) 3963{ 3964 return m_mainFrame->eventHandler()->handleWheelEvent(wheelEvent); 3965} 3966 3967bool WebPage::touchEvent(const Platform::TouchEvent& event) 3968{ 3969#if DEBUG_TOUCH_EVENTS 3970 Platform::logAlways(Platform::LogLevelCritical, "%s", event.toString().c_str()); 3971#endif 3972 3973#if ENABLE(TOUCH_EVENTS) 3974 if (!d->m_mainFrame) 3975 return false; 3976 3977 if (d->m_page->defersLoading()) 3978 return false; 3979 3980 if (d->m_inputHandler) 3981 d->m_inputHandler->setInputModeEnabled(); 3982 3983 PluginView* pluginView = d->m_fullScreenPluginView.get(); 3984 if (pluginView) 3985 return d->dispatchTouchEventToFullScreenPlugin(pluginView, event); 3986 3987 Platform::TouchEvent tEvent = event; 3988 if (event.isSingleTap()) 3989 d->m_pluginMayOpenNewTab = true; 3990 else if (tEvent.m_type == Platform::TouchEvent::TouchStart || tEvent.m_type == Platform::TouchEvent::TouchCancel) 3991 d->m_pluginMayOpenNewTab = false; 3992 3993 if (tEvent.m_type == Platform::TouchEvent::TouchStart) { 3994 d->clearCachedHitTestResult(); 3995 d->m_touchEventHandler->doFatFingers(tEvent.m_points[0]); 3996 3997 // Draw tap highlight as soon as possible if we can 3998 Element* elementUnderFatFinger = d->m_touchEventHandler->lastFatFingersResult().nodeAsElementIfApplicable(); 3999 if (elementUnderFatFinger) 4000 d->m_touchEventHandler->drawTapHighlight(); 4001 } 4002 4003 if (event.isTouchHold()) 4004 d->m_touchEventHandler->handleTouchHold(); 4005 4006 bool handled = false; 4007 4008 if (event.m_type != Platform::TouchEvent::TouchInjected) 4009 handled = d->m_mainFrame->eventHandler()->handleTouchEvent(PlatformTouchEvent(&tEvent)); 4010 4011 if (d->m_preventDefaultOnTouchStart) { 4012 if (tEvent.m_type == Platform::TouchEvent::TouchEnd || tEvent.m_type == Platform::TouchEvent::TouchCancel) 4013 d->m_preventDefaultOnTouchStart = false; 4014 return true; 4015 } 4016 4017 if (handled) { 4018 if (tEvent.m_type == Platform::TouchEvent::TouchStart) 4019 d->m_preventDefaultOnTouchStart = true; 4020 return true; 4021 } 4022#endif 4023 4024 return false; 4025} 4026 4027void WebPagePrivate::setScrollOriginPoint(const Platform::IntPoint& documentScrollOrigin) 4028{ 4029 m_inRegionScroller->d->reset(); 4030 4031 if (!m_hasInRegionScrollableAreas) 4032 return; 4033 4034 postponeDocumentStyleRecalc(); 4035 m_inRegionScroller->d->calculateInRegionScrollableAreasForPoint(documentScrollOrigin); 4036 if (!m_inRegionScroller->d->activeInRegionScrollableAreas().empty()) 4037 m_client->notifyInRegionScrollableAreasChanged(m_inRegionScroller->d->activeInRegionScrollableAreas()); 4038 resumeDocumentStyleRecalc(); 4039} 4040 4041void WebPage::setDocumentScrollOriginPoint(const Platform::IntPoint& documentScrollOrigin) 4042{ 4043 d->setScrollOriginPoint(documentScrollOrigin); 4044} 4045 4046void WebPage::touchPointAsMouseEvent(const Platform::TouchPoint& point, unsigned modifiers) 4047{ 4048 if (d->m_page->defersLoading()) 4049 return; 4050 4051 if (d->m_fullScreenPluginView.get()) 4052 return; 4053 4054 d->m_lastUserEventTimestamp = currentTime(); 4055 4056 d->m_touchEventHandler->handleTouchPoint(point, modifiers); 4057} 4058 4059void WebPage::playSoundIfAnchorIsTarget() const 4060{ 4061 d->m_touchEventHandler->playSoundIfAnchorIsTarget(); 4062} 4063 4064bool WebPagePrivate::dispatchTouchEventToFullScreenPlugin(PluginView* plugin, const Platform::TouchEvent& event) 4065{ 4066 // Always convert touch events to mouse events. 4067 // Don't send actual touch events because no one has ever implemented them in flash. 4068 if (!event.neverHadMultiTouch()) 4069 return false; 4070 4071 if (event.isDoubleTap() || event.isTouchHold() || event.m_type == Platform::TouchEvent::TouchCancel) { 4072 NPTouchEvent npTouchEvent; 4073 4074 if (event.isDoubleTap()) 4075 npTouchEvent.type = TOUCH_EVENT_DOUBLETAP; 4076 else if (event.isTouchHold()) 4077 npTouchEvent.type = TOUCH_EVENT_TOUCHHOLD; 4078 else if (event.m_type == Platform::TouchEvent::TouchCancel) 4079 npTouchEvent.type = TOUCH_EVENT_CANCEL; 4080 4081 npTouchEvent.points = 0; 4082 npTouchEvent.size = event.m_points.size(); 4083 if (npTouchEvent.size) { 4084 npTouchEvent.points = new NPTouchPoint[npTouchEvent.size]; 4085 for (int i = 0; i < npTouchEvent.size; i++) { 4086 npTouchEvent.points[i].touchId = event.m_points[i].id(); 4087 npTouchEvent.points[i].clientX = event.m_points[i].screenPosition().x(); 4088 npTouchEvent.points[i].clientY = event.m_points[i].screenPosition().y(); 4089 npTouchEvent.points[i].screenX = event.m_points[i].screenPosition().x(); 4090 npTouchEvent.points[i].screenY = event.m_points[i].screenPosition().y(); 4091 npTouchEvent.points[i].pageX = event.m_points[i].pixelViewportPosition().x(); 4092 npTouchEvent.points[i].pageY = event.m_points[i].pixelViewportPosition().y(); 4093 } 4094 } 4095 4096 NPEvent npEvent; 4097 npEvent.type = NP_TouchEvent; 4098 npEvent.data = &npTouchEvent; 4099 4100 plugin->dispatchFullScreenNPEvent(npEvent); 4101 delete[] npTouchEvent.points; 4102 return true; 4103 } 4104 4105 dispatchTouchPointAsMouseEventToFullScreenPlugin(plugin, event.m_points[0]); 4106 return true; 4107} 4108 4109bool WebPagePrivate::dispatchTouchPointAsMouseEventToFullScreenPlugin(PluginView* pluginView, const Platform::TouchPoint& point) 4110{ 4111 NPEvent npEvent; 4112 NPMouseEvent mouse; 4113 4114 switch (point.state()) { 4115 case Platform::TouchPoint::TouchPressed: 4116 mouse.type = MOUSE_BUTTON_DOWN; 4117 break; 4118 case Platform::TouchPoint::TouchReleased: 4119 mouse.type = MOUSE_BUTTON_UP; 4120 break; 4121 case Platform::TouchPoint::TouchMoved: 4122 mouse.type = MOUSE_MOTION; 4123 break; 4124 case Platform::TouchPoint::TouchStationary: 4125 return true; 4126 } 4127 4128 mouse.x = point.screenPosition().x(); 4129 mouse.y = point.screenPosition().y(); 4130 mouse.button = mouse.type != MOUSE_BUTTON_UP; 4131 mouse.flags = 0; 4132 npEvent.type = NP_MouseEvent; 4133 npEvent.data = &mouse; 4134 4135 pluginView->dispatchFullScreenNPEvent(npEvent); 4136 return true; 4137} 4138 4139void WebPage::touchEventCancel() 4140{ 4141 d->m_pluginMayOpenNewTab = false; 4142 if (d->m_page->defersLoading()) 4143 return; 4144} 4145 4146Frame* WebPagePrivate::focusedOrMainFrame() const 4147{ 4148 return m_page->focusController()->focusedOrMainFrame(); 4149} 4150 4151void WebPagePrivate::clearFocusNode() 4152{ 4153 Frame* frame = focusedOrMainFrame(); 4154 if (!frame) 4155 return; 4156 ASSERT(frame->document()); 4157 4158 if (frame->document()->focusedElement()) 4159 frame->page()->focusController()->setFocusedElement(0, frame); 4160} 4161 4162BlackBerry::Platform::String WebPage::textEncoding() 4163{ 4164 Frame* frame = d->focusedOrMainFrame(); 4165 if (!frame) 4166 return BlackBerry::Platform::String::emptyString(); 4167 4168 Document* document = frame->document(); 4169 if (!document) 4170 return BlackBerry::Platform::String::emptyString(); 4171 4172 return document->loader()->writer()->encoding(); 4173} 4174 4175BlackBerry::Platform::String WebPage::forcedTextEncoding() 4176{ 4177 Frame* frame = d->focusedOrMainFrame(); 4178 if (!frame) 4179 return BlackBerry::Platform::String::emptyString(); 4180 4181 Document* document = frame->document(); 4182 if (!document) 4183 return BlackBerry::Platform::String::emptyString(); 4184 4185 return document->loader()->overrideEncoding(); 4186} 4187 4188void WebPage::setForcedTextEncoding(const BlackBerry::Platform::String& encoding) 4189{ 4190 if (!encoding.empty() && d->focusedOrMainFrame() && d->focusedOrMainFrame()->loader() && d->focusedOrMainFrame()->loader()) 4191 d->focusedOrMainFrame()->loader()->reloadWithOverrideEncoding(encoding); 4192} 4193 4194bool WebPage::keyEvent(const Platform::KeyboardEvent& keyboardEvent) 4195{ 4196 if (!d->m_mainFrame->view()) 4197 return false; 4198 4199 if (d->m_page->defersLoading()) 4200 return false; 4201 4202 ASSERT(d->m_page->focusController()); 4203 4204 return d->m_inputHandler->handleKeyboardInput(keyboardEvent); 4205} 4206 4207bool WebPage::deleteTextRelativeToCursor(unsigned leftOffset, unsigned rightOffset) 4208{ 4209 if (d->m_page->defersLoading()) 4210 return false; 4211 4212 return d->m_inputHandler->deleteTextRelativeToCursor(leftOffset, rightOffset); 4213} 4214 4215spannable_string_t* WebPage::selectedText(int32_t flags) 4216{ 4217 return d->m_inputHandler->selectedText(flags); 4218} 4219 4220spannable_string_t* WebPage::textBeforeCursor(int32_t length, int32_t flags) 4221{ 4222 return d->m_inputHandler->textBeforeCursor(length, flags); 4223} 4224 4225spannable_string_t* WebPage::textAfterCursor(int32_t length, int32_t flags) 4226{ 4227 return d->m_inputHandler->textAfterCursor(length, flags); 4228} 4229 4230extracted_text_t* WebPage::extractedTextRequest(extracted_text_request_t* request, int32_t flags) 4231{ 4232 return d->m_inputHandler->extractedTextRequest(request, flags); 4233} 4234 4235int32_t WebPage::setComposingRegion(int32_t start, int32_t end) 4236{ 4237 return d->m_inputHandler->setComposingRegion(start, end); 4238} 4239 4240int32_t WebPage::finishComposition() 4241{ 4242 return d->m_inputHandler->finishComposition(); 4243} 4244 4245int32_t WebPage::setComposingText(spannable_string_t* spannableString, int32_t relativeCursorPosition) 4246{ 4247 if (d->m_page->defersLoading()) 4248 return -1; 4249 return d->m_inputHandler->setComposingText(spannableString, relativeCursorPosition); 4250} 4251 4252int32_t WebPage::commitText(spannable_string_t* spannableString, int32_t relativeCursorPosition) 4253{ 4254 if (d->m_page->defersLoading()) 4255 return -1; 4256 return d->m_inputHandler->commitText(spannableString, relativeCursorPosition); 4257} 4258 4259void WebPage::setSpellCheckingEnabled(bool enabled) 4260{ 4261 static_cast<EditorClientBlackBerry*>(d->m_page->editorClient())->enableSpellChecking(enabled); 4262 4263 d->m_inputHandler->setSystemSpellCheckStatus(enabled); 4264 4265 if (!enabled) 4266 d->m_inputHandler->stopPendingSpellCheckRequests(); 4267} 4268 4269void WebPage::spellCheckingRequestProcessed(int32_t transactionId, spannable_string_t* spannableString) 4270{ 4271 d->m_inputHandler->spellCheckingRequestProcessed(transactionId, spannableString); 4272} 4273 4274class DeferredTaskSelectionCancelled: public DeferredTask<&WebPagePrivate::m_wouldCancelSelection> { 4275public: 4276 explicit DeferredTaskSelectionCancelled(WebPagePrivate* webPagePrivate) 4277 : DeferredTaskType(webPagePrivate) 4278 { 4279 } 4280private: 4281 virtual void performInternal(WebPagePrivate* webPagePrivate) 4282 { 4283 webPagePrivate->m_webPage->selectionCancelled(); 4284 } 4285}; 4286 4287void WebPage::selectionCancelled() 4288{ 4289 if (d->m_page->defersLoading()) { 4290 d->m_deferredTasks.append(adoptPtr(new DeferredTaskSelectionCancelled(d))); 4291 return; 4292 } 4293 DeferredTaskSelectionCancelled::finishOrCancel(d); 4294 d->m_selectionHandler->cancelSelection(); 4295} 4296 4297bool WebPage::selectionContainsDocumentPoint(const Platform::IntPoint& point) 4298{ 4299 return d->m_selectionHandler->selectionContains(point); 4300} 4301 4302BlackBerry::Platform::String WebPage::title() const 4303{ 4304 if (d->m_mainFrame->document()) 4305 return d->m_mainFrame->loader()->documentLoader()->title().string(); 4306 return BlackBerry::Platform::String::emptyString(); 4307} 4308 4309BlackBerry::Platform::String WebPage::selectedText() const 4310{ 4311 return d->m_selectionHandler->selectedText(); 4312} 4313 4314BlackBerry::Platform::String WebPage::cutSelectedText() 4315{ 4316 BlackBerry::Platform::String selectedText = d->m_selectionHandler->selectedText(); 4317 if (!d->m_page->defersLoading() && !selectedText.empty()) 4318 d->m_inputHandler->deleteSelection(); 4319 return selectedText; 4320} 4321 4322void WebPage::insertText(const BlackBerry::Platform::String& string) 4323{ 4324 if (d->m_page->defersLoading()) 4325 return; 4326 d->m_inputHandler->insertText(string); 4327} 4328 4329void WebPage::clearCurrentInputField() 4330{ 4331 if (d->m_page->defersLoading()) 4332 return; 4333 d->m_inputHandler->clearField(); 4334} 4335 4336void WebPage::cut() 4337{ 4338 if (d->m_page->defersLoading()) 4339 return; 4340 d->m_inputHandler->cut(); 4341} 4342 4343void WebPage::copy() 4344{ 4345 d->m_inputHandler->copy(); 4346} 4347 4348void WebPage::paste() 4349{ 4350 if (d->m_page->defersLoading()) 4351 return; 4352 d->m_inputHandler->paste(); 4353} 4354 4355void WebPage::selectAll() 4356{ 4357 if (d->m_page->defersLoading()) 4358 return; 4359 d->m_inputHandler->selectAll(); 4360} 4361 4362bool WebPage::isInputMode() const 4363{ 4364 return d->m_inputHandler->isInputMode(); 4365} 4366 4367void WebPage::setDocumentSelection(const Platform::IntPoint& documentStartPoint, const Platform::IntPoint& documentEndPoint) 4368{ 4369 if (d->m_page->defersLoading()) 4370 return; 4371 4372 d->m_selectionHandler->setSelection(documentStartPoint, documentEndPoint); 4373} 4374 4375void WebPage::setDocumentCaretPosition(const Platform::IntPoint& documentCaretPosition) 4376{ 4377 if (d->m_page->defersLoading()) 4378 return; 4379 4380 // Handled by selection handler as it's point based. 4381 d->m_selectionHandler->setCaretPosition(documentCaretPosition); 4382} 4383 4384void WebPage::selectAtDocumentPoint(const Platform::IntPoint& documentPoint, SelectionExpansionType selectionExpansionType) 4385{ 4386 if (d->m_page->defersLoading()) 4387 return; 4388 d->m_selectionHandler->selectAtPoint(documentPoint, selectionExpansionType); 4389} 4390 4391void WebPage::expandSelection(bool isScrollStarted) 4392{ 4393 if (d->m_page->defersLoading()) 4394 return; 4395 d->m_selectionHandler->expandSelection(isScrollStarted); 4396} 4397 4398void WebPage::setOverlayExpansionPixelHeight(int dy) 4399{ 4400 d->setOverlayExpansionPixelHeight(dy); 4401} 4402 4403void WebPagePrivate::setOverlayExpansionPixelHeight(int dy) 4404{ 4405 // Transform from pixel to document coordinates. 4406 m_selectionHandler->setOverlayExpansionHeight(m_webkitThreadViewportAccessor->roundToDocumentFromPixelContents(Platform::IntPoint(0, dy)).y()); 4407} 4408 4409void WebPage::setParagraphExpansionPixelScrollMargin(const Platform::IntSize& scrollMargin) 4410{ 4411 // Transform from pixel to document coordinates. 4412 Platform::IntSize documentScrollMargin = d->m_webkitThreadViewportAccessor->roundToDocumentFromPixelContents(Platform::IntRect(Platform::IntPoint(), scrollMargin)).size(); 4413 d->m_selectionHandler->setParagraphExpansionScrollMargin(documentScrollMargin); 4414} 4415 4416void WebPage::setSelectionDocumentViewportSize(const Platform::IntSize& selectionDocumentViewportSize) 4417{ 4418 d->m_selectionHandler->setSelectionViewportSize(selectionDocumentViewportSize); 4419} 4420 4421BackingStore* WebPage::backingStore() const 4422{ 4423 return d->m_backingStore; 4424} 4425 4426InRegionScroller* WebPage::inRegionScroller() const 4427{ 4428 return d->m_inRegionScroller.get(); 4429} 4430 4431void WebPagePrivate::setTextReflowAnchorPoint(const Platform::IntPoint& documentFocalPoint) 4432{ 4433 // Should only be invoked when text reflow is enabled. 4434 ASSERT(m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled); 4435 4436 m_currentPinchZoomNode = bestNodeForZoomUnderPoint(documentFocalPoint); 4437 if (!m_currentPinchZoomNode) 4438 return; 4439 4440 IntRect nodeRect = rectForNode(m_currentPinchZoomNode.get()); 4441 m_anchorInNodeRectRatio.set( 4442 static_cast<float>(documentFocalPoint.x() - nodeRect.x()) / nodeRect.width(), 4443 static_cast<float>(documentFocalPoint.y() - nodeRect.y()) / nodeRect.height()); 4444} 4445 4446bool WebPage::pinchZoomAboutPoint(double scale, const Platform::FloatPoint& documentFocalPoint) 4447{ 4448 d->m_userPerformedManualZoom = true; 4449 d->m_userPerformedManualScroll = true; 4450 4451 if (d->m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled) { 4452 d->setTextReflowAnchorPoint(webkitThreadViewportAccessor()->roundedDocumentContents(documentFocalPoint)); 4453 4454 // Theoretically, d->nodeForZoomUnderPoint(documentFocalPoint) can return null. 4455 if (!d->m_currentPinchZoomNode) 4456 return false; 4457 } 4458 4459 return d->zoomAboutPoint(scale, documentFocalPoint); 4460} 4461 4462#if ENABLE(VIEWPORT_REFLOW) 4463void WebPagePrivate::toggleTextReflowIfEnabledForBlockZoomOnly(bool shouldEnableTextReflow) 4464{ 4465 if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabledOnlyForBlockZoom) 4466 m_page->settings()->setTextReflowEnabled(shouldEnableTextReflow); 4467} 4468#endif 4469 4470bool WebPage::blockZoom(const Platform::IntPoint& documentTargetPoint) 4471{ 4472 if (!d->m_mainFrame->view() || !d->isUserScalable()) 4473 return false; 4474 4475 Node* node = d->bestNodeForZoomUnderPoint(documentTargetPoint); 4476 if (!node) 4477 return false; 4478 4479 IntRect nodeRect = d->rectForNode(node); 4480 IntRect blockRect; 4481 bool endOfBlockZoomMode = d->compareNodesForBlockZoom(d->m_currentBlockZoomAdjustedNode.get(), node); 4482 const double oldScale = d->m_transformationMatrix->m11(); 4483 double newScale = 0; 4484 const double margin = endOfBlockZoomMode ? 0 : blockZoomMargin * 2 * oldScale; 4485 bool isFirstZoom = false; 4486 4487 const Platform::ViewportAccessor* viewportAccessor = webkitThreadViewportAccessor(); 4488 4489 if (endOfBlockZoomMode) { 4490 // End of block zoom mode 4491 const Platform::IntSize pixelContentsSize = viewportAccessor->pixelContentsSize(); 4492 const IntRect rect = d->blockZoomRectForNode(node); 4493 blockRect = IntRect(0, rect.y(), pixelContentsSize.width(), pixelContentsSize.height() - rect.y()); 4494 d->m_shouldReflowBlock = false; 4495 } else { 4496 // Start/continue block zoom mode 4497 Node* tempBlockZoomAdjustedNode = d->m_currentBlockZoomAdjustedNode.get(); 4498 blockRect = d->blockZoomRectForNode(node); 4499 4500 // Don't use a block if it is too close to the size of the actual contents. 4501 // We allow this for images only so that they can be zoomed tight to the screen. 4502 if (!node->hasTagName(HTMLNames::imgTag)) { 4503 const IntRect tRect = viewportAccessor->roundToDocumentFromPixelContents(WebCore::FloatRect(blockRect)); 4504 int blockArea = tRect.width() * tRect.height(); 4505 int pageArea = d->contentsSize().width() * d->contentsSize().height(); 4506 double blockToPageRatio = static_cast<double>(pageArea - blockArea) / pageArea; 4507 if (blockToPageRatio < minimumExpandingRatio) { 4508 // Restore old adjust node because zoom was canceled. 4509 d->m_currentBlockZoomAdjustedNode = tempBlockZoomAdjustedNode; 4510 return false; 4511 } 4512 } 4513 4514 if (blockRect.isEmpty() || !blockRect.width() || !blockRect.height()) 4515 return false; 4516 4517 if (!d->m_currentBlockZoomNode.get()) 4518 isFirstZoom = true; 4519 4520 d->m_currentBlockZoomNode = node; 4521 d->m_shouldReflowBlock = true; 4522 } 4523 4524 newScale = std::min(d->newScaleForBlockZoomRect(blockRect, oldScale, margin), d->maxBlockZoomScale()); 4525 newScale = std::max(newScale, minimumScale()); 4526 4527#if ENABLE(VIEWPORT_REFLOW) 4528 // If reflowing, adjust the reflow-width of text node to make sure the font is a reasonable size. 4529 if (d->m_currentBlockZoomNode && d->m_shouldReflowBlock && settings()->textReflowMode() != WebSettings::TextReflowDisabled) { 4530 RenderObject* renderer = d->m_currentBlockZoomNode->renderer(); 4531 if (renderer && renderer->isText()) { 4532 double newFontSize = renderer->style()->fontSize() * newScale; 4533 if (newFontSize < d->m_webSettings->defaultFontSize()) { 4534 newScale = std::min(static_cast<double>(d->m_webSettings->defaultFontSize()) / renderer->style()->fontSize(), d->maxBlockZoomScale()); 4535 newScale = std::max(newScale, minimumScale()); 4536 } 4537 blockRect.setWidth(oldScale * static_cast<double>(d->transformedActualVisibleSize().width()) / newScale); 4538 // Re-calculate the scale here to take in to account the margin. 4539 newScale = std::min(d->newScaleForBlockZoomRect(blockRect, oldScale, margin), d->maxBlockZoomScale()); 4540 newScale = std::max(newScale, minimumScale()); // Still, it's not allowed to be smaller than minimum scale. 4541 } 4542 } 4543#endif 4544 4545 // Align the zoomed block in the screen. 4546 const Platform::FloatRect newBlockRect = viewportAccessor->documentFromPixelContents(WebCore::FloatRect(blockRect)); 4547 float scaledViewportWidth = static_cast<double>(d->actualVisibleSize().width()) * oldScale / newScale; 4548 float scaledViewportHeight = static_cast<double>(d->actualVisibleSize().height()) * oldScale / newScale; 4549 float dx = std::max(0.0f, (scaledViewportWidth - newBlockRect.width()) / 2.0f); 4550 float dy = std::max(0.0f, (scaledViewportHeight - newBlockRect.height()) / 2.0f); 4551 4552 const RenderObject* renderer = d->m_currentBlockZoomAdjustedNode->renderer(); 4553 const FloatPoint topLeftPoint = newBlockRect.location(); 4554 FloatPoint anchor; 4555 4556 if (renderer && renderer->isText()) { 4557 ETextAlign textAlign = renderer->style()->textAlign(); 4558 switch (textAlign) { 4559 case CENTER: 4560 case WEBKIT_CENTER: 4561 anchor = FloatPoint(nodeRect.x() + (nodeRect.width() - scaledViewportWidth) / 2, topLeftPoint.y()); 4562 break; 4563 case LEFT: 4564 case WEBKIT_LEFT: 4565 anchor = topLeftPoint; 4566 break; 4567 case RIGHT: 4568 case WEBKIT_RIGHT: 4569 anchor = FloatPoint(nodeRect.x() + nodeRect.width() - scaledViewportWidth, topLeftPoint.y()); 4570 break; 4571 case TASTART: 4572 case JUSTIFY: 4573 default: 4574 if (renderer->style()->isLeftToRightDirection()) 4575 anchor = topLeftPoint; 4576 else 4577 anchor = FloatPoint(nodeRect.x() + nodeRect.width() - scaledViewportWidth, topLeftPoint.y()); 4578 break; 4579 } 4580 } else 4581 anchor = renderer->style()->isLeftToRightDirection() ? topLeftPoint : FloatPoint(nodeRect.x() + nodeRect.width() - scaledViewportWidth, topLeftPoint.y()); 4582 4583 WebCore::FloatPoint finalAnimationDocumentScrollPosition; 4584 4585 if (newBlockRect.height() <= scaledViewportHeight) { 4586 // The block fits in the viewport so center it. 4587 finalAnimationDocumentScrollPosition = FloatPoint(anchor.x() - dx, anchor.y() - dy); 4588 } else { 4589 // The block is longer than the viewport so top align it and add 3 pixel margin. 4590 finalAnimationDocumentScrollPosition = FloatPoint(anchor.x() - dx, anchor.y() - 3); 4591 } 4592 4593#if ENABLE(VIEWPORT_REFLOW) 4594 // We don't know how long the reflowed block will be so we position it at the top of the screen with a small margin. 4595 if (settings()->textReflowMode() != WebSettings::TextReflowDisabled) { 4596 finalAnimationDocumentScrollPosition = FloatPoint(anchor.x() - dx, anchor.y() - 3); 4597 d->m_finalAnimationDocumentScrollPositionReflowOffset = FloatPoint(-dx, -3); 4598 } 4599#endif 4600 4601 // Make sure that the original node rect is visible in the screen after the zoom. This is necessary because the identified block rect might 4602 // not be the same as the original node rect, and it could force the original node rect off the screen. 4603 FloatRect br(anchor, FloatSize(scaledViewportWidth, scaledViewportHeight)); 4604 if (!br.contains(IntPoint(documentTargetPoint))) { 4605 d->m_finalAnimationDocumentScrollPositionReflowOffset.move(0, (documentTargetPoint.y() - scaledViewportHeight / 2) - finalAnimationDocumentScrollPosition.y()); 4606 finalAnimationDocumentScrollPosition = FloatPoint(finalAnimationDocumentScrollPosition.x(), documentTargetPoint.y() - scaledViewportHeight / 2); 4607 } 4608 4609 // Clamp the finalBlockPoint to not cause any overflow scrolling. 4610 if (finalAnimationDocumentScrollPosition.x() < 0) { 4611 finalAnimationDocumentScrollPosition.setX(0); 4612 d->m_finalAnimationDocumentScrollPositionReflowOffset.setX(0); 4613 } else if (finalAnimationDocumentScrollPosition.x() + scaledViewportWidth > d->contentsSize().width()) { 4614 finalAnimationDocumentScrollPosition.setX(d->contentsSize().width() - scaledViewportWidth); 4615 d->m_finalAnimationDocumentScrollPositionReflowOffset.setX(0); 4616 } 4617 4618 if (finalAnimationDocumentScrollPosition.y() < 0) { 4619 finalAnimationDocumentScrollPosition.setY(0); 4620 d->m_finalAnimationDocumentScrollPositionReflowOffset.setY(0); 4621 } else if (finalAnimationDocumentScrollPosition.y() + scaledViewportHeight > d->contentsSize().height()) { 4622 finalAnimationDocumentScrollPosition.setY(d->contentsSize().height() - scaledViewportHeight); 4623 d->m_finalAnimationDocumentScrollPositionReflowOffset.setY(0); 4624 } 4625 4626 // Don't block zoom if the user is zooming and the new scale is only marginally different from the 4627 // oldScale with only a marginal change in scroll position. Ignore scroll difference in the special case 4628 // that the zoom level is the minimumScale. 4629 if (!endOfBlockZoomMode && abs(newScale - oldScale) / oldScale < minimumExpandingRatio) { 4630 const double minimumDisplacement = minimumExpandingRatio * viewportAccessor->documentViewportSize().width(); 4631 const int scrollPositionDisplacement = distanceBetweenPoints(viewportAccessor->documentScrollPosition(), viewportAccessor->roundedDocumentContents(finalAnimationDocumentScrollPosition)); 4632 4633 if (oldScale == d->minimumScale() || (scrollPositionDisplacement < minimumDisplacement && abs(newScale - oldScale) / oldScale < 0.10)) { 4634 if (isFirstZoom) { 4635 d->resetBlockZoom(); 4636 return false; 4637 } 4638 // Zoom out of block zoom. 4639 blockZoom(documentTargetPoint); 4640 return true; 4641 } 4642 } 4643 4644 // We set this here to make sure we don't try to re-render the page at a different zoom level during loading. 4645 d->m_userPerformedManualZoom = true; 4646 d->m_userPerformedManualScroll = true; 4647 d->m_client->animateToScaleAndDocumentScrollPosition(newScale, finalAnimationDocumentScrollPosition, true); 4648 4649 return true; 4650} 4651 4652bool WebPage::isMaxZoomed() const 4653{ 4654 return (d->currentScale() == d->maximumScale()) || !d->isUserScalable(); 4655} 4656 4657bool WebPage::isMinZoomed() const 4658{ 4659 return (d->currentScale() == d->minimumScale()) || !d->isUserScalable(); 4660} 4661 4662bool WebPage::isAtInitialZoom() const 4663{ 4664 return (d->currentScale() == d->initialScale()) || !d->isUserScalable(); 4665} 4666 4667class DeferredTaskSetFocused: public DeferredTask<&WebPagePrivate::m_wouldSetFocused> { 4668public: 4669 explicit DeferredTaskSetFocused(WebPagePrivate* webPagePrivate, bool focused) 4670 : DeferredTaskType(webPagePrivate) 4671 { 4672 webPagePrivate->m_cachedFocused = focused; 4673 } 4674private: 4675 virtual void performInternal(WebPagePrivate* webPagePrivate) 4676 { 4677 webPagePrivate->m_webPage->setFocused(webPagePrivate->m_cachedFocused); 4678 } 4679}; 4680 4681void WebPage::setFocused(bool focused) 4682{ 4683 if (d->m_page->defersLoading()) { 4684 d->m_deferredTasks.append(adoptPtr(new DeferredTaskSetFocused(d, focused))); 4685 return; 4686 } 4687 DeferredTaskSetFocused::finishOrCancel(d); 4688 FocusController* focusController = d->m_page->focusController(); 4689 focusController->setActive(focused); 4690 if (focused) { 4691 Frame* frame = focusController->focusedFrame(); 4692 if (!frame) 4693 focusController->setFocusedFrame(d->m_mainFrame); 4694 } 4695 focusController->setFocused(focused); 4696} 4697 4698bool WebPage::findNextString(const char* text, bool forward, bool caseSensitive, bool wrap, bool highlightAllMatches, bool selectActiveMatchOnClear) 4699{ 4700 WebCore::FindOptions findOptions = WebCore::StartInSelection; 4701 if (!forward) 4702 findOptions |= WebCore::Backwards; 4703 if (!caseSensitive) 4704 findOptions |= WebCore::CaseInsensitive; 4705 4706 // The WebCore::FindOptions::WrapAround boolean actually wraps the search 4707 // within the current frame as opposed to the entire Document, so we have to 4708 // provide our own wrapping code to wrap at the whole Document level. 4709 return d->m_inPageSearchManager->findNextString(String::fromUTF8(text), findOptions, wrap, highlightAllMatches, selectActiveMatchOnClear); 4710} 4711 4712void WebPage::runLayoutTests() 4713{ 4714#if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD 4715 // FIXME: do we need API to toggle this? 4716 d->m_page->settings()->setDeveloperExtrasEnabled(true); 4717 4718 if (!d->m_dumpRenderTree) 4719 d->m_dumpRenderTree = new DumpRenderTree(this); 4720 d->m_dumpRenderTree->runTests(); 4721#endif 4722} 4723 4724unsigned WebPage::timeoutForJavaScriptExecution() const 4725{ 4726 return Settings::timeoutForJavaScriptExecution(d->m_page->groupName()); 4727} 4728 4729void WebPage::setTimeoutForJavaScriptExecution(unsigned ms) 4730{ 4731 Settings::setTimeoutForJavaScriptExecution(d->m_page->groupName(), ms); 4732} 4733 4734JSGlobalContextRef WebPage::globalContext() const 4735{ 4736 if (!d->m_mainFrame) 4737 return 0; 4738 4739 return toGlobalRef(d->m_mainFrame->script()->globalObject(mainThreadNormalWorld())->globalExec()); 4740} 4741 4742// Serialize only the members of HistoryItem which are needed by the client, 4743// and copy them into a SharedArray. Also include the HistoryItem pointer which 4744// will be used by the client as an opaque reference to identify the item. 4745void WebPage::getBackForwardList(SharedArray<BackForwardEntry>& result) const 4746{ 4747 HistoryItemVector entries = static_cast<BackForwardListBlackBerry*>(d->m_page->backForward()->client())->entries(); 4748 result.reset(new BackForwardEntry[entries.size()], entries.size()); 4749 4750 for (unsigned i = 0; i < entries.size(); ++i) { 4751 RefPtr<HistoryItem> entry = entries[i]; 4752 BackForwardEntry& resultEntry = result[i]; 4753 resultEntry.url = entry->urlString(); 4754 resultEntry.originalUrl = entry->originalURLString(); 4755 resultEntry.title = entry->title(); 4756 resultEntry.networkToken = entry->viewState().networkToken; 4757 resultEntry.lastVisitWasHTTPNonGet = entry->lastVisitWasHTTPNonGet(); 4758 resultEntry.id = backForwardIdFromHistoryItem(entry.get()); 4759 4760 // FIXME: seems we can remove this now? 4761 // Make sure the HistoryItem is not disposed while the result list is still being used, to make sure the pointer is not reused 4762 // will be balanced by deref in releaseBackForwardEntry. 4763 entry->ref(); 4764 } 4765} 4766 4767void WebPage::releaseBackForwardEntry(BackForwardId id) const 4768{ 4769 HistoryItem* item = historyItemFromBackForwardId(id); 4770 ASSERT(item); 4771 item->deref(); 4772} 4773 4774void WebPage::clearBrowsingData() 4775{ 4776 clearMemoryCaches(); 4777 clearAppCache(d->m_page->groupName()); 4778 clearLocalStorage(); 4779 clearCookieCache(); 4780 clearHistory(); 4781 clearPluginSiteData(); 4782 clearWebFileSystem(); 4783} 4784 4785void WebPage::clearHistory() 4786{ 4787 // Don't clear the back-forward list as we might like to keep it. 4788 PageGroup::removeAllVisitedLinks(); 4789} 4790 4791void WebPage::clearCookies() 4792{ 4793 clearCookieCache(); 4794} 4795 4796void WebPage::clearLocalStorage() 4797{ 4798 if (PageGroup* group = d->m_page->groupPtr()) { 4799 if (StorageNamespace* storage = group->localStorage()) 4800 storage->clearAllOriginsForDeletion(); 4801 } 4802} 4803 4804void WebPage::clearCredentials() 4805{ 4806#if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST) 4807 if (d->m_webSettings->isCredentialAutofillEnabled()) 4808 credentialManager().clearCredentials(); 4809#endif 4810} 4811 4812void WebPage::clearAutofillData() 4813{ 4814 if (d->m_webSettings->isFormAutofillEnabled()) 4815 AutofillManager::clear(); 4816} 4817 4818void WebPage::clearNeverRememberSites() 4819{ 4820#if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST) 4821 if (d->m_webSettings->isCredentialAutofillEnabled()) 4822 credentialManager().clearNeverRememberSites(); 4823#endif 4824} 4825 4826void WebPage::clearWebFileSystem() 4827{ 4828#if ENABLE(FILE_SYSTEM) 4829 Platform::WebFileSystem::deleteAllFileSystems(); 4830#endif 4831} 4832 4833void WebPage::clearCache() 4834{ 4835 clearMemoryCaches(); 4836 clearAppCache(d->m_page->groupName()); 4837} 4838 4839void WebPage::clearBackForwardList(bool keepCurrentPage) const 4840{ 4841 BackForwardListBlackBerry* backForwardList = static_cast<BackForwardListBlackBerry*>(d->m_page->backForward()->client()); 4842 RefPtr<HistoryItem> currentItem = backForwardList->currentItem(); 4843 backForwardList->clear(); 4844 if (keepCurrentPage) 4845 d->m_page->backForward()->client()->addItem(currentItem); 4846} 4847 4848bool WebPage::isEnableLocalAccessToAllCookies() const 4849{ 4850 return cookieManager().canLocalAccessAllCookies(); 4851} 4852 4853void WebPage::setEnableLocalAccessToAllCookies(bool enabled) 4854{ 4855 cookieManager().setCanLocalAccessAllCookies(enabled); 4856} 4857 4858void WebPage::addVisitedLink(const unsigned short* url, unsigned length) 4859{ 4860 ASSERT(d->m_page); 4861 d->m_page->group().addVisitedLink(url, length); 4862} 4863 4864void WebPage::initPopupWebView(BlackBerry::WebKit::WebPage* webPage) 4865{ 4866 d->m_pagePopup->initialize(webPage); 4867} 4868 4869String WebPagePrivate::findPatternStringForUrl(const KURL& url) const 4870{ 4871 if ((m_webSettings->shouldHandlePatternUrls() && protocolIs(url, "pattern")) 4872 || protocolIs(url, "tel") 4873 || protocolIs(url, "wtai") 4874 || protocolIs(url, "cti") 4875 || protocolIs(url, "mailto") 4876 || protocolIs(url, "sms") 4877 || protocolIs(url, "pin")) { 4878 return url; 4879 } 4880 return String(); 4881} 4882 4883bool WebPage::defersLoading() const 4884{ 4885 return d->m_page->defersLoading(); 4886} 4887 4888void WebPage::notifyPagePause() 4889{ 4890 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4891 (*it)->handlePauseEvent(); 4892} 4893 4894void WebPage::notifyPageResume() 4895{ 4896 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4897 (*it)->handleResumeEvent(); 4898} 4899 4900void WebPage::notifyPageBackground() 4901{ 4902 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4903 (*it)->handleBackgroundEvent(); 4904} 4905 4906void WebPage::notifyPageForeground() 4907{ 4908 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4909 (*it)->handleForegroundEvent(); 4910} 4911 4912void WebPage::notifyPageFullScreenAllowed() 4913{ 4914 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4915 (*it)->handleFullScreenAllowedEvent(); 4916} 4917 4918void WebPage::notifyPageFullScreenExit() 4919{ 4920 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4921 (*it)->handleFullScreenExitEvent(); 4922} 4923 4924void WebPage::notifyDeviceIdleStateChange(bool enterIdle) 4925{ 4926 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4927 (*it)->handleIdleEvent(enterIdle); 4928} 4929 4930void WebPagePrivate::notifyAppActivationStateChange(ActivationStateType activationState) 4931{ 4932 m_activationState = activationState; 4933 4934#if USE(ACCELERATED_COMPOSITING) 4935 if (activationState == ActivationActive) 4936 resumeRootLayerCommit(); 4937 else 4938 suspendRootLayerCommit(); 4939#endif 4940 4941#if ENABLE(PAGE_VISIBILITY_API) 4942 setPageVisibilityState(); 4943#endif 4944} 4945 4946void WebPage::notifyAppActivationStateChange(ActivationStateType activationState) 4947{ 4948#if ENABLE(VIDEO) 4949 MediaPlayerPrivate::notifyAppActivatedEvent(activationState == ActivationActive); 4950#endif 4951 4952 FOR_EACH_PLUGINVIEW(d->m_pluginViews) { 4953 switch (activationState) { 4954 case ActivationActive: 4955 (*it)->handleAppActivatedEvent(); 4956 break; 4957 case ActivationInactive: 4958 (*it)->handleAppDeactivatedEvent(); 4959 break; 4960 case ActivationStandby: 4961 (*it)->handleAppStandbyEvent(); 4962 break; 4963 } 4964 } 4965 4966 d->notifyAppActivationStateChange(activationState); 4967} 4968 4969void WebPage::notifySwipeEvent() 4970{ 4971 if (d->m_fullScreenPluginView.get()) 4972 d->m_fullScreenPluginView->handleSwipeEvent(); 4973 else 4974 notifyFullScreenVideoExited(true); 4975} 4976 4977void WebPage::notifyScreenPowerStateChanged(bool powered) 4978{ 4979 FOR_EACH_PLUGINVIEW(d->m_pluginViews) 4980 (*it)->handleScreenPowerEvent(powered); 4981} 4982 4983void WebPage::notifyFullScreenVideoExited(bool done) 4984{ 4985 UNUSED_PARAM(done); 4986#if ENABLE(VIDEO) 4987 Element* element = toElement(d->m_fullscreenNode.get()); 4988 if (!element) 4989 return; 4990 if (d->m_webSettings->fullScreenVideoCapable() && element->hasTagName(HTMLNames::videoTag)) 4991 static_cast<HTMLMediaElement*>(element)->exitFullscreen(); 4992#if ENABLE(FULLSCREEN_API) 4993 else 4994 element->document()->webkitCancelFullScreen(); 4995#endif 4996#endif 4997} 4998 4999void WebPage::clearPluginSiteData() 5000{ 5001 PluginDatabase* database = PluginDatabase::installedPlugins(true); 5002 5003 if (!database) 5004 return; 5005 5006 Vector<PluginPackage*> plugins = database->plugins(); 5007 5008 Vector<PluginPackage*>::const_iterator end = plugins.end(); 5009 for (Vector<PluginPackage*>::const_iterator it = plugins.begin(); it != end; ++it) 5010 (*it)->clearSiteData(String()); 5011} 5012 5013void WebPage::setExtraPluginDirectory(const BlackBerry::Platform::String& path) 5014{ 5015 PluginDatabase* database = PluginDatabase::installedPlugins(true /* true for loading default directories */); 5016 if (!database) 5017 return; 5018 5019 Vector<String> pluginDirectories = database->pluginDirectories(); 5020 if (path.empty() || pluginDirectories.contains(String(path))) 5021 return; 5022 5023 pluginDirectories.append(path); 5024 database->setPluginDirectories(pluginDirectories); 5025 // Clear out every Page's local copy of PluginData, so it will 5026 // retrieve it again when necessary. Otherwise each page will be 5027 // using old data and may either direct content to a plugin that 5028 // doesn't exist (causing a crash) or not direct content to a plugin 5029 // that does exist. We do this even if plugins are disabled because 5030 // this step is not done when plugins get enabled. 5031 5032 // True only needs to be passed here if we want to reload each frame 5033 // in the page's frame tree. Here we are passing false for minimum disruption, 5034 // and because this does exactly what we need and nothing more: refresh the plugin data. 5035 d->m_page->refreshPlugins(false /* false for minimum disruption as described above */); 5036 5037 if (d->m_webSettings->arePluginsEnabled()) 5038 database->refresh(); 5039} 5040 5041void WebPage::updateDisabledPluginFiles(const BlackBerry::Platform::String& fileName, bool disabled) 5042{ 5043 // Passing true will set plugin database with default plugin directories and refresh it. 5044 PluginDatabase* database = PluginDatabase::installedPlugins(true /* true for loading default directories */); 5045 if (!database) 5046 return; 5047 5048 if (disabled) { 5049 if (!database->addDisabledPluginFile(fileName)) 5050 return; 5051 } else { 5052 if (!database->removeDisabledPluginFile(fileName)) 5053 return; 5054 } 5055 5056 // Clear out every Page's local copy of PluginData, so it will 5057 // retrieve it again when necessary. Otherwise each page will be 5058 // using old data and may either direct content to a plugin that 5059 // doesn't exist (causing a crash) or not direct content to a plugin 5060 // that does exist. We do this even if plugins are disabled because 5061 // this step is not done when plugins get enabled. 5062 5063 // True only needs to be passed here if we want to reload each frame 5064 // in the page's frame tree. Here we are passing false for minimum disruption, 5065 // and because this does exactly what we need and nothing more: refresh the plugin data. 5066 d->m_page->refreshPlugins(false /* false for minimum disruption as described above */); 5067 5068 // Refresh the plugin database if necessary. 5069 if (d->m_webSettings->arePluginsEnabled()) 5070 database->refresh(); 5071} 5072 5073void WebPage::onNetworkAvailabilityChanged(bool available) 5074{ 5075 updateOnlineStatus(available); 5076} 5077 5078void WebPage::onCertificateStoreLocationSet(const BlackBerry::Platform::String& caPath) 5079{ 5080#if ENABLE(VIDEO) 5081 MediaPlayerPrivate::setCertificatePath(caPath); 5082#endif 5083} 5084 5085void WebPage::enableDNSPrefetch() 5086{ 5087 d->m_page->settings()->setDNSPrefetchingEnabled(true); 5088} 5089 5090void WebPage::disableDNSPrefetch() 5091{ 5092 d->m_page->settings()->setDNSPrefetchingEnabled(false); 5093} 5094 5095bool WebPage::isDNSPrefetchEnabled() const 5096{ 5097 return d->m_page->settings()->dnsPrefetchingEnabled(); 5098} 5099 5100void WebPage::enableWebInspector() 5101{ 5102 if (isWebInspectorEnabled() || !d->m_inspectorClient) 5103 return; 5104 5105 d->m_page->inspectorController()->connectFrontend(d->m_inspectorClient); 5106 d->m_page->settings()->setDeveloperExtrasEnabled(true); 5107 d->setPreventsScreenDimming(true); 5108 d->m_inspectorEnabled = true; 5109} 5110 5111void WebPage::disableWebInspector() 5112{ 5113 if (!isWebInspectorEnabled()) 5114 return; 5115 5116 d->m_page->inspectorController()->disconnectFrontend(); 5117 d->m_page->settings()->setDeveloperExtrasEnabled(false); 5118 d->setPreventsScreenDimming(false); 5119 d->m_inspectorEnabled = false; 5120} 5121 5122bool WebPage::isWebInspectorEnabled() 5123{ 5124 return d->m_inspectorEnabled; 5125} 5126 5127void WebPage::enablePasswordEcho() 5128{ 5129 d->m_page->settings()->setPasswordEchoEnabled(true); 5130} 5131 5132void WebPage::disablePasswordEcho() 5133{ 5134 d->m_page->settings()->setPasswordEchoEnabled(false); 5135} 5136 5137void WebPage::dispatchInspectorMessage(const BlackBerry::Platform::String& message) 5138{ 5139 d->m_page->inspectorController()->dispatchMessageFromFrontend(message); 5140} 5141 5142void WebPage::inspectCurrentContextElement() 5143{ 5144 if (isWebInspectorEnabled() && d->m_currentContextNode.get()) 5145 d->m_page->inspectorController()->inspect(d->m_currentContextNode.get()); 5146} 5147 5148Platform::IntPoint WebPage::adjustDocumentScrollPosition(const Platform::IntPoint& documentScrollPosition, const Platform::IntRect& documentPaddingRect) 5149{ 5150 return d->m_proximityDetector->findBestPoint(documentScrollPosition, documentPaddingRect); 5151} 5152 5153Platform::IntSize WebPage::fixedElementSizeDelta() 5154{ 5155 ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread()); 5156 5157 // Traverse the layer tree and find the fixed element rect if there is one. 5158 IntRect fixedElementRect; 5159 if (d->compositor() && d->compositor()->rootLayer()) 5160 d->compositor()->findFixedElementRect(d->compositor()->rootLayer(), fixedElementRect); 5161 5162 // Ignore the fixed element if it is not at the top of page. 5163 if (!fixedElementRect.isEmpty() && !fixedElementRect.y()) 5164 return Platform::IntSize(0, fixedElementRect.height()); 5165 return Platform::IntSize(); 5166} 5167 5168bool WebPagePrivate::compositorDrawsRootLayer() const 5169{ 5170 if (!m_mainFrame) 5171 return false; 5172 5173#if USE(ACCELERATED_COMPOSITING) 5174 if (Platform::userInterfaceThreadMessageClient()->isCurrentThread()) 5175 return m_compositor && m_compositor->drawsRootLayer(); 5176 5177 // WebKit thread implementation: 5178 RenderView* renderView = m_mainFrame->contentRenderer(); 5179 if (!renderView || !renderView->layer() || !renderView->layer()->backing()) 5180 return false; 5181 5182 return !renderView->layer()->backing()->paintsIntoWindow(); 5183#else 5184 return false; 5185#endif 5186} 5187 5188void WebPagePrivate::setCompositorDrawsRootLayer(bool compositorDrawsRootLayer) 5189{ 5190#if USE(ACCELERATED_COMPOSITING) 5191 if (m_page->settings()->forceCompositingMode() == compositorDrawsRootLayer) 5192 return; 5193 5194 // When the BlackBerry port forces compositing mode, the root layer stops 5195 // painting to window and starts painting to layer instead. 5196 m_page->settings()->setForceCompositingMode(compositorDrawsRootLayer); 5197 m_backingStore->d->updateSuspendScreenUpdateState(); 5198 5199 if (!m_mainFrame) 5200 return; 5201 5202 if (FrameView* view = m_mainFrame->view()) 5203 view->updateCompositingLayers(); 5204#endif 5205} 5206 5207#if USE(ACCELERATED_COMPOSITING) 5208void WebPagePrivate::scheduleRootLayerCommit() 5209{ 5210 if (!(m_frameLayers && m_frameLayers->hasLayer()) && !m_overlayLayer) 5211 return; 5212 5213 m_needsCommit = true; 5214 if (!m_rootLayerCommitTimer->isActive()) { 5215#if DEBUG_AC_COMMIT 5216 Platform::logAlways(Platform::LogLevelCritical, 5217 "%s: m_rootLayerCommitTimer->isActive() = %d", 5218 WTF_PRETTY_FUNCTION, m_rootLayerCommitTimer->isActive()); 5219#endif 5220 m_rootLayerCommitTimer->startOneShot(0); 5221 } 5222} 5223 5224static bool needsLayoutRecursive(FrameView* view) 5225{ 5226 if (view->needsLayout()) 5227 return true; 5228 5229 bool subframesNeedsLayout = false; 5230 const HashSet<RefPtr<Widget> >* viewChildren = view->children(); 5231 HashSet<RefPtr<Widget> >::const_iterator end = viewChildren->end(); 5232 for (HashSet<RefPtr<Widget> >::const_iterator current = viewChildren->begin(); current != end && !subframesNeedsLayout; ++current) { 5233 Widget* widget = (*current).get(); 5234 if (widget->isFrameView()) 5235 subframesNeedsLayout |= needsLayoutRecursive(toFrameView(widget)); 5236 } 5237 5238 return subframesNeedsLayout; 5239} 5240 5241LayerRenderingResults WebPagePrivate::lastCompositingResults() const 5242{ 5243 if (m_compositor) 5244 return m_compositor->lastCompositingResults(); 5245 return LayerRenderingResults(); 5246} 5247 5248GraphicsLayer* WebPagePrivate::overlayLayer() 5249{ 5250 if (!m_overlayLayer) 5251 m_overlayLayer = GraphicsLayer::create(0, this); 5252 5253 return m_overlayLayer.get(); 5254} 5255 5256void WebPagePrivate::setCompositor(PassRefPtr<WebPageCompositorPrivate> compositor) 5257{ 5258 using namespace BlackBerry::Platform; 5259 5260 // We depend on the current thread being the WebKit thread when it's not the Compositing thread. 5261 // That seems extremely likely to be the case, but let's assert just to make sure. 5262 ASSERT(webKitThreadMessageClient()->isCurrentThread()); 5263 5264 m_backingStore->d->suspendScreenUpdates(); 5265 5266 // The m_compositor member has to be modified during a sync call for thread 5267 // safe access to m_compositor and its refcount. 5268 userInterfaceThreadMessageClient()->dispatchSyncMessage(createMethodCallMessage(&WebPagePrivate::setCompositorHelper, this, compositor)); 5269 5270 m_backingStore->d->resumeScreenUpdates(BackingStore::RenderAndBlit); 5271} 5272 5273void WebPagePrivate::setCompositorHelper(PassRefPtr<WebPageCompositorPrivate> compositor) 5274{ 5275 using namespace BlackBerry::Platform; 5276 5277 // The m_compositor member has to be modified during a sync call for thread 5278 // safe access to m_compositor and its refcount. 5279 ASSERT(userInterfaceThreadMessageClient()->isCurrentThread()); 5280 5281 m_compositor = compositor; 5282 if (m_compositor) { 5283 m_compositor->setPage(this); 5284 5285 m_compositor->setBackgroundColor(m_webSettings->backgroundColor()); 5286 } 5287 5288 // The previous compositor, if any, has now released it's OpenGL resources, 5289 // so we can safely free the owned context, if any. 5290 m_ownedContext.clear(); 5291} 5292 5293void WebPagePrivate::setCompositorBackgroundColor(const Color& backgroundColor) 5294{ 5295 if (m_compositor) 5296 m_compositor->setBackgroundColor(backgroundColor); 5297} 5298 5299void WebPagePrivate::commitRootLayer(const IntRect& layoutRect, const IntRect& documentRect, bool drawsRootLayer) 5300{ 5301#if DEBUG_AC_COMMIT 5302 Platform::logAlways(Platform::LogLevelCritical, 5303 "%s: m_compositor = 0x%p", 5304 WTF_PRETTY_FUNCTION, m_compositor.get()); 5305#endif 5306 5307 if (!m_compositor) 5308 return; 5309 5310 // Frame layers 5311 LayerWebKitThread* rootLayer = 0; 5312 if (m_frameLayers) 5313 rootLayer = m_frameLayers->rootLayer(); 5314 5315 if (rootLayer && rootLayer->layerCompositingThread() != m_compositor->rootLayer()) 5316 m_compositor->setRootLayer(rootLayer->layerCompositingThread()); 5317 5318 // Overlay layers 5319 LayerWebKitThread* overlayLayer = 0; 5320 if (m_overlayLayer) 5321 overlayLayer = m_overlayLayer->platformLayer(); 5322 5323 if (overlayLayer && overlayLayer->layerCompositingThread() != m_compositor->overlayLayer()) 5324 m_compositor->setOverlayLayer(overlayLayer->layerCompositingThread()); 5325 5326 m_compositor->setLayoutRect(layoutRect); 5327 m_compositor->setDocumentRect(documentRect); 5328 m_compositor->setDrawsRootLayer(drawsRootLayer); 5329 5330 if (rootLayer) 5331 rootLayer->commitOnCompositingThread(); 5332 if (overlayLayer) 5333 overlayLayer->commitOnCompositingThread(); 5334 5335 m_animationStartTime = currentTime(); 5336 m_didStartAnimations = false; 5337 if (rootLayer) 5338 m_didStartAnimations |= rootLayer->startAnimations(m_animationStartTime); 5339 if (overlayLayer) 5340 m_didStartAnimations |= overlayLayer->startAnimations(m_animationStartTime); 5341 5342 scheduleCompositingRun(); 5343} 5344 5345bool WebPagePrivate::commitRootLayerIfNeeded() 5346{ 5347#if DEBUG_AC_COMMIT 5348 Platform::logAlways(Platform::LogLevelCritical, 5349 "%s: m_suspendRootLayerCommit = %d, m_needsCommit = %d, m_frameLayers = 0x%p, m_frameLayers->hasLayer() = %d, needsLayoutRecursive() = %d", 5350 WTF_PRETTY_FUNCTION, 5351 m_suspendRootLayerCommit, 5352 m_needsCommit, 5353 m_frameLayers.get(), 5354 m_frameLayers && m_frameLayers->hasLayer(), 5355 m_mainFrame && m_mainFrame->view() && needsLayoutRecursive(m_mainFrame->view())); 5356#endif 5357 5358 if (m_suspendRootLayerCommit) 5359 return false; 5360 5361 if (!m_needsCommit) 5362 return false; 5363 5364 // Don't bail if the layers were removed and we now need a one shot drawing sync as a consequence. 5365 if (!(m_frameLayers && m_frameLayers->hasLayer()) && !m_overlayLayer && !m_needsOneShotDrawingSynchronization) 5366 return false; 5367 5368 FrameView* view = m_mainFrame->view(); 5369 if (!view) 5370 return false; 5371 5372 // This can do pretty much anything depending on the overlay, 5373 // so in case it causes relayout or schedule a commit, call it early. 5374 updateDelegatedOverlays(); 5375 5376 // If we sync compositing layers when a layout is pending, we may cause painting of compositing 5377 // layer content to occur before layout has happened, which will cause paintContents() to bail. 5378 if (needsLayoutRecursive(view)) { 5379 // In case of one shot drawing synchronization, you 5380 // should first layoutIfNeeded, render, then commit and draw the layers. 5381 ASSERT(!needsOneShotDrawingSynchronization()); 5382 return false; 5383 } 5384 5385 m_needsCommit = false; 5386 // We get here either due to the commit timer, which would have called 5387 // render if a one shot sync was needed. Or we get called from render 5388 // before the timer times out, which means we are doing a one shot anyway. 5389 m_needsOneShotDrawingSynchronization = false; 5390 5391 if (m_rootLayerCommitTimer->isActive()) 5392 m_rootLayerCommitTimer->stop(); 5393 5394 double scale = currentScale(); 5395 if (m_frameLayers && m_frameLayers->hasLayer()) 5396 m_frameLayers->commitOnWebKitThread(scale); 5397 5398 if (m_overlayLayer) 5399 m_overlayLayer->platformLayer()->commitOnWebKitThread(scale); 5400 5401 willComposite(); 5402 // Stash the visible content rect according to webkit thread 5403 // This is the rectangle used to layout fixed positioned elements, 5404 // and that's what the layer renderer wants. 5405 IntRect layoutRect(scrollPosition(), actualVisibleSize()); 5406 IntRect documentRect(view->minimumScrollPosition(), view->contentsSize()); 5407 bool drawsRootLayer = compositorDrawsRootLayer(); 5408 5409 // Commit changes made to the layers synchronously with the compositing thread. 5410 Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage( 5411 Platform::createMethodCallMessage( 5412 &WebPagePrivate::commitRootLayer, 5413 this, 5414 layoutRect, 5415 documentRect, 5416 drawsRootLayer)); 5417 5418 if (m_didStartAnimations) { 5419 if (m_frameLayers && m_frameLayers->hasLayer()) 5420 m_frameLayers->notifyAnimationsStarted(m_animationStartTime); 5421 if (m_overlayLayer) 5422 m_overlayLayer->platformLayer()->notifyAnimationsStarted(m_animationStartTime); 5423 5424 m_didStartAnimations = false; 5425 } 5426 5427 didComposite(); 5428 return true; 5429} 5430 5431void WebPagePrivate::rootLayerCommitTimerFired(Timer<WebPagePrivate>*) 5432{ 5433 if (m_suspendRootLayerCommit) 5434 return; 5435 5436#if DEBUG_AC_COMMIT 5437 Platform::logAlways(Platform::LogLevelCritical, "%s", WTF_PRETTY_FUNCTION); 5438#endif 5439 5440 m_backingStore->d->instrumentBeginFrame(); 5441 5442 // The commit timer may have fired just before the layout timer, or for some 5443 // other reason we need layout. It's not allowed to commit when a layout is 5444 // pending, becaues a commit can cause parts of the web page to be rendered 5445 // to texture. 5446 // The layout can also turn of compositing altogether, so we need to be prepared 5447 // to handle a one shot drawing synchronization after the layout. 5448 updateLayoutAndStyleIfNeededRecursive(); 5449 5450 // If we transitioned to drawing the root layer using compositor instead of 5451 // backing store, doing a one shot drawing synchronization with the 5452 // backing store is never necessary, because the backing store draws 5453 // nothing. 5454 if (!compositorDrawsRootLayer() && needsOneShotDrawingSynchronization()) { 5455#if DEBUG_AC_COMMIT 5456 Platform::logAlways(Platform::LogLevelCritical, 5457 "%s: OneShotDrawingSynchronization code path!", 5458 WTF_PRETTY_FUNCTION); 5459#endif 5460 const IntRect windowRect = IntRect(IntPoint::zero(), viewportSize()); 5461 m_backingStore->d->repaint(windowRect, true /*contentChanged*/, true /*immediate*/); 5462 return; 5463 } 5464 5465 commitRootLayerIfNeeded(); 5466} 5467 5468void WebPagePrivate::setRootLayerWebKitThread(Frame* frame, LayerWebKitThread* layer) 5469{ 5470 // This method updates the FrameLayers based on input from WebCore. 5471 // FrameLayers keeps track of the layer proxies attached to frames. 5472 // We will have to compute a new root layer and update the compositor. 5473 if (!layer && !m_frameLayers) 5474 return; 5475 5476 if (!layer) { 5477 ASSERT(m_frameLayers); 5478 m_frameLayers->removeLayerByFrame(frame); 5479 if (!m_frameLayers->hasLayer()) 5480 m_frameLayers.clear(); 5481 } else { 5482 if (!m_frameLayers) 5483 m_frameLayers = adoptPtr(new FrameLayers(this)); 5484 5485 if (!m_frameLayers->containsLayerForFrame(frame)) 5486 m_frameLayers->addLayer(frame, layer); 5487 5488 ASSERT(m_frameLayers); 5489 } 5490 5491 LayerCompositingThread* rootLayerCompositingThread = 0; 5492 if (m_frameLayers && m_frameLayers->rootLayer()) 5493 rootLayerCompositingThread = m_frameLayers->rootLayer()->layerCompositingThread(); 5494 5495 setRootLayerCompositingThread(rootLayerCompositingThread); 5496} 5497 5498void WebPagePrivate::setRootLayerCompositingThread(LayerCompositingThread* layer) 5499{ 5500 if (!Platform::userInterfaceThreadMessageClient()->isCurrentThread()) { 5501 Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage( 5502 Platform::createMethodCallMessage(&WebPagePrivate::setRootLayerCompositingThread, this, layer)); 5503 return; 5504 } 5505 5506 if (layer && !m_compositor) 5507 createCompositor(); 5508 5509 // Don't ASSERT(m_compositor) here because setIsAcceleratedCompositingActive(true) 5510 // may not turn accelerated compositing on since m_backingStore is 0. 5511 if (m_compositor) 5512 m_compositor->setRootLayer(layer); 5513} 5514 5515bool WebPagePrivate::createCompositor() 5516{ 5517 // If there's no window, the compositor will be supplied by the API client 5518 if (!m_client->window()) 5519 return false; 5520 5521 m_ownedContext = GLES2Context::create(this); 5522 m_compositor = WebPageCompositorPrivate::create(this, 0); 5523 m_compositor->setContext(m_ownedContext.get()); 5524 5525 // The compositor is created in a sync message, so there's no risk of race condition on the 5526 // WebSettings. 5527 m_compositor->setBackgroundColor(m_webSettings->backgroundColor()); 5528 5529 return true; 5530} 5531 5532void WebPagePrivate::destroyCompositor() 5533{ 5534 // m_compositor is a RefPtr, so it may live on beyond this point. 5535 // Disconnect the compositor from us 5536 m_compositor->detach(); 5537 m_compositor.clear(); 5538 m_ownedContext.clear(); 5539} 5540 5541void WebPagePrivate::syncDestroyCompositorOnCompositingThread() 5542{ 5543 if (!m_compositor) 5544 return; 5545 5546 Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage( 5547 Platform::createMethodCallMessage( 5548 &WebPagePrivate::destroyCompositor, this)); 5549} 5550 5551void WebPagePrivate::releaseLayerResources() 5552{ 5553 if (!isAcceleratedCompositingActive()) 5554 return; 5555 5556 if (m_frameLayers) 5557 m_frameLayers->releaseLayerResources(); 5558 5559 Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage( 5560 Platform::createMethodCallMessage(&WebPagePrivate::releaseLayerResourcesCompositingThread, this)); 5561} 5562 5563void WebPagePrivate::releaseLayerResourcesCompositingThread() 5564{ 5565 m_compositor->releaseLayerResources(); 5566} 5567 5568void WebPagePrivate::suspendRootLayerCommit() 5569{ 5570 if (m_suspendRootLayerCommit) 5571 return; 5572 5573 m_suspendRootLayerCommit = true; 5574 5575 if (!m_compositor) 5576 return; 5577 5578 releaseLayerResources(); 5579} 5580 5581void WebPagePrivate::resumeRootLayerCommit() 5582{ 5583 if (!m_suspendRootLayerCommit) 5584 return; 5585 5586 m_suspendRootLayerCommit = false; 5587 m_needsCommit = true; 5588 // PR 330917, explicitly start root layer commit timer, so that there's a commit 5589 // even if BackingStore got disabled/removed. 5590 scheduleRootLayerCommit(); 5591} 5592 5593bool WebPagePrivate::needsOneShotDrawingSynchronization() 5594{ 5595 return m_needsOneShotDrawingSynchronization; 5596} 5597 5598void WebPagePrivate::setNeedsOneShotDrawingSynchronization() 5599{ 5600 if (compositorDrawsRootLayer()) { 5601 scheduleRootLayerCommit(); 5602 return; 5603 } 5604 5605 // This means we have to commit layers on next render, or render on the next commit, 5606 // whichever happens first. 5607 m_needsCommit = true; 5608 m_needsOneShotDrawingSynchronization = true; 5609} 5610 5611void WebPagePrivate::notifyFlushRequired(const GraphicsLayer*) 5612{ 5613 scheduleRootLayerCommit(); 5614} 5615#endif // USE(ACCELERATED_COMPOSITING) 5616 5617void WebPagePrivate::enterFullscreenForNode(Node* node) 5618{ 5619#if ENABLE(VIDEO) 5620 if (!node || !node->hasTagName(HTMLNames::videoTag)) 5621 return; 5622 5623 MediaPlayer* player = static_cast<HTMLMediaElement*>(node)->player(); 5624 if (!player) 5625 return; 5626 5627 MediaPlayerPrivate* mmrPlayer = static_cast<MediaPlayerPrivate*>(player->implementation()); 5628 if (!mmrPlayer) 5629 return; 5630 5631 Platform::Graphics::Window* window = mmrPlayer->getWindow(); 5632 if (!window) 5633 return; 5634 5635 const char* contextName = mmrPlayer->mmrContextName(); 5636 if (!contextName) 5637 return; 5638 5639 mmrPlayer->setFullscreenWebPageClient(m_client); 5640 m_fullscreenNode = node; 5641 m_client->fullscreenStart(contextName, window, mmrPlayer->getWindowScreenRect()); 5642#endif 5643} 5644 5645void WebPagePrivate::exitFullscreenForNode(Node* node) 5646{ 5647#if ENABLE(VIDEO) 5648 if (m_fullscreenNode.get()) { 5649 m_client->fullscreenStop(); 5650 m_fullscreenNode = 0; 5651 } 5652 5653 if (!node || !node->hasTagName(HTMLNames::videoTag)) 5654 return; 5655 5656 MediaPlayer* player = static_cast<HTMLMediaElement*>(node)->player(); 5657 if (!player) 5658 return; 5659 5660 MediaPlayerPrivate* mmrPlayer = static_cast<MediaPlayerPrivate*>(player->implementation()); 5661 if (!mmrPlayer) 5662 return; 5663 5664 // Fullscreen mode is being turned off, so MediaPlayerPrivate no longer needs the pointer. 5665 mmrPlayer->setFullscreenWebPageClient(0); 5666#endif 5667} 5668 5669#if ENABLE(FULLSCREEN_API) 5670void WebPagePrivate::enterFullScreenForElement(Element* element) 5671{ 5672#if ENABLE(VIDEO) 5673 if (!element) 5674 return; 5675 if (m_webSettings->fullScreenVideoCapable() && element->hasTagName(HTMLNames::videoTag)) { 5676 // The Browser chrome has its own fullscreen video widget it wants to 5677 // use, and this is a video element. The only reason that 5678 // webkitWillEnterFullScreenForElement() and 5679 // webkitDidEnterFullScreenForElement() are still called in this case 5680 // is so that exitFullScreenForElement() gets called later. 5681 enterFullscreenForNode(element); 5682 } else { 5683 // At this point, we can assume that there would be a viewport size change if 5684 // the current visible size and screen size are not equal. 5685 if (transformedActualVisibleSize() != transformedViewportSize()) { 5686 // The current scale can be clamped to a greater minimum scale when we relayout contents during 5687 // the change of the viewport size. Cache the current scale so that we can restore it when 5688 // leaving fullscreen. Otherwise, it is possible that we will use the wrong scale. 5689 m_scaleBeforeFullScreen = currentScale(); 5690 5691 // When an element goes fullscreen, the viewport size changes and the scroll 5692 // position might change. So we keep track of it here, in order to restore it 5693 // once element leaves fullscreen. 5694 m_scrollPositionBeforeFullScreen = m_mainFrame->view()->scrollPosition(); 5695 5696 // We need to remember the orientation before entering fullscreen, so that we can adjust 5697 // the scale and scroll position when exiting fullscreen if needed, because the scale and 5698 // scroll position may not apply (overscale and/or overscroll) in the other orientation. 5699 m_orientationBeforeFullScreen = orientation(); 5700 } 5701 5702 // No fullscreen video widget has been made available by the Browser 5703 // chrome, or this is not a video element. The webkitRequestFullScreen 5704 // Javascript call is often made on a div element. 5705 // This is where we would hide the browser's chrome if we wanted to. 5706 client()->fullscreenStart(); 5707 m_fullscreenNode = element; 5708 } 5709#endif 5710} 5711 5712void WebPagePrivate::exitFullScreenForElement(Element* element) 5713{ 5714#if ENABLE(VIDEO) 5715 if (!element) 5716 return; 5717 if (m_webSettings->fullScreenVideoCapable() && element->hasTagName(HTMLNames::videoTag)) { 5718 // The Browser chrome has its own fullscreen video widget. 5719 exitFullscreenForNode(element); 5720 } else { 5721 // This is where we would restore the browser's chrome 5722 // if hidden above. 5723 client()->fullscreenStop(); 5724 m_fullscreenNode = 0; 5725 } 5726#endif 5727} 5728 5729// FIXME: Move this method to WebCore. 5730void WebPagePrivate::adjustFullScreenElementDimensionsIfNeeded() 5731{ 5732 // If we are in fullscreen video mode, and we change the FrameView::viewportRect, 5733 // we need to adjust the media container to the new size. 5734 if (!m_fullscreenNode || !m_fullscreenNode->renderer() 5735 || !m_fullscreenNode->document() || !m_fullscreenNode->document()->fullScreenRenderer()) 5736 return; 5737 5738 ASSERT(m_fullscreenNode->isElementNode()); 5739 ASSERT(toElement(m_fullscreenNode.get())->isMediaElement()); 5740 5741 Document* document = m_fullscreenNode->document(); 5742 RenderStyle* fullScreenStyle = document->fullScreenRenderer()->style(); 5743 ASSERT(fullScreenStyle); 5744 5745 // In order to compensate possible round errors when we scale the fullscreen 5746 // container element to fit to the viewport, lets make the fullscreen 1px wider 5747 // than the viewport size on the right, and one pixel longer at the bottom 5748 // of the scroll position. 5749 IntRect viewportRect = m_mainFrame->view()->visibleContentRect(); 5750 int viewportWidth = viewportRect.width() + 1; 5751 int viewportHeight = viewportRect.height() + 1; 5752 5753 fullScreenStyle->setWidth(Length(viewportWidth, WebCore::Fixed)); 5754 fullScreenStyle->setHeight(Length(viewportHeight, WebCore::Fixed)); 5755 fullScreenStyle->setLeft(Length(0, WebCore::Fixed)); 5756 fullScreenStyle->setTop(Length(0, WebCore::Fixed)); 5757 fullScreenStyle->setBackgroundColor(Color::black); 5758 5759 document->fullScreenRenderer()->setNeedsLayoutAndPrefWidthsRecalc(); 5760 document->recalcStyle(Node::Force); 5761} 5762#endif 5763 5764void WebPagePrivate::didChangeSettings(WebSettings* webSettings) 5765{ 5766 Settings* coreSettings = m_page->settings(); 5767 m_page->setGroupName(webSettings->pageGroupName()); 5768 coreSettings->setXSSAuditorEnabled(webSettings->xssAuditorEnabled()); 5769 coreSettings->setLoadsImagesAutomatically(webSettings->loadsImagesAutomatically()); 5770 coreSettings->setShouldDrawBorderWhileLoadingImages(webSettings->shouldDrawBorderWhileLoadingImages()); 5771 coreSettings->setScriptEnabled(webSettings->isJavaScriptEnabled()); 5772 coreSettings->setDeviceSupportsMouse(webSettings->deviceSupportsMouse()); 5773 coreSettings->setDefaultFixedFontSize(webSettings->defaultFixedFontSize()); 5774 coreSettings->setDefaultFontSize(webSettings->defaultFontSize()); 5775 coreSettings->setMinimumLogicalFontSize(webSettings->minimumFontSize()); 5776 if (!webSettings->serifFontFamily().empty()) 5777 coreSettings->setSerifFontFamily(String(webSettings->serifFontFamily())); 5778 if (!webSettings->fixedFontFamily().empty()) 5779 coreSettings->setFixedFontFamily(String(webSettings->fixedFontFamily())); 5780 if (!webSettings->sansSerifFontFamily().empty()) 5781 coreSettings->setSansSerifFontFamily(String(webSettings->sansSerifFontFamily())); 5782 if (!webSettings->standardFontFamily().empty()) 5783 coreSettings->setStandardFontFamily(String(webSettings->standardFontFamily())); 5784 coreSettings->setJavaScriptCanOpenWindowsAutomatically(webSettings->canJavaScriptOpenWindowsAutomatically()); 5785 coreSettings->setAllowScriptsToCloseWindows(webSettings->canJavaScriptOpenWindowsAutomatically()); // Why are we using the same value as setJavaScriptCanOpenWindowsAutomatically()? 5786 coreSettings->setPluginsEnabled(webSettings->arePluginsEnabled()); 5787 coreSettings->setDefaultTextEncodingName(webSettings->defaultTextEncodingName()); 5788 coreSettings->setDownloadableBinaryFontsEnabled(webSettings->downloadableBinaryFontsEnabled()); 5789 coreSettings->setSpatialNavigationEnabled(m_webSettings->isSpatialNavigationEnabled()); 5790 coreSettings->setAsynchronousSpellCheckingEnabled(m_webSettings->isAsynchronousSpellCheckingEnabled()); 5791 5792 BlackBerry::Platform::String stylesheetURL = webSettings->userStyleSheetLocation(); 5793 if (!stylesheetURL.empty()) 5794 coreSettings->setUserStyleSheetLocation(KURL(KURL(), stylesheetURL)); 5795 5796 coreSettings->setFirstScheduledLayoutDelay(webSettings->firstScheduledLayoutDelay()); 5797 coreSettings->setUseCache(webSettings->useWebKitCache()); 5798 coreSettings->setCookieEnabled(webSettings->areCookiesEnabled()); 5799 5800 if (coreSettings->privateBrowsingEnabled() != webSettings->isPrivateBrowsingEnabled()) { 5801 coreSettings->setPrivateBrowsingEnabled(webSettings->isPrivateBrowsingEnabled()); 5802 cookieManager().setPrivateMode(webSettings->isPrivateBrowsingEnabled()); 5803 CredentialStorage::setPrivateMode(webSettings->isPrivateBrowsingEnabled()); 5804 } 5805 5806#if ENABLE(SQL_DATABASE) 5807 // DatabaseManager can only be initialized for once, so it doesn't 5808 // make sense to change database path after DatabaseManager has 5809 // already been initialized. 5810 static bool dbinit = false; 5811 if (!dbinit && !webSettings->databasePath().empty()) { 5812 dbinit = true; 5813 DatabaseManager::manager().initialize(webSettings->databasePath()); 5814 } 5815 5816 // The directory of cacheStorage for one page group can only be initialized once. 5817 static bool acinit = false; 5818 if (!acinit && !webSettings->appCachePath().empty()) { 5819 acinit = true; 5820 cacheStorage().setCacheDirectory(webSettings->appCachePath()); 5821 } 5822 5823 coreSettings->setLocalStorageDatabasePath(webSettings->localStoragePath()); 5824 DatabaseManager::manager().setIsAvailable(webSettings->isDatabasesEnabled()); 5825 5826 coreSettings->setLocalStorageEnabled(webSettings->isLocalStorageEnabled()); 5827 coreSettings->setOfflineWebApplicationCacheEnabled(webSettings->isAppCacheEnabled()); 5828 5829 m_page->group().groupSettings()->setLocalStorageQuotaBytes(webSettings->localStorageQuota()); 5830 coreSettings->setSessionStorageQuota(webSettings->sessionStorageQuota()); 5831 coreSettings->setUsesPageCache(webSettings->maximumPagesInCache()); 5832 coreSettings->setFrameFlatteningEnabled(webSettings->isFrameFlatteningEnabled()); 5833#endif 5834 5835#if ENABLE(INDEXED_DATABASE) 5836 m_page->group().groupSettings()->setIndexedDBDatabasePath(webSettings->indexedDataBasePath()); 5837#endif 5838 5839 5840#if ENABLE(WEB_SOCKETS) 5841 WebSocket::setIsAvailable(webSettings->areWebSocketsEnabled()); 5842#endif 5843 5844#if ENABLE(FULLSCREEN_API) 5845 // This allows Javascript to call webkitRequestFullScreen() on an element. 5846 coreSettings->setFullScreenEnabled(true); 5847#endif 5848 5849#if ENABLE(VIEWPORT_REFLOW) 5850 coreSettings->setTextReflowEnabled(webSettings->textReflowMode() == WebSettings::TextReflowEnabled); 5851#endif 5852 5853 // FIXME: We don't want HTMLTokenizer to yield for anything other than email case because 5854 // call to currentTime() is too expensive on our platform. See RIM Bug #746. 5855 coreSettings->setShouldUseFirstScheduledLayoutDelay(webSettings->isEmailMode()); 5856 coreSettings->setProcessHTTPEquiv(!webSettings->isEmailMode()); 5857 5858 coreSettings->setShouldUseCrossOriginProtocolCheck(!webSettings->allowCrossSiteRequests()); 5859 coreSettings->setWebSecurityEnabled(!webSettings->allowCrossSiteRequests()); 5860 coreSettings->setApplyDeviceScaleFactorInCompositor(webSettings->applyDeviceScaleFactorInCompositor()); 5861 5862 updateBackgroundColor(webSettings->backgroundColor()); 5863 5864 m_page->setDeviceScaleFactor(webSettings->devicePixelRatio()); 5865 5866#if ENABLE(TEXT_AUTOSIZING) 5867 coreSettings->setTextAutosizingEnabled(webSettings->isTextAutosizingEnabled()); 5868#endif 5869} 5870 5871BlackBerry::Platform::String WebPage::textHasAttribute(const BlackBerry::Platform::String& query) const 5872{ 5873 if (Document* doc = d->m_page->focusController()->focusedOrMainFrame()->document()) 5874 return doc->queryCommandValue(query); 5875 5876 return BlackBerry::Platform::String::emptyString(); 5877} 5878 5879void WebPage::setJavaScriptCanAccessClipboard(bool enabled) 5880{ 5881 d->m_page->settings()->setJavaScriptCanAccessClipboard(enabled); 5882} 5883 5884#if USE(ACCELERATED_COMPOSITING) 5885void WebPagePrivate::scheduleCompositingRun() 5886{ 5887 if (WebPageCompositorClient* compositorClient = compositor()->client()) { 5888 double animationTime = compositorClient->requestAnimationFrame(); 5889 compositorClient->invalidate(animationTime); 5890 return; 5891 } 5892 5893 m_backingStore->d->blitVisibleContents(); 5894} 5895#endif 5896 5897void WebPage::setWebGLEnabled(bool enabled) 5898{ 5899 d->m_page->settings()->setWebGLEnabled(enabled); 5900} 5901 5902bool WebPage::isWebGLEnabled() const 5903{ 5904 return d->m_page->settings()->webGLEnabled(); 5905} 5906 5907void WebPagePrivate::frameUnloaded(const Frame* frame) 5908{ 5909 m_inputHandler->frameUnloaded(frame); 5910 m_inPageSearchManager->frameUnloaded(frame); 5911} 5912 5913const BlackBerry::Platform::String& WebPagePrivate::defaultUserAgent() 5914{ 5915 static BlackBerry::Platform::String* defaultUserAgent = 0; 5916 if (!defaultUserAgent) { 5917 BlackBerry::Platform::DeviceInfo* info = BlackBerry::Platform::DeviceInfo::instance(); 5918 char uaBuffer[256]; 5919 int uaSize = snprintf(uaBuffer, 256, "Mozilla/5.0 (%s) AppleWebKit/%d.%d+ (KHTML, like Gecko) Version/%s %sSafari/%d.%d+", 5920 info->family(), WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, info->osVersion(), 5921 info->isMobile() ? "Mobile " : "", WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION); 5922 5923 if (uaSize <= 0 || uaSize >= 256) 5924 BLACKBERRY_CRASH(); 5925 5926 defaultUserAgent = new BlackBerry::Platform::String(BlackBerry::Platform::String::fromUtf8(uaBuffer, uaSize)); 5927 } 5928 5929 return *defaultUserAgent; 5930} 5931 5932WebTapHighlight* WebPage::tapHighlight() const 5933{ 5934 return d->m_tapHighlight.get(); 5935} 5936 5937WebTapHighlight* WebPage::selectionHighlight() const 5938{ 5939 return d->m_selectionHighlight.get(); 5940} 5941 5942void WebPage::addOverlay(WebOverlay* overlay) 5943{ 5944#if USE(ACCELERATED_COMPOSITING) 5945 if (overlay->d->graphicsLayer()) { 5946 overlay->d->setPage(d); 5947 d->overlayLayer()->addChild(overlay->d->graphicsLayer()); 5948 } 5949#endif 5950} 5951 5952void WebPage::removeOverlay(WebOverlay* overlay) 5953{ 5954#if USE(ACCELERATED_COMPOSITING) 5955 if (overlay->d->graphicsLayer()->parent() != d->overlayLayer()) 5956 return; 5957 5958 overlay->removeFromParent(); 5959 overlay->d->clear(); 5960 overlay->d->setPage(0); 5961#endif 5962} 5963 5964void WebPage::addCompositingThreadOverlay(WebOverlay* overlay) 5965{ 5966#if USE(ACCELERATED_COMPOSITING) 5967 ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread()); 5968 if (!d->compositor()) 5969 return; 5970 5971 overlay->d->setPage(d); 5972 d->compositor()->addOverlay(overlay->d->layerCompositingThread()); 5973#endif 5974} 5975 5976void WebPage::removeCompositingThreadOverlay(WebOverlay* overlay) 5977{ 5978#if USE(ACCELERATED_COMPOSITING) 5979 ASSERT(Platform::userInterfaceThreadMessageClient()->isCurrentThread()); 5980 if (d->compositor()) 5981 d->compositor()->removeOverlay(overlay->d->layerCompositingThread()); 5982 overlay->d->clear(); 5983 overlay->d->setPage(0); 5984#endif 5985} 5986 5987bool WebPagePrivate::openPagePopup(PagePopupClient* popupClient, const WebCore::IntRect& originBoundsInRootView) 5988{ 5989 closePagePopup(); 5990 m_pagePopup = PagePopup::create(this, popupClient); 5991 5992 WebCore::IntRect popupRect = m_page->chrome().client()->rootViewToScreen(originBoundsInRootView); 5993 popupRect.setSize(popupClient->contentSize()); 5994 if (!m_client->createPopupWebView(popupRect)) { 5995 closePagePopup(); 5996 return false; 5997 } 5998 5999 return true; 6000} 6001 6002void WebPagePrivate::closePagePopup() 6003{ 6004 if (!m_pagePopup) 6005 return; 6006 6007 m_pagePopup->close(); 6008 m_client->closePopupWebView(); 6009 m_pagePopup = 0; 6010} 6011 6012bool WebPagePrivate::hasOpenedPopup() const 6013{ 6014 return m_pagePopup; 6015} 6016 6017void WebPagePrivate::setInspectorOverlayClient(InspectorOverlay::InspectorOverlayClient* inspectorOverlayClient) 6018{ 6019 if (inspectorOverlayClient) { 6020 if (!m_inspectorOverlay) 6021 m_inspectorOverlay = InspectorOverlay::create(this, inspectorOverlayClient); 6022 else 6023 m_inspectorOverlay->setClient(inspectorOverlayClient); 6024 m_inspectorOverlay->update(); 6025 scheduleRootLayerCommit(); 6026 } else { 6027 if (m_inspectorOverlay) { 6028 m_inspectorOverlay->clear(); 6029 m_inspectorOverlay = nullptr; 6030 scheduleRootLayerCommit(); 6031 } 6032 } 6033} 6034 6035void WebPagePrivate::applySizeOverride(int overrideWidth, int overrideHeight) 6036{ 6037 m_client->requestUpdateViewport(overrideWidth, overrideHeight); 6038} 6039 6040void WebPagePrivate::setTextZoomFactor(float textZoomFactor) 6041{ 6042 if (!m_mainFrame) 6043 return; 6044 6045 m_mainFrame->setTextZoomFactor(textZoomFactor); 6046} 6047 6048void WebPagePrivate::restoreHistoryViewState(const WebCore::IntPoint& scrollPosition, double scale, bool shouldReflowBlock) 6049{ 6050 if (!m_mainFrame) { 6051 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 6052 m_backingStore->d->resumeBackingStoreUpdates(); 6053 m_backingStore->d->resumeScreenUpdates(BackingStore::RenderAndBlit); 6054 return; 6055 } 6056 6057 // If we are about to overscroll, scroll back to the valid contents area. 6058 WebCore::IntPoint adjustedScrollPosition = scrollPosition; 6059 WebCore::IntSize validContentsSize = contentsSize(); 6060 WebCore::IntSize viewportSize = actualVisibleSize(); 6061 if (adjustedScrollPosition.x() + viewportSize.width() > validContentsSize.width()) 6062 adjustedScrollPosition.setX(validContentsSize.width() - viewportSize.width()); 6063 if (adjustedScrollPosition.y() + viewportSize.height() > validContentsSize.height()) 6064 adjustedScrollPosition.setY(validContentsSize.height() - viewportSize.height()); 6065 6066 // Here we need to set scroll position what we asked for. 6067 // So we use ScrollView::constrainsScrollingToContentEdge(false). 6068 bool oldConstrainsScrollingToContentEdge = m_mainFrame->view()->constrainsScrollingToContentEdge(); 6069 m_mainFrame->view()->setConstrainsScrollingToContentEdge(false); 6070 setScrollPosition(adjustedScrollPosition); 6071 m_mainFrame->view()->setConstrainsScrollingToContentEdge(oldConstrainsScrollingToContentEdge); 6072 6073 m_shouldReflowBlock = shouldReflowBlock; 6074 6075 if (!zoomAboutPoint(scale, m_mainFrame->view()->scrollPosition(), true /* enforceScaleClamping */, true /*forceRendering*/, true /*isRestoringZoomLevel*/)) { 6076 // We need to notify the client of the scroll position and content size change(s) above even if we didn't scale. 6077 notifyTransformedContentsSizeChanged(); 6078 notifyTransformedScrollChanged(); 6079 } 6080 6081 // If we're already at that scale, then we should still force rendering 6082 // since our scroll position changed. 6083 // FIXME: Do we really need to suspend/resume both backingstore and screen here? 6084 m_backingStore->d->resumeBackingStoreUpdates(); 6085 m_backingStore->d->resumeScreenUpdates(BackingStore::RenderAndBlit); 6086} 6087 6088IntSize WebPagePrivate::screenSize() const 6089{ 6090 return Platform::Graphics::Screen::primaryScreen()->size(); 6091} 6092 6093void WebPagePrivate::postponeDocumentStyleRecalc() 6094{ 6095 if (Document* document = m_mainFrame->document()) { 6096 m_documentChildNeedsStyleRecalc = document->childNeedsStyleRecalc(); 6097 document->clearChildNeedsStyleRecalc(); 6098 6099 m_documentStyleRecalcPostponed = document->hasPendingStyleRecalc(); 6100 document->unscheduleStyleRecalc(); 6101 } 6102} 6103 6104void WebPagePrivate::resumeDocumentStyleRecalc() 6105{ 6106 if (Document* document = m_mainFrame->document()) { 6107 if (m_documentChildNeedsStyleRecalc) 6108 document->setChildNeedsStyleRecalc(); 6109 6110 if (m_documentStyleRecalcPostponed) 6111 document->scheduleStyleRecalc(); 6112 } 6113 6114 m_documentChildNeedsStyleRecalc = false; 6115 m_documentStyleRecalcPostponed = false; 6116} 6117 6118const HitTestResult& WebPagePrivate::hitTestResult(const IntPoint& contentPos) 6119{ 6120 if (m_cachedHitTestContentPos != contentPos) { 6121 m_cachedHitTestContentPos = contentPos; 6122 m_cachedRectHitTestResults.clear(); 6123 m_cachedHitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_cachedHitTestContentPos, HitTestRequest::ReadOnly | HitTestRequest::Active); 6124 } 6125 6126 return m_cachedHitTestResult; 6127} 6128 6129void WebPagePrivate::clearCachedHitTestResult() 6130{ 6131 m_cachedHitTestContentPos = WebCore::IntPoint(-1, -1); 6132} 6133 6134void WebPagePrivate::willComposite() 6135{ 6136 if (!m_page->settings()->developerExtrasEnabled()) 6137 return; 6138 m_page->inspectorController()->willComposite(); 6139} 6140 6141void WebPagePrivate::didComposite() 6142{ 6143 if (!m_page->settings()->developerExtrasEnabled()) 6144 return; 6145 m_page->inspectorController()->didComposite(); 6146} 6147 6148void WebPage::updateNotificationPermission(const BlackBerry::Platform::String& requestId, bool allowed) 6149{ 6150#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 6151 d->notificationManager().updatePermission(requestId, allowed); 6152#else 6153 UNUSED_PARAM(requestId); 6154 UNUSED_PARAM(allowed); 6155#endif 6156} 6157 6158void WebPage::notificationClicked(const BlackBerry::Platform::String& notificationId) 6159{ 6160#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 6161 d->notificationManager().notificationClicked(notificationId); 6162#else 6163 UNUSED_PARAM(notificationId); 6164#endif 6165} 6166 6167void WebPage::notificationClosed(const BlackBerry::Platform::String& notificationId) 6168{ 6169#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 6170 d->notificationManager().notificationClosed(notificationId); 6171#else 6172 UNUSED_PARAM(notificationId); 6173#endif 6174} 6175 6176void WebPage::notificationError(const BlackBerry::Platform::String& notificationId) 6177{ 6178#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 6179 d->notificationManager().notificationError(notificationId); 6180#else 6181 UNUSED_PARAM(notificationId); 6182#endif 6183} 6184 6185void WebPage::notificationShown(const BlackBerry::Platform::String& notificationId) 6186{ 6187#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 6188 d->notificationManager().notificationShown(notificationId); 6189#else 6190 UNUSED_PARAM(notificationId); 6191#endif 6192} 6193 6194void WebPagePrivate::animateToScaleAndDocumentScrollPosition(double destinationZoomScale, const WebCore::FloatPoint& destinationScrollPosition, bool shouldConstrainScrollingToContentEdge) 6195{ 6196 if (destinationScrollPosition == scrollPosition() && destinationZoomScale == currentScale()) 6197 return; 6198 6199 m_shouldReflowBlock = false; 6200 m_userPerformedManualZoom = true; 6201 m_userPerformedManualScroll = true; 6202 client()->animateToScaleAndDocumentScrollPosition(destinationZoomScale, destinationScrollPosition, shouldConstrainScrollingToContentEdge); 6203} 6204 6205void WebPage::animateToScaleAndDocumentScrollPosition(double destinationZoomScale, const BlackBerry::Platform::FloatPoint& destinationScrollPosition, bool shouldConstrainScrollingToContentEdge) 6206{ 6207 d->animateToScaleAndDocumentScrollPosition(destinationZoomScale, destinationScrollPosition, shouldConstrainScrollingToContentEdge); 6208} 6209 6210void WebPagePrivate::updateBackgroundColor(const Color& backgroundColor) 6211{ 6212 if (!m_mainFrame || !m_mainFrame->view()) 6213 return; 6214 6215 m_mainFrame->view()->updateBackgroundRecursively(backgroundColor, backgroundColor.hasAlpha()); 6216 6217 // FIXME: The BackingStore uses the document background color but the WebPageCompositor gets 6218 // the color from settings, which can be different. 6219 Platform::userInterfaceThreadMessageClient()->dispatchMessage( 6220 createMethodCallMessage(&WebPagePrivate::setCompositorBackgroundColor, this, backgroundColor)); 6221 6222 if (m_backingStore) 6223 m_backingStore->d->setWebPageBackgroundColor(documentBackgroundColor()); 6224} 6225 6226Color WebPagePrivate::documentBackgroundColor() const 6227{ 6228 Color color; 6229 if (m_mainFrame && m_mainFrame->view()) 6230 color = m_mainFrame->view()->documentBackgroundColor(); 6231 if (!color.isValid()) 6232 color = m_webSettings->backgroundColor(); 6233 return color; 6234} 6235 6236bool WebPage::isProcessingUserGesture() const 6237{ 6238 return ScriptController::processingUserGesture(); 6239} 6240 6241#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) 6242void WebPagePrivate::animationFrameChanged() 6243{ 6244 if (!m_animationMutex.tryLock()) 6245 return; 6246 6247 if (!m_previousFrameDone) { 6248 m_animationMutex.unlock(); 6249 return; 6250 } 6251 6252 if (!m_animationScheduled) { 6253 stopRefreshAnimationClient(); 6254 m_animationMutex.unlock(); 6255 return; 6256 } 6257 6258 m_previousFrameDone = false; 6259 6260 m_monotonicAnimationStartTime = monotonicallyIncreasingTime(); 6261 callOnMainThread(handleServiceScriptedAnimationsOnMainThread, this); 6262 m_animationMutex.unlock(); 6263} 6264 6265void WebPagePrivate::scheduleAnimation() 6266{ 6267 if (m_animationScheduled) 6268 return; 6269 MutexLocker lock(m_animationMutex); 6270 m_animationScheduled = true; 6271 startRefreshAnimationClient(); 6272} 6273 6274void WebPagePrivate::startRefreshAnimationClient() 6275{ 6276 if (m_isRunningRefreshAnimationClient) 6277 return; 6278 m_isRunningRefreshAnimationClient = true; 6279 BlackBerry::Platform::AnimationFrameRateController::instance()->addClient(this); 6280} 6281 6282void WebPagePrivate::stopRefreshAnimationClient() 6283{ 6284 if (!m_isRunningRefreshAnimationClient) 6285 return; 6286 m_isRunningRefreshAnimationClient = false; 6287 BlackBerry::Platform::AnimationFrameRateController::instance()->removeClient(this); 6288} 6289 6290void WebPagePrivate::serviceAnimations() 6291{ 6292 double monotonicAnimationStartTime; 6293 { 6294 MutexLocker lock(m_animationMutex); 6295 m_animationScheduled = false; 6296 monotonicAnimationStartTime = m_monotonicAnimationStartTime; 6297 } 6298 6299 m_mainFrame->view()->serviceScriptedAnimations(monotonicAnimationStartTime); 6300 6301 { 6302 MutexLocker lock(m_animationMutex); 6303 m_previousFrameDone = true; 6304 } 6305} 6306 6307void WebPagePrivate::handleServiceScriptedAnimationsOnMainThread(void* data) 6308{ 6309 static_cast<WebPagePrivate*>(data)->serviceAnimations(); 6310} 6311#endif 6312 6313void WebPage::setShowDebugBorders(bool show) 6314{ 6315#if USE(ACCELERATED_COMPOSITING) 6316 d->m_page->settings()->setShowDebugBorders(show); 6317#endif 6318} 6319 6320} 6321} 6322