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