[BlackBerry] Remove Mobile mode from WebPage.cpp and WebPage_p.h
[WebKit-https.git] / Source / WebKit / blackberry / Api / WebPage.cpp
1 /*
2  * Copyright (C) 2009, 2010, 2011, 2012 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 "ApplicationCacheStorage.h"
23 #include "AutofillManager.h"
24 #include "BackForwardController.h"
25 #include "BackForwardListImpl.h"
26 #include "BackingStoreClient.h"
27 #include "BackingStoreCompositingSurface.h"
28 #include "BackingStore_p.h"
29 #if ENABLE(BATTERY_STATUS)
30 #include "BatteryClientBlackBerry.h"
31 #endif
32 #include "CString.h"
33 #include "CachedImage.h"
34 #include "Chrome.h"
35 #include "ChromeClientBlackBerry.h"
36 #include "ContextMenuClientBlackBerry.h"
37 #include "CookieManager.h"
38 #include "CredentialManager.h"
39 #include "CredentialStorage.h"
40 #include "CredentialTransformData.h"
41 #include "DOMSupport.h"
42 #include "Database.h"
43 #include "DatabaseSync.h"
44 #include "DatabaseTracker.h"
45 #include "DefaultTapHighlight.h"
46 #include "DeviceMotionClientBlackBerry.h"
47 #include "DeviceOrientationClientBlackBerry.h"
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 "FrameLoaderClientBlackBerry.h"
57 #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD
58 #include "GeolocationClientMock.h"
59 #endif
60 #include "GeolocationControllerClientBlackBerry.h"
61 #include "GroupSettings.h"
62 #include "HTMLAreaElement.h"
63 #include "HTMLFrameOwnerElement.h"
64 #include "HTMLImageElement.h"
65 #include "HTMLInputElement.h"
66 #include "HTMLMediaElement.h"
67 #include "HTMLNames.h"
68 #include "HTMLParserIdioms.h"
69 #include "HTTPParsers.h"
70 #include "HistoryItem.h"
71 #include "IconDatabaseClientBlackBerry.h"
72 #include "InPageSearchManager.h"
73 #include "InRegionScrollableArea.h"
74 #include "InRegionScroller_p.h"
75 #include "InputHandler.h"
76 #include "InspectorBackendDispatcher.h"
77 #include "InspectorClientBlackBerry.h"
78 #include "InspectorController.h"
79 #include "InspectorOverlay.h"
80 #include "JavaScriptDebuggerBlackBerry.h"
81 #include "JavaScriptVariant_p.h"
82 #include "LayerWebKitThread.h"
83 #include "NetworkManager.h"
84 #include "NodeRenderStyle.h"
85 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
86 #include "NotificationPresenterImpl.h"
87 #endif
88 #include "Page.h"
89 #include "PageCache.h"
90 #include "PageGroup.h"
91 #include "PagePopupBlackBerry.h"
92 #include "PlatformTouchEvent.h"
93 #include "PlatformWheelEvent.h"
94 #include "PluginDatabase.h"
95 #include "PluginView.h"
96 #include "RenderLayerBacking.h"
97 #include "RenderLayerCompositor.h"
98 #if ENABLE(FULLSCREEN_API)
99 #include "RenderFullScreen.h"
100 #endif
101 #include "RenderText.h"
102 #include "RenderThemeBlackBerry.h"
103 #include "RenderTreeAsText.h"
104 #include "RenderView.h"
105 #include "RenderWidget.h"
106 #include "ScriptSourceCode.h"
107 #include "ScriptValue.h"
108 #include "ScrollTypes.h"
109 #include "SecurityPolicy.h"
110 #include "SelectionHandler.h"
111 #include "SelectionOverlay.h"
112 #include "Settings.h"
113 #include "Storage.h"
114 #include "StorageNamespace.h"
115 #include "SurfacePool.h"
116 #include "Text.h"
117 #include "ThreadCheck.h"
118 #include "TouchEventHandler.h"
119 #include "TransformationMatrix.h"
120 #if ENABLE(MEDIA_STREAM)
121 #include "UserMediaClientImpl.h"
122 #endif
123 #if ENABLE(VIBRATION)
124 #include "VibrationClientBlackBerry.h"
125 #endif
126 #include "VisiblePosition.h"
127 #if ENABLE(WEBDOM)
128 #include "WebDOMDocument.h"
129 #endif
130 #include "WebKitVersion.h"
131 #include "WebOverlay.h"
132 #include "WebOverlay_p.h"
133 #include "WebPageClient.h"
134 #include "WebSocket.h"
135 #include "WebViewportArguments.h"
136 #include "npapi.h"
137 #include "runtime_root.h"
138
139 #if ENABLE(VIDEO)
140 #include "MediaPlayer.h"
141 #include "MediaPlayerPrivateBlackBerry.h"
142 #endif
143
144 #if USE(SKIA)
145 #include "PlatformContextSkia.h"
146 #endif
147
148 #if USE(ACCELERATED_COMPOSITING)
149 #include "FrameLayers.h"
150 #include "WebPageCompositorClient.h"
151 #include "WebPageCompositor_p.h"
152 #endif
153
154 #include <BlackBerryPlatformDeviceInfo.h>
155 #include <BlackBerryPlatformExecutableMessage.h>
156 #include <BlackBerryPlatformITPolicy.h>
157 #include <BlackBerryPlatformKeyboardEvent.h>
158 #include <BlackBerryPlatformMessageClient.h>
159 #include <BlackBerryPlatformMouseEvent.h>
160 #include <BlackBerryPlatformScreen.h>
161 #include <BlackBerryPlatformSettings.h>
162 #include <JavaScriptCore/APICast.h>
163 #include <JavaScriptCore/JSContextRef.h>
164 #include <JavaScriptCore/JSStringRef.h>
165 #include <SharedPointer.h>
166 #include <sys/keycodes.h>
167 #include <unicode/ustring.h> // platform ICU
168
169 #ifndef USER_PROCESSES
170 #include <memalloc.h>
171 #endif
172
173 #if ENABLE(ACCELERATED_2D_CANVAS)
174 #include "GrContext.h"
175 #include "SharedGraphicsContext3D.h"
176 #endif
177
178 #if ENABLE(REQUEST_ANIMATION_FRAME)
179 #include "PlatformScreen.h"
180 #endif
181
182 #define DEBUG_BLOCK_ZOOM 0
183 #define DEBUG_TOUCH_EVENTS 0
184 #define DEBUG_WEBPAGE_LOAD 0
185 #define DEBUG_AC_COMMIT 0
186
187 using namespace std;
188 using namespace WebCore;
189
190 typedef const unsigned short* CUShortPtr;
191
192 namespace BlackBerry {
193 namespace WebKit {
194
195 static Vector<WebPage*>* visibleWebPages()
196 {
197     static Vector<WebPage*>* s_visibleWebPages = 0; // Initially, no web page is visible.
198     if (!s_visibleWebPages)
199         s_visibleWebPages = new Vector<WebPage*>;
200     return s_visibleWebPages;
201 }
202
203 const unsigned blockZoomMargin = 3; // Add 3 pixel margin on each side.
204 static int blockClickRadius = 0;
205 static double maximumBlockZoomScale = 3; // This scale can be clamped by the maximumScale set for the page.
206
207 const double manualScrollInterval = 0.1; // The time interval during which we associate user action with scrolling.
208
209 const double delayedZoomInterval = 0;
210
211 const IntSize minimumLayoutSize(10, 10); // Needs to be a small size, greater than 0, that we can grow the layout from.
212
213 const double minimumExpandingRatio = 0.15;
214
215 const double minimumZoomToFitScale = 0.25;
216
217 // Helper function to parse a URL and fill in missing parts.
218 static KURL parseUrl(const String& url)
219 {
220     String urlString(url);
221     KURL kurl = KURL(KURL(), urlString);
222     if (kurl.protocol().isEmpty()) {
223         urlString.insert("http://", 0);
224         kurl = KURL(KURL(), urlString);
225     }
226
227     return kurl;
228 }
229
230 // Helper functions to convert to and from WebCore types.
231 static inline WebCore::PlatformEvent::Type toWebCoreMouseEventType(const BlackBerry::Platform::MouseEvent::Type type)
232 {
233     switch (type) {
234     case BlackBerry::Platform::MouseEvent::MouseButtonDown:
235         return WebCore::PlatformEvent::MousePressed;
236     case Platform::MouseEvent::MouseButtonUp:
237         return WebCore::PlatformEvent::MouseReleased;
238     case Platform::MouseEvent::MouseMove:
239     default:
240         return WebCore::PlatformEvent::MouseMoved;
241     }
242 }
243
244 static inline ResourceRequestCachePolicy toWebCoreCachePolicy(Platform::NetworkRequest::CachePolicy policy)
245 {
246     switch (policy) {
247     case Platform::NetworkRequest::UseProtocolCachePolicy:
248         return UseProtocolCachePolicy;
249     case Platform::NetworkRequest::ReloadIgnoringCacheData:
250         return ReloadIgnoringCacheData;
251     case Platform::NetworkRequest::ReturnCacheDataElseLoad:
252         return ReturnCacheDataElseLoad;
253     case Platform::NetworkRequest::ReturnCacheDataDontLoad:
254         return ReturnCacheDataDontLoad;
255     default:
256         ASSERT_NOT_REACHED();
257         return UseProtocolCachePolicy;
258     }
259 }
260
261 #if ENABLE(EVENT_MODE_METATAGS)
262 static inline Platform::CursorEventMode toPlatformCursorEventMode(CursorEventMode mode)
263 {
264     switch (mode) {
265     case ProcessedCursorEvents:
266         return Platform::ProcessedCursorEvents;
267     case NativeCursorEvents:
268         return Platform::NativeCursorEvents;
269     default:
270         ASSERT_NOT_REACHED();
271         return Platform::ProcessedCursorEvents;
272     }
273 }
274
275 static inline Platform::TouchEventMode toPlatformTouchEventMode(TouchEventMode mode)
276 {
277     switch (mode) {
278     case ProcessedTouchEvents:
279         return Platform::ProcessedTouchEvents;
280     case NativeTouchEvents:
281         return Platform::NativeTouchEvents;
282     case PureTouchEventsWithMouseConversion:
283         return Platform::PureTouchEventsWithMouseConversion;
284     default:
285         ASSERT_NOT_REACHED();
286         return Platform::ProcessedTouchEvents;
287     }
288 }
289 #endif
290
291 static inline HistoryItem* historyItemFromBackForwardId(WebPage::BackForwardId id)
292 {
293     return reinterpret_cast<HistoryItem*>(id);
294 }
295
296 static inline WebPage::BackForwardId backForwardIdFromHistoryItem(HistoryItem* item)
297 {
298     return reinterpret_cast<WebPage::BackForwardId>(item);
299 }
300
301 void WebPage::setUserViewportArguments(const WebViewportArguments& viewportArguments)
302 {
303     d->m_userViewportArguments = *(viewportArguments.d);
304 }
305
306 void WebPage::resetUserViewportArguments()
307 {
308     d->m_userViewportArguments = ViewportArguments();
309 }
310
311 template <bool WebPagePrivate::* isActive>
312 class DeferredTask: public WebPagePrivate::DeferredTaskBase {
313 public:
314     static void finishOrCancel(WebPagePrivate* webPagePrivate)
315     {
316         webPagePrivate->*isActive = false;
317     }
318 protected:
319     DeferredTask(WebPagePrivate* webPagePrivate)
320         : DeferredTaskBase(webPagePrivate, isActive)
321     {
322     }
323     typedef DeferredTask<isActive> DeferredTaskType;
324 };
325
326 void WebPage::autofillTextField(const string& item)
327 {
328     if (!d->m_webSettings->isFormAutofillEnabled())
329         return;
330
331     d->m_autofillManager->autofillTextField(item.c_str());
332 }
333
334 WebPagePrivate::WebPagePrivate(WebPage* webPage, WebPageClient* client, const IntRect& rect)
335     : m_webPage(webPage)
336     , m_client(client)
337     , m_inspectorClient(0)
338     , m_page(0) // Initialized by init.
339     , m_mainFrame(0) // Initialized by init.
340     , m_currentContextNode(0)
341     , m_webSettings(0) // Initialized by init.
342     , m_visible(false)
343     , m_activationState(ActivationActive)
344     , m_shouldResetTilesWhenShown(false)
345     , m_shouldZoomToInitialScaleAfterLoadFinished(false)
346     , m_userScalable(true)
347     , m_userPerformedManualZoom(false)
348     , m_userPerformedManualScroll(false)
349     , m_contentsSizeChanged(false)
350     , m_overflowExceedsContentsSize(false)
351     , m_resetVirtualViewportOnCommitted(true)
352     , m_shouldUseFixedDesktopMode(false)
353     , m_needTouchEvents(false)
354     , m_preventIdleDimmingCount(0)
355 #if ENABLE(TOUCH_EVENTS)
356     , m_preventDefaultOnTouchStart(false)
357 #endif
358     , m_nestedLayoutFinishedCount(0)
359     , m_actualVisibleWidth(rect.width())
360     , m_actualVisibleHeight(rect.height())
361     , m_virtualViewportWidth(0)
362     , m_virtualViewportHeight(0)
363     , m_defaultLayoutSize(minimumLayoutSize)
364     , m_didRestoreFromPageCache(false)
365     , m_viewMode(WebPagePrivate::Desktop) // Default to Desktop mode for PB.
366     , m_loadState(WebPagePrivate::None)
367     , m_transformationMatrix(new TransformationMatrix())
368     , m_backingStore(0) // Initialized by init.
369     , m_backingStoreClient(0) // Initialized by init.
370     , m_inPageSearchManager(new InPageSearchManager(this))
371     , m_inputHandler(new InputHandler(this))
372     , m_selectionHandler(new SelectionHandler(this))
373     , m_touchEventHandler(new TouchEventHandler(this))
374 #if ENABLE(EVENT_MODE_METATAGS)
375     , m_cursorEventMode(ProcessedCursorEvents)
376     , m_touchEventMode(ProcessedTouchEvents)
377 #endif
378 #if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO)
379     , m_scaleBeforeFullScreen(-1.0)
380     , m_xScrollOffsetBeforeFullScreen(-1)
381 #endif
382     , m_currentCursor(Platform::CursorNone)
383     , m_dumpRenderTree(0) // Lazy initialization.
384     , m_initialScale(-1.0)
385     , m_minimumScale(-1.0)
386     , m_maximumScale(-1.0)
387     , m_blockZoomFinalScale(1.0)
388     , m_anchorInNodeRectRatio(-1, -1)
389     , m_currentBlockZoomNode(0)
390     , m_currentBlockZoomAdjustedNode(0)
391     , m_shouldReflowBlock(false)
392     , m_delayedZoomTimer(adoptPtr(new Timer<WebPagePrivate>(this, &WebPagePrivate::zoomAboutPointTimerFired)))
393     , m_lastUserEventTimestamp(0.0)
394     , m_pluginMouseButtonPressed(false)
395     , m_pluginMayOpenNewTab(false)
396 #if USE(ACCELERATED_COMPOSITING)
397     , m_rootLayerCommitTimer(adoptPtr(new Timer<WebPagePrivate>(this, &WebPagePrivate::rootLayerCommitTimerFired)))
398     , m_needsOneShotDrawingSynchronization(false)
399     , m_needsCommit(false)
400     , m_suspendRootLayerCommit(false)
401 #endif
402     , m_hasPendingSurfaceSizeChange(false)
403     , m_pendingOrientation(-1)
404     , m_fullscreenVideoNode(0)
405     , m_hasInRegionScrollableAreas(false)
406     , m_updateDelegatedOverlaysDispatched(false)
407     , m_deferredTasksTimer(this, &WebPagePrivate::deferredTasksTimerFired)
408     , m_selectPopup(0)
409     , m_autofillManager(AutofillManager::create(this))
410 {
411     static bool isInitialized = false;
412     if (!isInitialized) {
413         isInitialized = true;
414         BlackBerry::Platform::DeviceInfo::instance();
415         defaultUserAgent();
416     }
417 }
418
419 WebPage::WebPage(WebPageClient* client, const WebString& pageGroupName, const Platform::IntRect& rect)
420 {
421     globalInitialize();
422     d = new WebPagePrivate(this, client, rect);
423     d->init(pageGroupName);
424 }
425
426 WebPagePrivate::~WebPagePrivate()
427 {
428     // Hand the backingstore back to another owner if necessary.
429     m_webPage->setVisible(false);
430     if (BackingStorePrivate::currentBackingStoreOwner() == m_webPage)
431         BackingStorePrivate::setCurrentBackingStoreOwner(0);
432
433     delete m_webSettings;
434     m_webSettings = 0;
435
436     delete m_backingStoreClient;
437     m_backingStoreClient = 0;
438     m_backingStore = 0;
439
440     delete m_page;
441     m_page = 0;
442
443     delete m_transformationMatrix;
444     m_transformationMatrix = 0;
445
446     delete m_inPageSearchManager;
447     m_inPageSearchManager = 0;
448
449     delete m_selectionHandler;
450     m_selectionHandler = 0;
451
452     delete m_inputHandler;
453     m_inputHandler = 0;
454
455     delete m_touchEventHandler;
456     m_touchEventHandler = 0;
457
458 #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD
459     delete m_dumpRenderTree;
460     m_dumpRenderTree = 0;
461 #endif
462
463 #if USE(ACCELERATED_COMPOSITING)
464     deleteGuardedObject(m_selectionOverlay);
465     m_selectionOverlay = 0;
466 #endif
467 }
468
469 WebPage::~WebPage()
470 {
471     deleteGuardedObject(d);
472     d = 0;
473 }
474
475 Page* WebPagePrivate::core(const WebPage* webPage)
476 {
477     return webPage->d->m_page;
478 }
479
480 void WebPagePrivate::init(const WebString& pageGroupName)
481 {
482     ChromeClientBlackBerry* chromeClient = new ChromeClientBlackBerry(this);
483     ContextMenuClientBlackBerry* contextMenuClient = 0;
484 #if ENABLE(CONTEXT_MENUS)
485     contextMenuClient = new ContextMenuClientBlackBerry();
486 #endif
487     EditorClientBlackBerry* editorClient = new EditorClientBlackBerry(this);
488     DragClientBlackBerry* dragClient = 0;
489 #if ENABLE(DRAG_SUPPORT)
490     dragClient = new DragClientBlackBerry();
491 #endif
492 #if ENABLE(INSPECTOR)
493     m_inspectorClient = new InspectorClientBlackBerry(this);
494 #endif
495
496     FrameLoaderClientBlackBerry* frameLoaderClient = new FrameLoaderClientBlackBerry();
497
498     Page::PageClients pageClients;
499     pageClients.chromeClient = chromeClient;
500     pageClients.contextMenuClient = contextMenuClient;
501     pageClients.editorClient = editorClient;
502     pageClients.dragClient = dragClient;
503     pageClients.inspectorClient = m_inspectorClient;
504
505     m_page = new Page(pageClients);
506 #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD
507     if (getenv("drtRun")) {
508         // In case running in DumpRenderTree mode set the controller to mock provider.
509         GeolocationClientMock* mock = new GeolocationClientMock();
510         WebCore::provideGeolocationTo(m_page, mock);
511         mock->setController(WebCore::GeolocationController::from(m_page));
512     } else
513 #else
514         WebCore::provideGeolocationTo(m_page, new GeolocationControllerClientBlackBerry(this));
515 #endif
516     WebCore::provideDeviceOrientationTo(m_page, new DeviceOrientationClientBlackBerry(this));
517     WebCore::provideDeviceMotionTo(m_page, new DeviceMotionClientBlackBerry(this));
518 #if ENABLE(VIBRATION)
519     WebCore::provideVibrationTo(m_page, new VibrationClientBlackBerry());
520 #endif
521
522 #if ENABLE(BATTERY_STATUS)
523     WebCore::provideBatteryTo(m_page, new WebCore::BatteryClientBlackBerry(this));
524 #endif
525
526 #if ENABLE(MEDIA_STREAM)
527     WebCore::provideUserMediaTo(m_page, new UserMediaClientImpl(m_webPage));
528 #endif
529
530 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
531     WebCore::provideNotification(m_page, NotificationPresenterImpl::instance());
532 #endif
533
534     m_page->setCustomHTMLTokenizerChunkSize(256);
535     m_page->setCustomHTMLTokenizerTimeDelay(0.3);
536
537     m_webSettings = WebSettings::createFromStandardSettings();
538     m_webSettings->setUserAgentString(defaultUserAgent());
539     m_page->setDeviceScaleFactor(m_webSettings->devicePixelRatio());
540
541 #if USE(ACCELERATED_COMPOSITING)
542     m_tapHighlight = DefaultTapHighlight::create(this);
543     m_selectionOverlay = SelectionOverlay::create(this);
544     m_page->settings()->setAcceleratedCompositingForFixedPositionEnabled(true);
545 #endif
546
547     // FIXME: We explicitly call setDelegate() instead of passing ourself in createFromStandardSettings()
548     // so that we only get one didChangeSettings() callback when we set the page group name. This causes us
549     // to make a copy of the WebSettings since some WebSettings method make use of the page group name.
550     // Instead, we shouldn't be storing the page group name in WebSettings.
551     m_webSettings->setPageGroupName(pageGroupName);
552     m_webSettings->setDelegate(this);
553     didChangeSettings(m_webSettings);
554
555     RefPtr<Frame> newFrame = Frame::create(m_page, /* HTMLFrameOwnerElement* */ 0, frameLoaderClient);
556
557     m_mainFrame = newFrame.get();
558     frameLoaderClient->setFrame(m_mainFrame, this);
559     m_mainFrame->init();
560
561     m_inRegionScroller = adoptPtr(new InRegionScroller(this));
562
563 #if ENABLE(WEBGL)
564     Platform::Settings* settings = Platform::Settings::instance();
565     m_page->settings()->setWebGLEnabled(settings && settings->isWebGLSupported());
566 #endif
567 #if ENABLE(ACCELERATED_2D_CANVAS)
568     m_page->settings()->setCanvasUsesAcceleratedDrawing(true);
569     m_page->settings()->setAccelerated2dCanvasEnabled(true);
570 #endif
571
572     m_page->settings()->setInteractiveFormValidationEnabled(true);
573     m_page->settings()->setAllowUniversalAccessFromFileURLs(false);
574     m_page->settings()->setAllowFileAccessFromFileURLs(false);
575
576     m_backingStoreClient = BackingStoreClient::create(m_mainFrame, /* parent frame */ 0, m_webPage);
577     // The direct access to BackingStore is left here for convenience since it
578     // is owned by BackingStoreClient and then deleted by its destructor.
579     m_backingStore = m_backingStoreClient->backingStore();
580
581     blockClickRadius = int(roundf(0.35 * Platform::Graphics::Screen::primaryScreen()->pixelsPerInch(0).width())); // The clicked rectangle area should be a fixed unit of measurement.
582
583     m_page->settings()->setDelegateSelectionPaint(true);
584
585 #if ENABLE(REQUEST_ANIMATION_FRAME)
586     m_page->windowScreenDidChange((PlatformDisplayID)0);
587 #endif
588
589 #if ENABLE(WEB_TIMING)
590     m_page->settings()->setMemoryInfoEnabled(true);
591 #endif
592
593 #if USE(ACCELERATED_COMPOSITING)
594     // The compositor will be needed for overlay rendering, so create it
595     // unconditionally. It will allocate OpenGL objects lazily, so this incurs
596     // no overhead in the unlikely case where the compositor is not needed.
597     Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
598             createMethodCallMessage(&WebPagePrivate::createCompositor, this));
599 #endif
600     m_page->settings()->setDNSPrefetchingEnabled(true);
601 }
602
603 class DeferredTaskLoadManualScript: public DeferredTask<&WebPagePrivate::m_wouldLoadManualScript> {
604 public:
605     explicit DeferredTaskLoadManualScript(WebPagePrivate* webPagePrivate, const KURL& url)
606         : DeferredTaskType(webPagePrivate)
607     {
608         webPagePrivate->m_cachedManualScript = url;
609     }
610 private:
611     virtual void performInternal(WebPagePrivate* webPagePrivate)
612     {
613         webPagePrivate->m_mainFrame->script()->executeIfJavaScriptURL(webPagePrivate->m_cachedManualScript, DoNotReplaceDocumentIfJavaScriptURL);
614     }
615 };
616
617 void WebPagePrivate::load(const char* url, const char* networkToken, const char* method, Platform::NetworkRequest::CachePolicy cachePolicy, const char* data, size_t dataLength, const char* const* headers, size_t headersLength, bool isInitial, bool mustHandleInternally, bool forceDownload, const char* overrideContentType, const char* suggestedSaveName)
618 {
619     stopCurrentLoad();
620     DeferredTaskLoadManualScript::finishOrCancel(this);
621
622     String urlString(url);
623     if (urlString.startsWith("vs:", false)) {
624         urlString = urlString.substring(3);
625         m_mainFrame->setInViewSourceMode(true);
626     } else
627         m_mainFrame->setInViewSourceMode(false);
628
629     KURL kurl = parseUrl(urlString);
630     if (protocolIs(kurl, "javascript")) {
631         // Never run javascript while loading is deferred.
632         if (m_page->defersLoading())
633             m_deferredTasks.append(adoptPtr(new DeferredTaskLoadManualScript(this, kurl)));
634         else
635             m_mainFrame->script()->executeIfJavaScriptURL(kurl, DoNotReplaceDocumentIfJavaScriptURL);
636         return;
637     }
638
639     if (isInitial)
640         NetworkManager::instance()->setInitialURL(kurl);
641
642     ResourceRequest request(kurl);
643     request.setToken(networkToken);
644     if (isInitial || mustHandleInternally)
645         request.setMustHandleInternally(true);
646     request.setHTTPMethod(method);
647     request.setCachePolicy(toWebCoreCachePolicy(cachePolicy));
648     if (overrideContentType)
649         request.setOverrideContentType(overrideContentType);
650
651     if (data)
652         request.setHTTPBody(FormData::create(data, dataLength));
653
654     for (unsigned i = 0; i + 1 < headersLength; i += 2)
655         request.addHTTPHeaderField(headers[i], headers[i + 1]);
656
657     if (forceDownload)
658         request.setForceDownload(true);
659
660     request.setSuggestedSaveName(suggestedSaveName);
661
662     m_mainFrame->loader()->load(request, "" /* name */, false);
663 }
664
665 void WebPage::load(const char* url, const char* networkToken, bool isInitial)
666 {
667     d->load(url, networkToken, "GET", Platform::NetworkRequest::UseProtocolCachePolicy, 0, 0, 0, 0, isInitial, false);
668 }
669
670 void WebPage::loadExtended(const char* url, const char* networkToken, const char* method, Platform::NetworkRequest::CachePolicy cachePolicy, const char* data, size_t dataLength, const char* const* headers, size_t headersLength, bool mustHandleInternally)
671 {
672     d->load(url, networkToken, method, cachePolicy, data, dataLength, headers, headersLength, false, mustHandleInternally, false, "");
673 }
674
675 void WebPage::loadFile(const char* path, const char* overrideContentType)
676 {
677     std::string fileUrl(path);
678     if (!fileUrl.find("/"))
679         fileUrl.insert(0, "file://");
680     else if (fileUrl.find("file:///"))
681         return;
682
683     d->load(fileUrl.c_str(), 0, "GET", Platform::NetworkRequest::UseProtocolCachePolicy, 0, 0, 0, 0, false, false, false, overrideContentType);
684 }
685
686 void WebPage::download(const Platform::NetworkRequest& request)
687 {
688     vector<const char*> headers;
689     Platform::NetworkRequest::HeaderList& list = request.getHeaderListRef();
690     for (unsigned i = 0; i < list.size(); i++) {
691         headers.push_back(list[i].first.c_str());
692         headers.push_back(list[i].second.c_str());
693     }
694     d->load(request.getUrlRef().c_str(), 0, "GET", Platform::NetworkRequest::UseProtocolCachePolicy, 0, 0, headers.empty() ? 0 : &headers[0], headers.size(), false, false, true, "", request.getSuggestedSaveName().c_str());
695 }
696
697 void WebPagePrivate::loadString(const char* string, const char* baseURL, const char* contentType, const char* failingURL)
698 {
699     KURL kurl = parseUrl(baseURL);
700     ResourceRequest request(kurl);
701     WTF::RefPtr<SharedBuffer> buffer
702         = SharedBuffer::create(string, strlen(string));
703     SubstituteData substituteData(buffer,
704                                   extractMIMETypeFromMediaType(contentType),
705                                   extractCharsetFromMediaType(contentType),
706                                   failingURL ? parseUrl(failingURL) : KURL());
707     m_mainFrame->loader()->load(request, substituteData, false);
708 }
709
710 void WebPage::loadString(const char* string, const char* baseURL, const char* mimeType, const char* failingURL)
711 {
712     d->loadString(string, baseURL, mimeType, failingURL);
713 }
714
715 bool WebPagePrivate::executeJavaScript(const char* scriptUTF8, JavaScriptDataType& returnType, WebString& returnValue)
716 {
717     String script = String::fromUTF8(scriptUTF8);
718
719     if (script.isNull()) {
720         returnType = JSException;
721         return false;
722     }
723
724     if (script.isEmpty()) {
725         returnType = JSUndefined;
726         return true;
727     }
728
729     ScriptValue result = m_mainFrame->script()->executeScript(script, false);
730     JSC::JSValue value = result.jsValue();
731     if (!value) {
732         returnType = JSException;
733         return false;
734     }
735
736     JSC::ExecState* exec = m_mainFrame->script()->globalObject(mainThreadNormalWorld())->globalExec();
737     JSGlobalContextRef context = toGlobalRef(exec);
738
739     JSType type = JSValueGetType(context, toRef(exec, value));
740
741     switch (type) {
742     case kJSTypeNull:
743         returnType = JSNull;
744         break;
745     case kJSTypeBoolean:
746         returnType = JSBoolean;
747         break;
748     case kJSTypeNumber:
749         returnType = JSNumber;
750         break;
751     case kJSTypeString:
752         returnType = JSString;
753         break;
754     case kJSTypeObject:
755         returnType = JSObject;
756         break;
757     case kJSTypeUndefined:
758     default:
759         returnType = JSUndefined;
760         break;
761     }
762
763     if (returnType == JSBoolean || returnType == JSNumber || returnType == JSString || returnType == JSObject) {
764         String str = result.toString(exec);
765         returnValue = WebString(str.impl());
766     }
767
768     return true;
769 }
770
771 bool WebPage::executeJavaScript(const char* script, JavaScriptDataType& returnType, WebString& returnValue)
772 {
773     return d->executeJavaScript(script, returnType, returnValue);
774 }
775
776 bool WebPagePrivate::executeJavaScriptInIsolatedWorld(const ScriptSourceCode& sourceCode, JavaScriptDataType& returnType, WebString& returnValue)
777 {
778     if (!m_isolatedWorld)
779         m_isolatedWorld = m_mainFrame->script()->createWorld();
780
781     // Use evaluateInWorld to avoid canExecuteScripts check.
782     ScriptValue result = m_mainFrame->script()->evaluateInWorld(sourceCode, m_isolatedWorld.get());
783     JSC::JSValue value = result.jsValue();
784     if (!value) {
785         returnType = JSException;
786         return false;
787     }
788
789     JSC::ExecState* exec = m_mainFrame->script()->globalObject(m_isolatedWorld.get())->globalExec();
790     JSGlobalContextRef context = toGlobalRef(exec);
791
792     JSType type = JSValueGetType(context, toRef(exec, value));
793
794     switch (type) {
795     case kJSTypeNull:
796         returnType = JSNull;
797         break;
798     case kJSTypeBoolean:
799         returnType = JSBoolean;
800         break;
801     case kJSTypeNumber:
802         returnType = JSNumber;
803         break;
804     case kJSTypeString:
805         returnType = JSString;
806         break;
807     case kJSTypeObject:
808         returnType = JSObject;
809         break;
810     case kJSTypeUndefined:
811     default:
812         returnType = JSUndefined;
813         break;
814     }
815
816     if (returnType == JSBoolean || returnType == JSNumber || returnType == JSString || returnType == JSObject) {
817         String str = result.toString(exec);
818         returnValue = WebString(str.impl());
819     }
820
821     return true;
822 }
823
824 bool WebPage::executeJavaScriptInIsolatedWorld(const std::wstring& script, JavaScriptDataType& returnType, WebString& returnValue)
825 {
826     // On our platform wchar_t is unsigned int and UChar is unsigned short
827     // so we have to convert using ICU conversion function
828     int lengthCopied = 0;
829     UErrorCode error = U_ZERO_ERROR;
830     const int length = script.length() + 1 /*null termination char*/;
831     UChar data[length];
832
833     // FIXME: PR 138162 is giving U_INVALID_CHAR_FOUND error.
834     u_strFromUTF32(data, length, &lengthCopied, reinterpret_cast<const UChar32*>(script.c_str()), script.length(), &error);
835     BLACKBERRY_ASSERT(error == U_ZERO_ERROR);
836     if (error != U_ZERO_ERROR) {
837         Platform::logAlways(Platform::LogLevelCritical, "WebPage::executeJavaScriptInIsolatedWorld failed to convert UTF16 to JavaScript!");
838         return false;
839     }
840     String str = String(data, lengthCopied);
841     ScriptSourceCode sourceCode(str, KURL());
842     return d->executeJavaScriptInIsolatedWorld(sourceCode, returnType, returnValue);
843 }
844
845 bool WebPage::executeJavaScriptInIsolatedWorld(const char* script, JavaScriptDataType& returnType, WebString& returnValue)
846 {
847     ScriptSourceCode sourceCode(String::fromUTF8(script), KURL());
848     return d->executeJavaScriptInIsolatedWorld(sourceCode, returnType, returnValue);
849 }
850
851 void WebPage::executeJavaScriptFunction(const std::vector<std::string> &function, const std::vector<JavaScriptVariant> &args, JavaScriptVariant& returnValue)
852 {
853     if (!d->m_mainFrame) {
854         returnValue.setType(JavaScriptVariant::Exception);
855         return;
856     }
857
858     JSC::Bindings::RootObject* root = d->m_mainFrame->script()->bindingRootObject();
859     if (!root) {
860         returnValue.setType(JavaScriptVariant::Exception);
861         return;
862     }
863
864     JSC::ExecState* exec = root->globalObject()->globalExec();
865     JSGlobalContextRef ctx = toGlobalRef(exec);
866
867     WTF::Vector<JSValueRef> argListRef(args.size());
868     for (unsigned i = 0; i < args.size(); ++i)
869         argListRef[i] = BlackBerryJavaScriptVariantToJSValueRef(ctx, args[i]);
870
871     JSValueRef windowObjectValue = windowObject();
872     JSObjectRef obj = JSValueToObject(ctx, windowObjectValue, 0);
873     JSObjectRef thisObject = obj;
874     for (unsigned i = 0; i < function.size(); ++i) {
875         JSStringRef str = JSStringCreateWithUTF8CString(function[i].c_str());
876         thisObject = obj;
877         obj = JSValueToObject(ctx, JSObjectGetProperty(ctx, obj, str, 0), 0);
878         JSStringRelease(str);
879         if (!obj)
880             break;
881     }
882
883     JSObjectRef functionObject = obj;
884     JSValueRef result = 0;
885     if (functionObject && thisObject)
886         result = JSObjectCallAsFunction(ctx, functionObject, thisObject, args.size(), argListRef.data(), 0);
887
888     if (!result) {
889         returnValue.setType(JavaScriptVariant::Exception);
890         return;
891     }
892
893     returnValue = JSValueRefToBlackBerryJavaScriptVariant(ctx, result);
894 }
895
896 void WebPagePrivate::stopCurrentLoad()
897 {
898     // This function should contain all common code triggered by WebPage::load
899     // (which stops any load in progress before starting the new load) and
900     // WebPage::stoploading (the entry point for the client to stop the load
901     // explicitly). If it should only be done while stopping the load
902     // explicitly, it goes in WebPage::stopLoading, not here.
903     m_mainFrame->loader()->stopAllLoaders();
904
905     // Cancel any deferred script that hasn't been processed yet.
906     DeferredTaskLoadManualScript::finishOrCancel(this);
907 }
908
909 void WebPage::stopLoading()
910 {
911     d->stopCurrentLoad();
912 }
913
914 static void closeURLRecursively(Frame* frame)
915 {
916     // Do not create more frame please.
917     FrameLoaderClientBlackBerry* frameLoaderClient = static_cast<FrameLoaderClientBlackBerry*>(frame->loader()->client());
918     frameLoaderClient->suppressChildFrameCreation();
919
920     frame->loader()->closeURL();
921
922     Vector<RefPtr<Frame>, 10> childFrames;
923
924     for (RefPtr<Frame> childFrame = frame->tree()->firstChild(); childFrame; childFrame = childFrame->tree()->nextSibling())
925         childFrames.append(childFrame);
926
927     unsigned size = childFrames.size();
928     for (unsigned i = 0; i < size; i++)
929         closeURLRecursively(childFrames[i].get());
930 }
931
932 void WebPagePrivate::prepareToDestroy()
933 {
934     // Before the client starts tearing itself down, dispatch the unload event
935     // so it can take effect while all the client's state (e.g. scroll position)
936     // is still present.
937     closeURLRecursively(m_mainFrame);
938 }
939
940 void WebPage::prepareToDestroy()
941 {
942     d->prepareToDestroy();
943 }
944
945 bool WebPage::dispatchBeforeUnloadEvent()
946 {
947     return d->m_page->mainFrame()->loader()->shouldClose();
948 }
949
950 static void enableCrossSiteXHRRecursively(Frame* frame)
951 {
952     frame->document()->securityOrigin()->grantUniversalAccess();
953
954     Vector<RefPtr<Frame>, 10> childFrames;
955     for (RefPtr<Frame> childFrame = frame->tree()->firstChild(); childFrame; childFrame = childFrame->tree()->nextSibling())
956         childFrames.append(childFrame);
957
958     unsigned size = childFrames.size();
959     for (unsigned i = 0; i < size; i++)
960         enableCrossSiteXHRRecursively(childFrames[i].get());
961 }
962
963 void WebPagePrivate::enableCrossSiteXHR()
964 {
965     enableCrossSiteXHRRecursively(m_mainFrame);
966 }
967
968 void WebPage::enableCrossSiteXHR()
969 {
970     d->enableCrossSiteXHR();
971 }
972
973 void WebPagePrivate::addOriginAccessWhitelistEntry(const char* sourceOrigin, const char* destinationOrigin, bool allowDestinationSubdomains)
974 {
975     RefPtr<SecurityOrigin> source = SecurityOrigin::createFromString(sourceOrigin);
976     if (source->isUnique())
977         return;
978
979     KURL destination(KURL(), destinationOrigin);
980     SecurityPolicy::addOriginAccessWhitelistEntry(*source, destination.protocol(), destination.host(), allowDestinationSubdomains);
981 }
982
983 void WebPage::addOriginAccessWhitelistEntry(const char* sourceOrigin, const char* destinationOrigin, bool allowDestinationSubdomains)
984 {
985     d->addOriginAccessWhitelistEntry(sourceOrigin, destinationOrigin, allowDestinationSubdomains);
986 }
987
988 void WebPagePrivate::removeOriginAccessWhitelistEntry(const char* sourceOrigin, const char* destinationOrigin, bool allowDestinationSubdomains)
989 {
990     RefPtr<SecurityOrigin> source = SecurityOrigin::createFromString(sourceOrigin);
991     if (source->isUnique())
992         return;
993
994     KURL destination(KURL(), destinationOrigin);
995     SecurityPolicy::removeOriginAccessWhitelistEntry(*source, destination.protocol(), destination.host(), allowDestinationSubdomains);
996 }
997
998 void WebPage::removeOriginAccessWhitelistEntry(const char* sourceOrigin, const char* destinationOrigin, bool allowDestinationSubdomains)
999 {
1000     d->removeOriginAccessWhitelistEntry(sourceOrigin, destinationOrigin, allowDestinationSubdomains);
1001 }
1002
1003 void WebPagePrivate::setLoadState(LoadState state)
1004 {
1005     if (m_loadState == state)
1006         return;
1007
1008     bool isFirstLoad = m_loadState == None;
1009
1010     // See RIM Bug #1068.
1011     if (state == Finished && m_mainFrame && m_mainFrame->document())
1012         m_mainFrame->document()->updateStyleIfNeeded();
1013
1014     m_loadState = state;
1015
1016 #if DEBUG_WEBPAGE_LOAD
1017     Platform::log(Platform::LogLevelInfo, "WebPagePrivate::setLoadState %d", state);
1018 #endif
1019
1020     switch (m_loadState) {
1021     case Provisional:
1022         if (isFirstLoad) {
1023             // Paints the visible backingstore as white to prevent initial checkerboard on
1024             // the first blit.
1025             if (m_backingStore->d->renderVisibleContents() && !m_backingStore->d->isSuspended() && !m_backingStore->d->shouldDirectRenderingToWindow())
1026                 m_backingStore->d->blitVisibleContents();
1027         }
1028         break;
1029     case Committed:
1030         {
1031             unscheduleZoomAboutPoint();
1032
1033 #if ENABLE(ACCELERATED_2D_CANVAS)
1034             if (m_page->settings()->canvasUsesAcceleratedDrawing()) {
1035                 // Free GPU resources as we're on a new page.
1036                 // This will help us to free memory pressure.
1037                 SharedGraphicsContext3D::get()->makeContextCurrent();
1038                 GrContext* grContext = Platform::Graphics::getGrContext();
1039                 grContext->freeGpuResources();
1040             }
1041 #endif
1042
1043 #if USE(ACCELERATED_COMPOSITING)
1044             releaseLayerResources();
1045 #endif
1046
1047             // Suspend screen update to avoid ui thread blitting while resetting backingstore.
1048             m_backingStore->d->suspendScreenAndBackingStoreUpdates();
1049
1050             m_previousContentsSize = IntSize();
1051             m_backingStore->d->resetRenderQueue();
1052             m_backingStore->d->resetTiles(true /* resetBackground */);
1053             m_backingStore->d->setScrollingOrZooming(false, false /* shouldBlit */);
1054             m_shouldZoomToInitialScaleAfterLoadFinished = false;
1055             m_userPerformedManualZoom = false;
1056             m_userPerformedManualScroll = false;
1057             m_shouldUseFixedDesktopMode = false;
1058             if (m_resetVirtualViewportOnCommitted) { // For DRT.
1059                 m_virtualViewportWidth = 0;
1060                 m_virtualViewportHeight = 0;
1061             }
1062             if (m_webSettings->viewportWidth() > 0) {
1063                 m_virtualViewportWidth = m_webSettings->viewportWidth();
1064                 m_virtualViewportHeight = m_defaultLayoutSize.height();
1065             }
1066             // Check if we have already process the meta viewport tag, this only happens on history navigation.
1067             // For back/forward history navigation, we should only keep these previous values if the document
1068             // has the meta viewport tag when the state is Committed in setLoadState.
1069             // Refreshing should keep these previous values as well.
1070             static ViewportArguments defaultViewportArguments;
1071             bool documentHasViewportArguments = false;
1072             FrameLoadType frameLoadType = FrameLoadTypeStandard;
1073             if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->viewportArguments() != defaultViewportArguments)
1074                 documentHasViewportArguments = true;
1075             if (m_mainFrame && m_mainFrame->loader())
1076                 frameLoadType = m_mainFrame->loader()->loadType();
1077             if (!((m_didRestoreFromPageCache && documentHasViewportArguments) || (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin))) {
1078                 m_viewportArguments = ViewportArguments();
1079                 m_userScalable = m_webSettings->isUserScalable();
1080                 resetScales();
1081
1082                 // At the moment we commit a new load, set the viewport arguments
1083                 // to any fallback values. If there is a meta viewport in the
1084                 // content it will overwrite the fallback arguments soon.
1085                 dispatchViewportPropertiesDidChange(m_userViewportArguments);
1086             } else {
1087                 IntSize virtualViewport = recomputeVirtualViewportFromViewportArguments();
1088                 m_webPage->setVirtualViewportSize(virtualViewport.width(), virtualViewport.height());
1089             }
1090
1091 #if ENABLE(EVENT_MODE_METATAGS)
1092             didReceiveCursorEventMode(ProcessedCursorEvents);
1093             didReceiveTouchEventMode(ProcessedTouchEvents);
1094 #endif
1095
1096             // Reset block zoom and reflow.
1097             resetBlockZoom();
1098 #if ENABLE(VIEWPORT_REFLOW)
1099             toggleTextReflowIfEnabledForBlockZoomOnly();
1100 #endif
1101
1102             // Notify InputHandler of state change.
1103             m_inputHandler->setInputModeEnabled(false);
1104
1105             // Set the scroll to origin here and notify the client since we'll be
1106             // zooming below without any real contents yet thus the contents size
1107             // we report to the client could make our current scroll position invalid.
1108             setScrollPosition(IntPoint::zero());
1109             notifyTransformedScrollChanged();
1110
1111             m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::None);
1112
1113             // Paints the visible backingstore as white. Note it is important we do
1114             // this strictly after re-setting the scroll position to origin and resetting
1115             // the scales otherwise the visible contents calculation is wrong and we
1116             // can end up blitting artifacts instead. See: RIM Bug #401.
1117             if (m_backingStore->d->renderVisibleContents() && !m_backingStore->d->isSuspended() && !m_backingStore->d->shouldDirectRenderingToWindow())
1118                 m_backingStore->d->blitVisibleContents();
1119
1120             zoomToInitialScaleOnLoad();
1121
1122             // Update cursor status.
1123             updateCursor();
1124
1125 #if USE(ACCELERATED_COMPOSITING)
1126             // Don't render compositing contents from previous page.
1127             resetCompositingSurface();
1128 #endif
1129             break;
1130         }
1131     case Finished:
1132     case Failed:
1133         // Notify client of the initial zoom change.
1134         m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
1135         m_backingStore->d->updateTiles(true /* updateVisible */, false /* immediate */);
1136         break;
1137     default:
1138         break;
1139     }
1140 }
1141
1142 double WebPagePrivate::clampedScale(double scale) const
1143 {
1144     if (scale < minimumScale())
1145         return minimumScale();
1146     if (scale > maximumScale())
1147         return maximumScale();
1148     return scale;
1149 }
1150
1151 bool WebPagePrivate::shouldZoomAboutPoint(double scale, const FloatPoint&, bool enforceScaleClamping, double* clampedScale)
1152 {
1153     if (!m_mainFrame->view())
1154         return false;
1155
1156     if (enforceScaleClamping)
1157         scale = this->clampedScale(scale);
1158
1159     ASSERT(clampedScale);
1160     *clampedScale = scale;
1161
1162     if (currentScale() == scale) {
1163         m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
1164         return false;
1165     }
1166
1167     return true;
1168 }
1169
1170 bool 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::log(Platform::LogLevelInfo, "WebPagePrivate::zoomAboutPoint scale %f anchor (%f, %f)", scale, anchor.x(), anchor.y());
1200 #endif
1201
1202     // Our current scroll position in float.
1203     FloatPoint scrollPosition = this->scrollPosition();
1204
1205     // Anchor offset from scroll position in float.
1206     FloatPoint anchorOffset(anchor.x() - scrollPosition.x(), anchor.y() - scrollPosition.y());
1207
1208     // The horizontal scaling factor and vertical scaling factor should be equal
1209     // to preserve aspect ratio of content.
1210     ASSERT(m_transformationMatrix->m11() == m_transformationMatrix->m22());
1211
1212     // Need to invert the previous transform to anchor the viewport.
1213     double inverseScale = scale / m_transformationMatrix->m11();
1214
1215     // Actual zoom.
1216     *m_transformationMatrix = zoom;
1217
1218     // Suspend all screen updates to the backingstore.
1219     m_backingStore->d->suspendScreenAndBackingStoreUpdates();
1220
1221     updateViewportSize();
1222
1223     IntPoint newScrollPosition(IntPoint(max(0, static_cast<int>(roundf(anchor.x() - anchorOffset.x() / inverseScale))),
1224                                         max(0, static_cast<int>(roundf(anchor.y() - anchorOffset.y() / inverseScale)))));
1225
1226     if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled) {
1227         // This is a hack for email which has reflow always turned on.
1228         m_mainFrame->view()->setNeedsLayout();
1229         requestLayoutIfNeeded();
1230         if (m_currentPinchZoomNode)
1231             newScrollPosition = calculateReflowedScrollPosition(anchorOffset, scale == minimumScale() ? 1 : inverseScale);
1232          m_currentPinchZoomNode = 0;
1233          m_anchorInNodeRectRatio = FloatPoint(-1, -1);
1234     }
1235
1236     setScrollPosition(newScrollPosition);
1237
1238     notifyTransformChanged();
1239
1240     bool isLoading = this->isLoading();
1241
1242     // We need to invalidate all tiles both visible and non-visible if we're loading.
1243     m_backingStore->d->updateTiles(isLoading /* updateVisible */, false /* immediate */);
1244
1245     m_client->resetBitmapZoomScale(m_transformationMatrix->m11());
1246
1247     bool shouldRender = !isLoading || m_userPerformedManualZoom || forceRendering;
1248     bool shouldClearVisibleZoom = isLoading && shouldRender;
1249
1250     if (shouldClearVisibleZoom) {
1251         // If we are loading and rendering then we need to clear the render queue's
1252         // visible zoom jobs as they will be irrelevant with the render below.
1253         m_backingStore->d->clearVisibleZoom();
1254     }
1255
1256     // Clear window to make sure there are no artifacts.
1257     if (shouldRender) {
1258         // Resume all screen updates to the backingstore and render+blit visible contents to screen.
1259         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::RenderAndBlit);
1260     } else {
1261         // Resume all screen updates to the backingstore but do not blit to the screen because we not rendering.
1262         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::None);
1263     }
1264
1265     m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
1266
1267     return true;
1268 }
1269
1270 IntPoint WebPagePrivate::calculateReflowedScrollPosition(const FloatPoint& anchorOffset, double inverseScale)
1271 {
1272     // Should only be invoked when text reflow is enabled.
1273     ASSERT(m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled);
1274
1275     int offsetY = 0;
1276     int offsetX = 0;
1277
1278     IntRect nodeRect = rectForNode(m_currentPinchZoomNode.get());
1279
1280     if (m_currentPinchZoomNode->renderer() && m_anchorInNodeRectRatio.y() >= 0) {
1281         offsetY = nodeRect.height() * m_anchorInNodeRectRatio.y();
1282         if (m_currentPinchZoomNode->renderer()->isImage() && m_anchorInNodeRectRatio.x() > 0)
1283             offsetX = nodeRect.width() * m_anchorInNodeRectRatio.x() - anchorOffset.x() / inverseScale;
1284     }
1285
1286     IntRect reflowedRect = adjustRectOffsetForFrameOffset(nodeRect, m_currentPinchZoomNode.get());
1287
1288     return IntPoint(max(0, static_cast<int>(roundf(reflowedRect.x() + offsetX))),
1289                     max(0, static_cast<int>(roundf(reflowedRect.y() + offsetY - anchorOffset.y() / inverseScale))));
1290 }
1291
1292 bool WebPagePrivate::scheduleZoomAboutPoint(double unclampedScale, const FloatPoint& anchor, bool enforceScaleClamping, bool forceRendering)
1293 {
1294     double scale;
1295     if (!shouldZoomAboutPoint(unclampedScale, anchor, enforceScaleClamping, &scale)) {
1296         // We could be back to the right zoom level before the timer has
1297         // timed out, because of wiggling back and forth. Stop the timer.
1298         unscheduleZoomAboutPoint();
1299         return false;
1300     }
1301
1302     // For some reason, the bitmap zoom wants an anchor in backingstore coordinates!
1303     // this is different from zoomAboutPoint, which wants content coordinates.
1304     // See RIM Bug #641.
1305
1306     FloatPoint transformedAnchor = mapToTransformedFloatPoint(anchor);
1307     FloatPoint transformedScrollPosition = mapToTransformedFloatPoint(scrollPosition());
1308
1309     // Prohibit backingstore from updating the window overtop of the bitmap.
1310     m_backingStore->d->suspendScreenAndBackingStoreUpdates();
1311
1312     // Need to invert the previous transform to anchor the viewport.
1313     double zoomFraction = scale / transformationMatrix()->m11();
1314
1315     // Anchor offset from scroll position in float.
1316     FloatPoint anchorOffset(transformedAnchor.x() - transformedScrollPosition.x(),
1317                             transformedAnchor.y() - transformedScrollPosition.y());
1318
1319     IntPoint srcPoint(
1320         static_cast<int>(roundf(transformedAnchor.x() - anchorOffset.x() / zoomFraction)),
1321         static_cast<int>(roundf(transformedAnchor.y() - anchorOffset.y() / zoomFraction)));
1322
1323     const IntRect viewportRect = IntRect(IntPoint::zero(), transformedViewportSize());
1324     const IntRect dstRect = viewportRect;
1325
1326     // This is the rect to pass as the actual source rect in the backingstore
1327     // for the transform given by zoom.
1328     IntRect srcRect(srcPoint.x(),
1329                     srcPoint.y(),
1330                     viewportRect.width() / zoomFraction,
1331                     viewportRect.height() / zoomFraction);
1332     m_backingStore->d->blitContents(dstRect, srcRect);
1333
1334     m_delayedZoomArguments.scale = scale;
1335     m_delayedZoomArguments.anchor = anchor;
1336     m_delayedZoomArguments.enforceScaleClamping = enforceScaleClamping;
1337     m_delayedZoomArguments.forceRendering = forceRendering;
1338     m_delayedZoomTimer->startOneShot(delayedZoomInterval);
1339
1340     return true;
1341 }
1342
1343 void WebPagePrivate::unscheduleZoomAboutPoint()
1344 {
1345     if (m_delayedZoomTimer->isActive())
1346         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::None);
1347
1348     m_delayedZoomTimer->stop();
1349 }
1350
1351 void WebPagePrivate::zoomAboutPointTimerFired(Timer<WebPagePrivate>*)
1352 {
1353     zoomAboutPoint(m_delayedZoomArguments.scale, m_delayedZoomArguments.anchor, m_delayedZoomArguments.enforceScaleClamping, m_delayedZoomArguments.forceRendering);
1354     m_backingStore->d->resumeScreenAndBackingStoreUpdates(m_delayedZoomArguments.forceRendering ? BackingStore::RenderAndBlit : BackingStore::None);
1355 }
1356
1357 void WebPagePrivate::setNeedsLayout()
1358 {
1359     FrameView* view = m_mainFrame->view();
1360     ASSERT(view);
1361     view->setNeedsLayout();
1362 }
1363
1364 void WebPagePrivate::requestLayoutIfNeeded() const
1365 {
1366     FrameView* view = m_mainFrame->view();
1367     ASSERT(view);
1368     view->updateLayoutAndStyleIfNeededRecursive();
1369     ASSERT(!view->needsLayout());
1370 }
1371
1372 IntPoint WebPagePrivate::scrollPosition() const
1373 {
1374     return m_backingStoreClient->scrollPosition();
1375 }
1376
1377 IntPoint WebPagePrivate::maximumScrollPosition() const
1378 {
1379     return m_backingStoreClient->maximumScrollPosition();
1380 }
1381
1382 void WebPagePrivate::setScrollPosition(const IntPoint& pos)
1383 {
1384     m_backingStoreClient->setScrollPosition(pos);
1385 }
1386
1387 // Setting the scroll position is in transformed coordinates.
1388 void WebPage::setScrollPosition(const Platform::IntPoint& point)
1389 {
1390     if (d->transformedPointEqualsUntransformedPoint(point, d->scrollPosition()))
1391         return;
1392
1393     // If the user recently performed an event, this new scroll position
1394     // could possibly be a result of that. Or not, this is just a heuristic.
1395     if (currentTime() - d->m_lastUserEventTimestamp < manualScrollInterval)
1396         d->m_userPerformedManualScroll = true;
1397
1398     d->m_backingStoreClient->setIsClientGeneratedScroll(true);
1399
1400     // UI thread can call BackingStorePrivate::setScrollingOrZooming(false) before WebKit thread calls WebPage::setScrollPosition(),
1401     // in which case it will set ScrollableArea::m_constrainsScrollingToContentEdge to true earlier.
1402     // We can cache ScrollableArea::m_constrainsScrollingToContentEdge and always set it to false before we set scroll position in
1403     // WebKit thread to avoid scroll position clamping during scrolling, and restore it to what it was after that.
1404     bool constrainsScrollingToContentEdge = d->m_mainFrame->view()->constrainsScrollingToContentEdge();
1405     d->m_mainFrame->view()->setConstrainsScrollingToContentEdge(false);
1406     d->setScrollPosition(d->mapFromTransformed(point));
1407     d->m_mainFrame->view()->setConstrainsScrollingToContentEdge(constrainsScrollingToContentEdge);
1408
1409     d->m_backingStoreClient->setIsClientGeneratedScroll(false);
1410 }
1411
1412 bool WebPagePrivate::shouldSendResizeEvent()
1413 {
1414     if (!m_mainFrame->document())
1415         return false;
1416
1417     // PR#96865 : Provide an option to always send resize events, regardless of the loading
1418     //            status. The scenario for this are Sapphire applications which tend to
1419     //            maintain an open GET request to the server. This open GET results in
1420     //            webkit thinking that content is still arriving when at the application
1421     //            level it is considered fully loaded.
1422     //
1423     //            NOTE: Care must be exercised in the use of this option, as it bypasses
1424     //                  the sanity provided in 'isLoadingInAPISense()' below.
1425     //
1426     static const bool unrestrictedResizeEvents = Platform::Settings::instance()->unrestrictedResizeEvents();
1427     if (unrestrictedResizeEvents)
1428         return true;
1429
1430     // Don't send the resize event if the document is loading. Some pages automatically reload
1431     // when the window is resized; Safari on iPhone often resizes the window while setting up its
1432     // viewport. This obviously can cause problems.
1433     DocumentLoader* documentLoader = m_mainFrame->loader()->documentLoader();
1434     if (documentLoader && documentLoader->isLoadingInAPISense())
1435         return false;
1436
1437     return true;
1438 }
1439
1440 void WebPagePrivate::willDeferLoading()
1441 {
1442     m_deferredTasksTimer.stop();
1443     m_client->willDeferLoading();
1444 }
1445
1446 void WebPagePrivate::didResumeLoading()
1447 {
1448     if (!m_deferredTasks.isEmpty())
1449         m_deferredTasksTimer.startOneShot(0);
1450     m_client->didResumeLoading();
1451 }
1452
1453 void WebPagePrivate::deferredTasksTimerFired(WebCore::Timer<WebPagePrivate>*)
1454 {
1455     ASSERT(!m_deferredTasks.isEmpty());
1456     if (!m_deferredTasks.isEmpty())
1457         return;
1458
1459     OwnPtr<DeferredTaskBase> task = m_deferredTasks[0].release();
1460     m_deferredTasks.remove(0);
1461
1462     if (!m_deferredTasks.isEmpty())
1463         m_deferredTasksTimer.startOneShot(0);
1464
1465     task->perform(this);
1466 }
1467
1468 bool WebPagePrivate::scrollBy(int deltaX, int deltaY, bool scrollMainFrame)
1469 {
1470     IntSize delta(deltaX, deltaY);
1471     if (!scrollMainFrame) {
1472         // We need to work around the fact that ::map{To,From}Transformed do not
1473         // work well with negative values, like a negative width or height of an IntSize.
1474         IntSize copiedDelta(IntSize(abs(delta.width()), abs(delta.height())));
1475         IntSize untransformedCopiedDelta = mapFromTransformed(copiedDelta);
1476         delta = IntSize(
1477             delta.width() < 0 ? -untransformedCopiedDelta.width() : untransformedCopiedDelta.width(),
1478             delta.height() < 0 ? -untransformedCopiedDelta.height(): untransformedCopiedDelta.height());
1479
1480         if (m_inRegionScroller->d->scrollBy(delta)) {
1481             m_selectionHandler->selectionPositionChanged();
1482             // FIXME: We have code in place to handle scrolling and clipping tap highlight
1483             // on in-region scrolling. As soon as it is fast enough (i.e. we have it backed by
1484             // a backing store), we can reliably make use of it in the real world.
1485             // m_touchEventHandler->drawTapHighlight();
1486             return true;
1487         }
1488
1489         return false;
1490     }
1491
1492     setScrollPosition(scrollPosition() + delta);
1493     return true;
1494 }
1495
1496 bool WebPage::scrollBy(const Platform::IntSize& delta, bool scrollMainFrame)
1497 {
1498     d->m_backingStoreClient->setIsClientGeneratedScroll(true);
1499     bool b = d->scrollBy(delta.width(), delta.height(), scrollMainFrame);
1500     d->m_backingStoreClient->setIsClientGeneratedScroll(false);
1501     return b;
1502 }
1503
1504 void WebPagePrivate::notifyInRegionScrollStatusChanged(bool status)
1505 {
1506     if (!status && m_inRegionScroller->d->hasNode()) {
1507         enqueueRenderingOfClippedContentOfScrollableNodeAfterInRegionScrolling(m_inRegionScroller->d->node());
1508         m_inRegionScroller->d->reset();
1509     }
1510 }
1511
1512 void WebPage::notifyInRegionScrollStatusChanged(bool status)
1513 {
1514     d->notifyInRegionScrollStatusChanged(status);
1515 }
1516
1517 void WebPagePrivate::enqueueRenderingOfClippedContentOfScrollableNodeAfterInRegionScrolling(Node* scrolledNode)
1518 {
1519     ASSERT(scrolledNode);
1520     if (scrolledNode->isDocumentNode()) {
1521         Frame* frame = static_cast<const Document*>(scrolledNode)->frame();
1522         ASSERT(frame);
1523         if (!frame)
1524             return;
1525         ASSERT(frame != m_mainFrame);
1526         FrameView* view = frame->view();
1527         if (!view)
1528             return;
1529
1530         // Steps:
1531         // #1 - Get frame rect in contents coords.
1532         // #2 - Get the clipped scrollview rect in contents coords.
1533         // #3 - Take transform into account for 1 and 2.
1534         // #4 - Subtract 2 from 1, so we know exactly which areas of the frame
1535         //      are offscreen, and need async repainting.
1536         FrameView* mainFrameView = m_mainFrame->view();
1537         ASSERT(mainFrameView);
1538         IntRect frameRect = view->frameRect();
1539         frameRect = frame->tree()->parent()->view()->contentsToWindow(frameRect);
1540         frameRect = mainFrameView->windowToContents(frameRect);
1541
1542         IntRect visibleWindowRect = getRecursiveVisibleWindowRect(view);
1543         IntRect visibleContentsRect = mainFrameView->windowToContents(visibleWindowRect);
1544
1545         IntRect transformedFrameRect = mapToTransformed(frameRect);
1546         IntRect transformedVisibleContentsRect = mapToTransformed(visibleContentsRect);
1547
1548         Platform::IntRectRegion offscreenRegionOfIframe
1549             = Platform::IntRectRegion::subtractRegions(Platform::IntRect(transformedFrameRect), Platform::IntRect(transformedVisibleContentsRect));
1550
1551         if (!offscreenRegionOfIframe.isEmpty())
1552             m_backingStore->d->m_renderQueue->addToQueue(RenderQueue::RegularRender, offscreenRegionOfIframe.rects());
1553     }
1554 }
1555
1556 void WebPagePrivate::setHasInRegionScrollableAreas(bool b)
1557 {
1558     if (b != m_hasInRegionScrollableAreas)
1559         m_hasInRegionScrollableAreas = b;
1560 }
1561
1562 IntSize WebPagePrivate::viewportSize() const
1563 {
1564     return mapFromTransformed(transformedViewportSize());
1565 }
1566
1567 IntSize WebPagePrivate::actualVisibleSize() const
1568 {
1569     return mapFromTransformed(transformedActualVisibleSize());
1570 }
1571
1572 bool WebPagePrivate::hasVirtualViewport() const
1573 {
1574     return m_virtualViewportWidth && m_virtualViewportHeight;
1575 }
1576
1577 void WebPagePrivate::updateViewportSize(bool setFixedReportedSize, bool sendResizeEvent)
1578 {
1579     ASSERT(m_mainFrame->view());
1580     if (setFixedReportedSize)
1581         m_mainFrame->view()->setFixedReportedSize(actualVisibleSize());
1582
1583     IntRect frameRect = IntRect(scrollPosition(), viewportSize());
1584     if (frameRect != m_mainFrame->view()->frameRect()) {
1585         m_mainFrame->view()->setFrameRect(frameRect);
1586         m_mainFrame->view()->adjustViewSize();
1587
1588 #if ENABLE(FULLSCREEN_API)
1589         // If we are in fullscreen video mode, and we change the FrameView::viewportRect,
1590         // we need to adjust the media container to the new size.
1591         if (m_fullscreenVideoNode) {
1592             Document* document = m_fullscreenVideoNode->document();
1593             ASSERT(document);
1594             ASSERT(document->fullScreenRenderer());
1595
1596             int width = m_mainFrame->view()->visibleContentRect().size().width();
1597             document->fullScreenRenderer()->style()->setWidth(Length(width, Fixed));
1598         }
1599 #endif
1600     }
1601
1602     // We're going to need to send a resize event to JavaScript because
1603     // innerWidth and innerHeight depend on fixed reported size.
1604     // This is how we support mobile pages where JavaScript resizes
1605     // the page in order to get around the fixed layout size, e.g.
1606     // google maps when it detects a mobile user agent.
1607     if (sendResizeEvent && shouldSendResizeEvent())
1608         m_mainFrame->eventHandler()->sendResizeEvent();
1609
1610     // When the actual visible size changes, we also
1611     // need to reposition fixed elements.
1612     m_mainFrame->view()->repaintFixedElementsAfterScrolling();
1613 }
1614
1615 FloatPoint WebPagePrivate::centerOfVisibleContentsRect() const
1616 {
1617     // The visible contents rect in float.
1618     FloatRect visibleContentsRect = this->visibleContentsRect();
1619
1620     // The center of the visible contents rect in float.
1621     return FloatPoint(visibleContentsRect.x() + visibleContentsRect.width() / 2.0,
1622                       visibleContentsRect.y() + visibleContentsRect.height() / 2.0);
1623 }
1624
1625 IntRect WebPagePrivate::visibleContentsRect() const
1626 {
1627     return m_backingStoreClient->visibleContentsRect();
1628 }
1629
1630 IntSize WebPagePrivate::contentsSize() const
1631 {
1632     if (!m_mainFrame->view())
1633         return IntSize();
1634
1635     return m_backingStoreClient->contentsSize();
1636 }
1637
1638 IntSize WebPagePrivate::absoluteVisibleOverflowSize() const
1639 {
1640     if (!m_mainFrame->contentRenderer())
1641         return IntSize();
1642
1643     return IntSize(m_mainFrame->contentRenderer()->rightAbsoluteVisibleOverflow(), m_mainFrame->contentRenderer()->bottomAbsoluteVisibleOverflow());
1644 }
1645
1646 void WebPagePrivate::contentsSizeChanged(const IntSize& contentsSize)
1647 {
1648     if (m_previousContentsSize == contentsSize)
1649         return;
1650
1651     // This should only occur in the middle of layout so we set a flag here and
1652     // handle it at the end of the layout.
1653     m_contentsSizeChanged = true;
1654
1655 #if DEBUG_WEBPAGE_LOAD
1656     Platform::log(Platform::LogLevelInfo, "WebPagePrivate::contentsSizeChanged %dx%d", contentsSize.width(), contentsSize.height());
1657 #endif
1658 }
1659
1660 void WebPagePrivate::layoutFinished()
1661 {
1662     if (!m_contentsSizeChanged && !m_overflowExceedsContentsSize)
1663         return;
1664
1665     m_contentsSizeChanged = false; // Toggle to turn off notification again.
1666     m_overflowExceedsContentsSize = false;
1667
1668     if (contentsSize().isEmpty())
1669         return;
1670
1671     // The call to zoomToInitialScaleOnLoad can cause recursive layout when called from
1672     // the middle of a layout, but the recursion is limited by detection code in
1673     // setViewMode() and mitigation code in fixedLayoutSize().
1674     if (didLayoutExceedMaximumIterations()) {
1675         notifyTransformedContentsSizeChanged();
1676         return;
1677     }
1678
1679     // Temporarily save the m_previousContentsSize here before updating it (in
1680     // notifyTransformedContentsSizeChanged()) so we can compare if our contents
1681     // shrunk afterwards.
1682     IntSize previousContentsSize = m_previousContentsSize;
1683
1684     m_nestedLayoutFinishedCount++;
1685
1686     if (shouldZoomToInitialScaleOnLoad()) {
1687         zoomToInitialScaleOnLoad();
1688         m_shouldZoomToInitialScaleAfterLoadFinished = false;
1689     } else if (loadState() != None)
1690         notifyTransformedContentsSizeChanged();
1691
1692     m_nestedLayoutFinishedCount--;
1693
1694     if (!m_nestedLayoutFinishedCount) {
1695         // When the contents shrinks, there is a risk that we
1696         // will be left at a scroll position that lies outside of the
1697         // contents rect. Since we allow overscrolling and neglect
1698         // to clamp overscroll in order to retain input focus (RIM Bug #414)
1699         // we need to clamp somewhere, and this is where we know the
1700         // contents size has changed.
1701
1702         if (contentsSize() != previousContentsSize) {
1703
1704             IntPoint newScrollPosition = scrollPosition();
1705
1706             if (contentsSize().height() < previousContentsSize.height()) {
1707                 IntPoint scrollPositionWithHeightShrunk = IntPoint(newScrollPosition.x(), maximumScrollPosition().y());
1708                 newScrollPosition = newScrollPosition.shrunkTo(scrollPositionWithHeightShrunk);
1709             }
1710
1711             if (contentsSize().width() < previousContentsSize.width()) {
1712                 IntPoint scrollPositionWithWidthShrunk = IntPoint(maximumScrollPosition().x(), newScrollPosition.y());
1713                 newScrollPosition = newScrollPosition.shrunkTo(scrollPositionWithWidthShrunk);
1714             }
1715
1716             if (newScrollPosition != scrollPosition()) {
1717                 setScrollPosition(newScrollPosition);
1718                 notifyTransformedScrollChanged();
1719             }
1720         }
1721     }
1722 }
1723
1724 void WebPagePrivate::zoomToInitialScaleOnLoad()
1725 {
1726 #if DEBUG_WEBPAGE_LOAD
1727     Platform::log(Platform::LogLevelInfo, "WebPagePrivate::zoomToInitialScaleOnLoad");
1728 #endif
1729
1730     bool needsLayout = false;
1731
1732     // If the contents width exceeds the viewport width set to desktop mode.
1733     if (m_shouldUseFixedDesktopMode)
1734         needsLayout = setViewMode(FixedDesktop);
1735     else
1736         needsLayout = setViewMode(Desktop);
1737
1738     if (needsLayout) {
1739         // This can cause recursive layout...
1740         setNeedsLayout();
1741     }
1742
1743     if (contentsSize().isEmpty()) {
1744 #if DEBUG_WEBPAGE_LOAD
1745         Platform::log(Platform::LogLevelInfo, "WebPagePrivate::zoomToInitialScaleOnLoad content is empty!");
1746 #endif
1747         requestLayoutIfNeeded();
1748         m_client->resetBitmapZoomScale(currentScale());
1749         notifyTransformedContentsSizeChanged();
1750         return;
1751     }
1752
1753     bool performedZoom = false;
1754     bool shouldZoom = !m_userPerformedManualZoom;
1755
1756     // If this load should restore view state, don't zoom to initial scale
1757     // but instead let the HistoryItem's saved viewport reign supreme.
1758     if (m_mainFrame && m_mainFrame->loader() && m_mainFrame->loader()->shouldRestoreScrollPositionAndViewState())
1759         shouldZoom = false;
1760
1761     if (shouldZoom && shouldZoomToInitialScaleOnLoad()) {
1762         // Preserve at top and at left position, to avoid scrolling
1763         // to a non top-left position for web page with viewport meta tag
1764         // that specifies an initial-scale that is zoomed in.
1765         FloatPoint anchor = centerOfVisibleContentsRect();
1766         if (!scrollPosition().x())
1767             anchor.setX(0);
1768         if (!scrollPosition().y())
1769             anchor.setY(0);
1770         performedZoom = zoomAboutPoint(initialScale(), anchor);
1771     }
1772
1773     // zoomAboutPoint above can also toggle setNeedsLayout and cause recursive layout...
1774     requestLayoutIfNeeded();
1775
1776     if (!performedZoom) {
1777         // We only notify if we didn't perform zoom, because zoom will notify on
1778         // its own...
1779         m_client->resetBitmapZoomScale(currentScale());
1780         notifyTransformedContentsSizeChanged();
1781     }
1782 }
1783
1784 double WebPagePrivate::zoomToFitScale() const
1785 {
1786     int contentWidth = contentsSize().width();
1787     int contentHeight = contentsSize().height();
1788     double zoomToFitScale = contentWidth > 0.0 ? static_cast<double>(m_actualVisibleWidth) / contentWidth : 1.0;
1789     if (contentHeight * zoomToFitScale < static_cast<double>(m_defaultLayoutSize.height()))
1790         zoomToFitScale = contentHeight > 0 ? static_cast<double>(m_defaultLayoutSize.height()) / contentHeight : 1.0;
1791
1792     return std::max(zoomToFitScale, minimumZoomToFitScale);
1793 }
1794
1795 double WebPage::zoomToFitScale() const
1796 {
1797     return d->zoomToFitScale();
1798 }
1799
1800 double WebPagePrivate::initialScale() const
1801 {
1802     if (m_initialScale > 0.0)
1803         return m_initialScale;
1804
1805     if (m_webSettings->isZoomToFitOnLoad())
1806         return zoomToFitScale();
1807
1808     return 1.0;
1809 }
1810
1811 double WebPage::initialScale() const
1812 {
1813     return d->initialScale();
1814 }
1815
1816 void WebPage::initializeIconDataBase()
1817 {
1818     IconDatabaseClientBlackBerry::getInstance()->initIconDatabase(d->m_webSettings);
1819 }
1820
1821 bool WebPage::isUserScalable() const
1822 {
1823     return d->isUserScalable();
1824 }
1825
1826 void WebPage::setUserScalable(bool userScalable)
1827 {
1828     d->setUserScalable(userScalable);
1829 }
1830
1831 double WebPage::currentScale() const
1832 {
1833     return d->currentScale();
1834 }
1835
1836 void WebPage::setInitialScale(double initialScale)
1837 {
1838     d->setInitialScale(initialScale);
1839 }
1840
1841 double WebPage::minimumScale() const
1842 {
1843     return d->minimumScale();
1844 }
1845
1846 void WebPage::setMinimumScale(double minimumScale)
1847 {
1848     d->setMinimumScale(minimumScale);
1849 }
1850
1851 void WebPage::setMaximumScale(double maximumScale)
1852 {
1853     d->setMaximumScale(maximumScale);
1854 }
1855
1856 double WebPagePrivate::maximumScale() const
1857 {
1858     if (m_maximumScale >= zoomToFitScale() && m_maximumScale >= m_minimumScale)
1859         return m_maximumScale;
1860
1861     return hasVirtualViewport() ? std::max<double>(zoomToFitScale(), 4.0) : 4.0;
1862 }
1863
1864 double WebPage::maximumScale() const
1865 {
1866     return d->maximumScale();
1867 }
1868
1869 void WebPagePrivate::resetScales()
1870 {
1871     TransformationMatrix identity;
1872     *m_transformationMatrix = identity;
1873     m_initialScale = m_webSettings->initialScale() > 0 ? m_webSettings->initialScale() : -1.0;
1874     m_minimumScale = -1.0;
1875     m_maximumScale = -1.0;
1876
1877     // We have to let WebCore know about updated framerect now that we've
1878     // reset our scales. See: RIM Bug #401.
1879     updateViewportSize();
1880 }
1881
1882 IntPoint WebPagePrivate::transformedScrollPosition() const
1883 {
1884     return m_backingStoreClient->transformedScrollPosition();
1885 }
1886
1887 // Returned scroll position is in transformed coordinates.
1888 Platform::IntPoint WebPage::scrollPosition() const
1889 {
1890     return d->transformedScrollPosition();
1891 }
1892
1893 IntPoint WebPagePrivate::transformedMaximumScrollPosition() const
1894 {
1895     return m_backingStoreClient->transformedMaximumScrollPosition();
1896 }
1897
1898 IntSize WebPagePrivate::transformedActualVisibleSize() const
1899 {
1900     return IntSize(m_actualVisibleWidth, m_actualVisibleHeight);
1901 }
1902
1903 Platform::IntSize WebPage::viewportSize() const
1904 {
1905     return d->transformedActualVisibleSize();
1906 }
1907
1908 IntSize WebPagePrivate::transformedViewportSize() const
1909 {
1910     return Platform::Graphics::Screen::primaryScreen()->size();
1911 }
1912
1913 IntRect WebPagePrivate::transformedVisibleContentsRect() const
1914 {
1915     // Usually this would be mapToTransformed(visibleContentsRect()), but
1916     // that results in rounding errors because we already set the WebCore
1917     // viewport size from our original transformedViewportSize().
1918     // Instead, we only transform the scroll position and take the
1919     // viewport size as it is, which ensures that e.g. blitting operations
1920     // always cover the whole widget/screen.
1921     return IntRect(transformedScrollPosition(), transformedViewportSize());
1922 }
1923
1924 IntSize WebPagePrivate::transformedContentsSize() const
1925 {
1926     // mapToTransformed() functions use this method to crop their results,
1927     // so we can't make use of them here. While we want rounding inside page
1928     // boundaries to extend rectangles and round points, we need to crop the
1929     // contents size to the floored values so that we don't try to display
1930     // or report points that are not fully covered by the actual float-point
1931     // contents rectangle.
1932     const IntSize untransformedContentsSize = contentsSize();
1933     const FloatPoint transformedBottomRight = m_transformationMatrix->mapPoint(
1934         FloatPoint(untransformedContentsSize.width(), untransformedContentsSize.height()));
1935     return IntSize(floorf(transformedBottomRight.x()), floorf(transformedBottomRight.y()));
1936 }
1937
1938 IntPoint WebPagePrivate::mapFromContentsToViewport(const IntPoint& point) const
1939 {
1940     return m_backingStoreClient->mapFromContentsToViewport(point);
1941 }
1942
1943 IntPoint WebPagePrivate::mapFromViewportToContents(const IntPoint& point) const
1944 {
1945     return m_backingStoreClient->mapFromViewportToContents(point);
1946 }
1947
1948 IntRect WebPagePrivate::mapFromContentsToViewport(const IntRect& rect) const
1949 {
1950     return m_backingStoreClient->mapFromContentsToViewport(rect);
1951 }
1952
1953 IntRect WebPagePrivate::mapFromViewportToContents(const IntRect& rect) const
1954 {
1955     return m_backingStoreClient->mapFromViewportToContents(rect);
1956 }
1957
1958 IntPoint WebPagePrivate::mapFromTransformedContentsToTransformedViewport(const IntPoint& point) const
1959 {
1960     return m_backingStoreClient->mapFromTransformedContentsToTransformedViewport(point);
1961 }
1962
1963 IntPoint WebPagePrivate::mapFromTransformedViewportToTransformedContents(const IntPoint& point) const
1964 {
1965     return m_backingStoreClient->mapFromTransformedViewportToTransformedContents(point);
1966 }
1967
1968 IntRect WebPagePrivate::mapFromTransformedContentsToTransformedViewport(const IntRect& rect) const
1969 {
1970     return m_backingStoreClient->mapFromTransformedContentsToTransformedViewport(rect);
1971 }
1972
1973 IntRect WebPagePrivate::mapFromTransformedViewportToTransformedContents(const IntRect& rect) const
1974 {
1975     return m_backingStoreClient->mapFromTransformedViewportToTransformedContents(rect);
1976 }
1977
1978 // NOTE: PIXEL ROUNDING!
1979 // Accurate back-and-forth rounding is not possible with information loss
1980 // by integer points and sizes, so we always expand the resulting mapped
1981 // float rectangles to the nearest integer. For points, we always use
1982 // floor-rounding in mapToTransformed() so that we don't have to crop to
1983 // the (floor'd) transformed contents size.
1984 static inline IntPoint roundTransformedPoint(const FloatPoint &point)
1985 {
1986     // Maps by rounding half towards zero.
1987     return IntPoint(static_cast<int>(floorf(point.x())), static_cast<int>(floorf(point.y())));
1988 }
1989
1990 static inline IntPoint roundUntransformedPoint(const FloatPoint &point)
1991 {
1992     // Maps by rounding half away from zero.
1993     return IntPoint(static_cast<int>(ceilf(point.x())), static_cast<int>(ceilf(point.y())));
1994 }
1995
1996 IntPoint WebPagePrivate::mapToTransformed(const IntPoint& point) const
1997 {
1998     return roundTransformedPoint(m_transformationMatrix->mapPoint(FloatPoint(point)));
1999 }
2000
2001 FloatPoint WebPagePrivate::mapToTransformedFloatPoint(const FloatPoint& point) const
2002 {
2003     return m_transformationMatrix->mapPoint(point);
2004 }
2005
2006 IntPoint WebPagePrivate::mapFromTransformed(const IntPoint& point) const
2007 {
2008     return roundUntransformedPoint(m_transformationMatrix->inverse().mapPoint(FloatPoint(point)));
2009 }
2010
2011 FloatPoint WebPagePrivate::mapFromTransformedFloatPoint(const FloatPoint& point) const
2012 {
2013     return m_transformationMatrix->inverse().mapPoint(point);
2014 }
2015
2016 FloatRect WebPagePrivate::mapFromTransformedFloatRect(const FloatRect& rect) const
2017 {
2018     return m_transformationMatrix->inverse().mapRect(rect);
2019 }
2020
2021 IntSize WebPagePrivate::mapToTransformed(const IntSize& size) const
2022 {
2023     return mapToTransformed(IntRect(IntPoint::zero(), size)).size();
2024 }
2025
2026 IntSize WebPagePrivate::mapFromTransformed(const IntSize& size) const
2027 {
2028     return mapFromTransformed(IntRect(IntPoint::zero(), size)).size();
2029 }
2030
2031 IntRect WebPagePrivate::mapToTransformed(const IntRect& rect) const
2032 {
2033     return enclosingIntRect(m_transformationMatrix->mapRect(FloatRect(rect)));
2034 }
2035
2036 // Use this in conjunction with mapToTransformed(IntRect), in most cases.
2037 void WebPagePrivate::clipToTransformedContentsRect(IntRect& rect) const
2038 {
2039     rect.intersect(IntRect(IntPoint::zero(), transformedContentsSize()));
2040 }
2041
2042 IntRect WebPagePrivate::mapFromTransformed(const IntRect& rect) const
2043 {
2044     return enclosingIntRect(m_transformationMatrix->inverse().mapRect(FloatRect(rect)));
2045 }
2046
2047 bool WebPagePrivate::transformedPointEqualsUntransformedPoint(const IntPoint& transformedPoint, const IntPoint& untransformedPoint)
2048 {
2049     // Scaling down is always more accurate than scaling up.
2050     if (m_transformationMatrix->a() > 1.0)
2051         return transformedPoint == mapToTransformed(untransformedPoint);
2052
2053     return mapFromTransformed(transformedPoint) == untransformedPoint;
2054 }
2055
2056 void WebPagePrivate::notifyTransformChanged()
2057 {
2058     notifyTransformedContentsSizeChanged();
2059     notifyTransformedScrollChanged();
2060
2061     m_backingStore->d->transformChanged();
2062 }
2063
2064 void WebPagePrivate::notifyTransformedContentsSizeChanged()
2065 {
2066     // We mark here as the last reported content size we sent to the client.
2067     m_previousContentsSize = contentsSize();
2068
2069     const IntSize size = transformedContentsSize();
2070     m_backingStore->d->contentsSizeChanged(size);
2071     m_client->contentsSizeChanged(size);
2072 }
2073
2074 void WebPagePrivate::notifyTransformedScrollChanged()
2075 {
2076     const IntPoint pos = transformedScrollPosition();
2077     m_backingStore->d->scrollChanged(pos);
2078     m_client->scrollChanged(pos);
2079 }
2080
2081 bool WebPagePrivate::setViewMode(ViewMode mode)
2082 {
2083     if (!m_mainFrame->view())
2084         return false;
2085
2086     m_viewMode = mode;
2087
2088     // If we're in the middle of a nested layout with a recursion count above
2089     // some maximum threshold, then our algorithm for finding the minimum content
2090     // width of a given page has become dependent on the visible width.
2091     //
2092     // We need to find some method to ensure that we don't experience excessive
2093     // and even infinite recursion. This can even happen with valid html. The
2094     // former can happen when we run into inline text with few candidates for line
2095     // break. The latter can happen for instance if the page has a negative margin
2096     // set against the right border. Note: this is valid by spec and can lead to
2097     // a situation where there is no value for which the content width will ensure
2098     // no horizontal scrollbar.
2099     // Example: LayoutTests/css1/box_properties/margin.html
2100     //
2101     // In order to address such situations when we detect a recursion above some
2102     // maximum threshold we snap our fixed layout size to a defined quantum increment.
2103     // Eventually, either the content width will be satisfied to ensure no horizontal
2104     // scrollbar or this increment will run into the maximum layout size and the
2105     // recursion will necessarily end.
2106     bool snapToIncrement = didLayoutExceedMaximumIterations();
2107
2108     IntSize currentSize = m_mainFrame->view()->fixedLayoutSize();
2109     IntSize newSize = fixedLayoutSize(snapToIncrement);
2110     if (currentSize == newSize)
2111         return false;
2112
2113     // FIXME: Temp solution. We'll get back to this.
2114     if (m_nestedLayoutFinishedCount) {
2115         double widthChange = fabs(double(newSize.width() - currentSize.width()) / currentSize.width());
2116         double heightChange = fabs(double(newSize.height() - currentSize.height()) / currentSize.height());
2117         if (widthChange < 0.05 && heightChange < 0.05)
2118             return false;
2119     }
2120
2121     m_mainFrame->view()->setUseFixedLayout(useFixedLayout());
2122     m_mainFrame->view()->setFixedLayoutSize(newSize);
2123     return true; // Needs re-layout!
2124 }
2125
2126 void WebPagePrivate::setCursor(PlatformCursor handle)
2127 {
2128     if (m_currentCursor.type() != handle.type()) {
2129         m_currentCursor = handle;
2130         m_client->cursorChanged(handle.type(), handle.url().c_str(), handle.hotspot().x(), handle.hotspot().y());
2131     }
2132 }
2133
2134 Platform::NetworkStreamFactory* WebPagePrivate::networkStreamFactory()
2135 {
2136     return m_client->networkStreamFactory();
2137 }
2138
2139 Platform::Graphics::Window* WebPagePrivate::platformWindow() const
2140 {
2141     return m_client->window();
2142 }
2143
2144 void WebPagePrivate::setPreventsScreenDimming(bool keepAwake)
2145 {
2146     if (keepAwake) {
2147         if (!m_preventIdleDimmingCount)
2148             m_client->setPreventsScreenIdleDimming(true);
2149         m_preventIdleDimmingCount++;
2150     } else if (m_preventIdleDimmingCount > 0) {
2151         m_preventIdleDimmingCount--;
2152         if (!m_preventIdleDimmingCount)
2153             m_client->setPreventsScreenIdleDimming(false);
2154     } else
2155         ASSERT_NOT_REACHED(); // SetPreventsScreenIdleDimming(false) called too many times.
2156 }
2157
2158 void WebPagePrivate::showVirtualKeyboard(bool showKeyboard)
2159 {
2160     m_client->showVirtualKeyboard(showKeyboard);
2161 }
2162
2163 void WebPagePrivate::ensureContentVisible(bool centerInView)
2164 {
2165     m_inputHandler->ensureFocusElementVisible(centerInView);
2166 }
2167
2168 void WebPagePrivate::zoomToContentRect(const IntRect& rect)
2169 {
2170     // Don't scale if the user is not supposed to scale.
2171     if (!isUserScalable())
2172         return;
2173
2174     FloatPoint anchor = FloatPoint(rect.width() / 2.0 + rect.x(), rect.height() / 2.0 + rect.y());
2175     IntSize viewSize = viewportSize();
2176
2177     // Calculate the scale required to scale that dimension to fit.
2178     double scaleH = (double)viewSize.width() / (double)rect.width();
2179     double scaleV = (double)viewSize.height() / (double)rect.height();
2180
2181     // Choose the smaller scale factor so that all of the content is visible.
2182     zoomAboutPoint(min(scaleH, scaleV), anchor);
2183 }
2184
2185 void WebPagePrivate::registerPlugin(PluginView* plugin, bool shouldRegister)
2186 {
2187     if (shouldRegister)
2188         m_pluginViews.add(plugin);
2189     else
2190         m_pluginViews.remove(plugin);
2191 }
2192
2193 #define FOR_EACH_PLUGINVIEW(pluginViews) \
2194     HashSet<PluginView*>::const_iterator it = pluginViews.begin(); \
2195     HashSet<PluginView*>::const_iterator last = pluginViews.end(); \
2196     for (; it != last; ++it)
2197
2198 void WebPagePrivate::notifyPageOnLoad()
2199 {
2200     FOR_EACH_PLUGINVIEW(m_pluginViews)
2201         (*it)->handleOnLoadEvent();
2202 }
2203
2204 bool WebPagePrivate::shouldPluginEnterFullScreen(PluginView* plugin, const char* windowUniquePrefix)
2205 {
2206     return m_client->shouldPluginEnterFullScreen();
2207 }
2208
2209 void WebPagePrivate::didPluginEnterFullScreen(PluginView* plugin, const char* windowUniquePrefix)
2210 {
2211     m_fullScreenPluginView = plugin;
2212     m_client->didPluginEnterFullScreen();
2213
2214     if (!m_client->window())
2215         return;
2216
2217     Platform::Graphics::Window::setTransparencyDiscardFilter(windowUniquePrefix);
2218     m_client->window()->setSensitivityFullscreenOverride(true);
2219 }
2220
2221 void WebPagePrivate::didPluginExitFullScreen(PluginView* plugin, const char* windowUniquePrefix)
2222 {
2223     m_fullScreenPluginView = 0;
2224     m_client->didPluginExitFullScreen();
2225
2226     if (!m_client->window())
2227         return;
2228
2229     Platform::Graphics::Window::setTransparencyDiscardFilter(0);
2230     m_client->window()->setSensitivityFullscreenOverride(false);
2231 }
2232
2233 void WebPagePrivate::onPluginStartBackgroundPlay(PluginView* plugin, const char* windowUniquePrefix)
2234 {
2235     m_client->onPluginStartBackgroundPlay();
2236 }
2237
2238 void WebPagePrivate::onPluginStopBackgroundPlay(PluginView* plugin, const char* windowUniquePrefix)
2239 {
2240     m_client->onPluginStopBackgroundPlay();
2241 }
2242
2243 bool WebPagePrivate::lockOrientation(bool landscape)
2244 {
2245     return m_client->lockOrientation(landscape);
2246 }
2247
2248 void WebPagePrivate::unlockOrientation()
2249 {
2250     return m_client->unlockOrientation();
2251 }
2252
2253 int WebPagePrivate::orientation() const
2254 {
2255 #if ENABLE(ORIENTATION_EVENTS)
2256     return m_mainFrame->orientation();
2257 #else
2258 #error ORIENTATION_EVENTS must be defined.
2259 // Or a copy of the orientation value will have to be stored in these objects.
2260 #endif
2261 }
2262
2263 double WebPagePrivate::currentZoomFactor() const
2264 {
2265     return currentScale();
2266 }
2267
2268 int WebPagePrivate::showAlertDialog(WebPageClient::AlertType atype)
2269 {
2270     return m_client->showAlertDialog(atype);
2271 }
2272
2273 bool WebPagePrivate::isActive() const
2274 {
2275     return m_client->isActive();
2276 }
2277
2278 bool WebPagePrivate::authenticationChallenge(const KURL& url, const ProtectionSpace& protectionSpace, Credential& inputCredential)
2279 {
2280     WebString username;
2281     WebString password;
2282
2283 #if !defined(PUBLIC_BUILD) || !PUBLIC_BUILD
2284     if (m_dumpRenderTree)
2285         return m_dumpRenderTree->didReceiveAuthenticationChallenge(inputCredential);
2286 #endif
2287
2288 #if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)
2289     if (m_webSettings->isCredentialAutofillEnabled() && !m_webSettings->isPrivateBrowsingEnabled())
2290         credentialManager().autofillAuthenticationChallenge(protectionSpace, username, password);
2291 #endif
2292
2293     bool isConfirmed = m_client->authenticationChallenge(protectionSpace.realm().characters(), protectionSpace.realm().length(), username, password);
2294
2295 #if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)
2296     Credential credential(username, password, CredentialPersistencePermanent);
2297     if (m_webSettings->isCredentialAutofillEnabled() && !m_webSettings->isPrivateBrowsingEnabled() && isConfirmed)
2298         credentialManager().saveCredentialIfConfirmed(this, CredentialTransformData(url, protectionSpace, credential));
2299 #else
2300     Credential credential(username, password, CredentialPersistenceNone);
2301 #endif
2302     inputCredential = credential;
2303     return isConfirmed;
2304 }
2305
2306 PageClientBlackBerry::SaveCredentialType WebPagePrivate::notifyShouldSaveCredential(bool isNew)
2307 {
2308     return static_cast<PageClientBlackBerry::SaveCredentialType>(m_client->notifyShouldSaveCredential(isNew));
2309 }
2310
2311 void WebPagePrivate::syncProxyCredential(const WebCore::Credential& credential)
2312 {
2313     m_client->syncProxyCredential(credential.user().utf8().data(), credential.password().utf8().data());
2314 }
2315
2316 void WebPagePrivate::notifyPopupAutofillDialog(const Vector<String>& candidates, const WebCore::IntRect& screenRect)
2317 {
2318     vector<string> textItems;
2319     for (size_t i = 0; i < candidates.size(); i++)
2320         textItems.push_back(candidates[i].utf8().data());
2321     m_client->notifyPopupAutofillDialog(textItems, screenRect);
2322 }
2323
2324 void WebPagePrivate::notifyDismissAutofillDialog()
2325 {
2326     m_client->notifyDismissAutofillDialog();
2327 }
2328
2329 bool WebPagePrivate::useFixedLayout() const
2330 {
2331     return true;
2332 }
2333
2334 Platform::WebContext WebPagePrivate::webContext(TargetDetectionStrategy strategy)
2335 {
2336     Platform::WebContext context;
2337
2338     RefPtr<Node> node = contextNode(strategy);
2339     m_currentContextNode = node;
2340     if (!m_currentContextNode)
2341         return context;
2342
2343     // Send an onContextMenu event to the current context ndoe and get the result. Since we've already figured out
2344     // which node we want, we can send it directly to the node and not do a hit test. The onContextMenu event doesn't require
2345     // mouse positions so we just set the position at (0,0)
2346     PlatformMouseEvent mouseEvent(IntPoint(), IntPoint(), PlatformEvent::MouseMoved, 0, NoButton, TouchScreen);
2347     if (m_currentContextNode->dispatchMouseEvent(mouseEvent, eventNames().contextmenuEvent, 0)) {
2348         context.setFlag(Platform::WebContext::IsOnContextMenuPrevented);
2349         return context;
2350     }
2351
2352     // Unpress the mouse button if we're actually getting context.
2353     EventHandler* eventHandler = focusedOrMainFrame()->eventHandler();
2354     if (eventHandler->mousePressed())
2355         eventHandler->setMousePressed(false);
2356
2357     requestLayoutIfNeeded();
2358
2359     bool nodeAllowSelectionOverride = false;
2360     if (Node* linkNode = node->enclosingLinkEventParentOrSelf()) {
2361         KURL href;
2362         if (linkNode->isLink() && linkNode->hasAttributes()) {
2363             if (Attribute* attribute = static_cast<Element*>(linkNode)->getAttributeItem(HTMLNames::hrefAttr))
2364                 href = linkNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(attribute->value()));
2365         }
2366
2367         String pattern = findPatternStringForUrl(href);
2368         if (!pattern.isEmpty())
2369             context.setPattern(pattern.utf8().data());
2370
2371         if (!href.string().isEmpty()) {
2372             context.setUrl(href.string().utf8().data());
2373
2374             // Links are non-selectable by default, but selection should be allowed
2375             // providing the page is selectable, use the parent to determine it.
2376             if (linkNode->parentNode() && linkNode->parentNode()->canStartSelection())
2377                 nodeAllowSelectionOverride = true;
2378         }
2379     }
2380
2381     if (node->isHTMLElement()) {
2382         HTMLImageElement* imageElement = 0;
2383         HTMLMediaElement* mediaElement = 0;
2384
2385         if (node->hasTagName(HTMLNames::imgTag))
2386             imageElement = static_cast<HTMLImageElement*>(node.get());
2387         else if (node->hasTagName(HTMLNames::areaTag))
2388             imageElement = static_cast<HTMLAreaElement*>(node.get())->imageElement();
2389
2390         if (static_cast<HTMLElement*>(node.get())->isMediaElement())
2391             mediaElement = static_cast<HTMLMediaElement*>(node.get());
2392
2393         if (imageElement && imageElement->renderer()) {
2394             context.setFlag(Platform::WebContext::IsImage);
2395             // FIXME: At the mean time, we only show "Save Image" when the image data is available.
2396             if (CachedResource* cachedResource = imageElement->cachedImage()) {
2397                 if (cachedResource->isLoaded() && cachedResource->data()) {
2398                     String url = stripLeadingAndTrailingHTMLSpaces(imageElement->getAttribute(HTMLNames::srcAttr).string());
2399                     context.setSrc(node->document()->completeURL(url).string().utf8().data());
2400                 }
2401             }
2402             String alt = imageElement->altText();
2403             if (!alt.isNull())
2404                 context.setAlt(alt.utf8().data());
2405         }
2406
2407         if (mediaElement) {
2408             if (mediaElement->hasAudio())
2409                 context.setFlag(Platform::WebContext::IsAudio);
2410             if (mediaElement->hasVideo())
2411                 context.setFlag(Platform::WebContext::IsVideo);
2412
2413             String src = stripLeadingAndTrailingHTMLSpaces(mediaElement->getAttribute(HTMLNames::srcAttr).string());
2414             context.setSrc(node->document()->completeURL(src).string().utf8().data());
2415         }
2416     }
2417
2418     if (node->isTextNode()) {
2419         Text* curText = toText(node.get());
2420         if (!curText->wholeText().isEmpty())
2421             context.setText(curText->wholeText().utf8().data());
2422     }
2423
2424     bool canStartSelection = node->canStartSelection();
2425
2426     if (node->isElementNode()) {
2427         Element* element = static_cast<Element*>(node->shadowAncestorNode());
2428         if (DOMSupport::isTextBasedContentEditableElement(element)) {
2429             if (!canStartSelection) {
2430                 // Input fields host node is by spec non-editable unless the field itself has content editable enabled.
2431                 // Enable selection if the shadow tree for the input field is selectable.
2432                 Node* nodeUnderFinger = m_touchEventHandler->lastFatFingersResult().isValid() ? m_touchEventHandler->lastFatFingersResult().node(FatFingersResult::ShadowContentAllowed) : 0;
2433                 if (nodeUnderFinger)
2434                     canStartSelection = nodeUnderFinger->canStartSelection();
2435             }
2436             context.setFlag(Platform::WebContext::IsInput);
2437             if (element->hasTagName(HTMLNames::inputTag))
2438                 context.setFlag(Platform::WebContext::IsSingleLine);
2439             if (DOMSupport::isPasswordElement(element))
2440                 context.setFlag(Platform::WebContext::IsPassword);
2441
2442             String elementText(DOMSupport::inputElementText(element));
2443             if (!elementText.stripWhiteSpace().isEmpty())
2444                 context.setText(elementText.utf8().data());
2445         }
2446     }
2447
2448     if (!nodeAllowSelectionOverride && !canStartSelection)
2449         context.resetFlag(Platform::WebContext::IsSelectable);
2450
2451     if (node->isFocusable())
2452         context.setFlag(Platform::WebContext::IsFocusable);
2453
2454     return context;
2455 }
2456
2457 Platform::WebContext WebPage::webContext(TargetDetectionStrategy strategy) const
2458 {
2459     return d->webContext(strategy);
2460 }
2461
2462 void WebPagePrivate::updateCursor()
2463 {
2464     int buttonMask = 0;
2465     if (m_lastMouseEvent.button() == LeftButton)
2466         buttonMask = Platform::MouseEvent::ScreenLeftMouseButton;
2467     else if (m_lastMouseEvent.button() == MiddleButton)
2468         buttonMask = Platform::MouseEvent::ScreenMiddleMouseButton;
2469     else if (m_lastMouseEvent.button() == RightButton)
2470         buttonMask = Platform::MouseEvent::ScreenRightMouseButton;
2471
2472     BlackBerry::Platform::MouseEvent event(buttonMask, buttonMask, mapToTransformed(m_lastMouseEvent.position()), mapToTransformed(m_lastMouseEvent.globalPosition()), 0, 0);
2473     m_webPage->mouseEvent(event);
2474 }
2475
2476 IntSize WebPagePrivate::fixedLayoutSize(bool snapToIncrement) const
2477 {
2478     if (hasVirtualViewport())
2479         return IntSize(m_virtualViewportWidth, m_virtualViewportHeight);
2480
2481     const int defaultLayoutWidth = m_defaultLayoutSize.width();
2482     const int defaultLayoutHeight = m_defaultLayoutSize.height();
2483
2484     int minWidth = defaultLayoutWidth;
2485     int maxWidth = defaultMaxLayoutSize().width();
2486     int maxHeight = defaultMaxLayoutSize().height();
2487
2488     // If the load state is none then we haven't actually got anything yet, but we need to layout
2489     // the entire page so that the user sees the entire page (unrendered) instead of just part of it.
2490     if (m_loadState == None)
2491         return IntSize(defaultLayoutWidth, defaultLayoutHeight);
2492
2493     if (m_viewMode == FixedDesktop) {
2494         int width  = maxWidth;
2495         // if the defaultLayoutHeight is at minimum, it probably was set as 0
2496         // and clamped, meaning it's effectively not set.  (Even if it happened
2497         // to be set exactly to the minimum, it's too small to be useful.)  So
2498         // ignore it.
2499         int height;
2500         if (defaultLayoutHeight <= minimumLayoutSize.height())
2501             height = maxHeight;
2502         else
2503             height = ceilf(static_cast<float>(width) / static_cast<float>(defaultLayoutWidth) * static_cast<float>(defaultLayoutHeight));
2504         return IntSize(width, height);
2505     }
2506
2507     if (m_viewMode == Desktop) {
2508         // If we detect an overflow larger than the contents size then use that instead since
2509         // it'll still be clamped by the maxWidth below...
2510         int width = std::max(absoluteVisibleOverflowSize().width(), contentsSize().width());
2511         if (m_pendingOrientation != -1 && !m_nestedLayoutFinishedCount)
2512             width = 0;
2513
2514         if (snapToIncrement) {
2515             // Snap to increments of defaultLayoutWidth / 2.0.
2516             float factor = static_cast<float>(width) / (defaultLayoutWidth / 2.0);
2517             factor = ceilf(factor);
2518             width = (defaultLayoutWidth / 2.0) * factor;
2519         }
2520
2521         if (width < minWidth)
2522             width = minWidth;
2523         if (width > maxWidth)
2524             width = maxWidth;
2525         int height = ceilf(static_cast<float>(width) / static_cast<float>(defaultLayoutWidth) * static_cast<float>(defaultLayoutHeight));
2526         return IntSize(width, height);
2527     }
2528
2529     ASSERT_NOT_REACHED();
2530     return IntSize(defaultLayoutWidth, defaultLayoutHeight);
2531 }
2532
2533 BackingStoreClient* WebPagePrivate::backingStoreClientForFrame(const Frame* frame) const
2534 {
2535     ASSERT(frame);
2536     BackingStoreClient* backingStoreClient = 0;
2537     if (m_backingStoreClientForFrameMap.contains(frame))
2538         backingStoreClient = m_backingStoreClientForFrameMap.get(frame);
2539     return backingStoreClient;
2540 }
2541
2542 void WebPagePrivate::addBackingStoreClientForFrame(const Frame* frame, BackingStoreClient* client)
2543 {
2544     ASSERT(frame);
2545     ASSERT(client);
2546     m_backingStoreClientForFrameMap.add(frame, client);
2547 }
2548
2549 void WebPagePrivate::removeBackingStoreClientForFrame(const Frame* frame)
2550 {
2551     ASSERT(frame);
2552     if (m_backingStoreClientForFrameMap.contains(frame))
2553         m_backingStoreClientForFrameMap.remove(frame);
2554 }
2555
2556
2557 void WebPagePrivate::clearDocumentData(const Document* documentGoingAway)
2558 {
2559     ASSERT(documentGoingAway);
2560     if (m_currentContextNode && m_currentContextNode->document() == documentGoingAway)
2561         m_currentContextNode = 0;
2562
2563     if (m_currentPinchZoomNode && m_currentPinchZoomNode->document() == documentGoingAway)
2564         m_currentPinchZoomNode = 0;
2565
2566     if (m_currentBlockZoomAdjustedNode && m_currentBlockZoomAdjustedNode->document() == documentGoingAway)
2567         m_currentBlockZoomAdjustedNode = 0;
2568
2569     if (m_inRegionScroller->d->hasNode() && m_inRegionScroller->d->node()->document() == documentGoingAway)
2570         m_inRegionScroller->d->reset();
2571
2572     if (documentGoingAway->frame())
2573         m_inputHandler->frameUnloaded(documentGoingAway->frame());
2574
2575     Node* nodeUnderFatFinger = m_touchEventHandler->lastFatFingersResult().node();
2576     if (nodeUnderFatFinger && nodeUnderFatFinger->document() == documentGoingAway)
2577         m_touchEventHandler->resetLastFatFingersResult();
2578
2579     // NOTE: m_fullscreenVideoNode, m_fullScreenPluginView and m_pluginViews
2580     // are cleared in other methods already.
2581 }
2582
2583 typedef bool (*PredicateFunction)(RenderLayer*);
2584 static bool isPositionedContainer(RenderLayer* layer)
2585 {
2586     RenderObject* o = layer->renderer();
2587     return o->isRenderView() || o->isOutOfFlowPositioned() || o->isRelPositioned() || layer->hasTransform();
2588 }
2589
2590 static bool isFixedPositionedContainer(RenderLayer* layer)
2591 {
2592     RenderObject* o = layer->renderer();
2593     return o->isRenderView() || (o->isOutOfFlowPositioned() && o->style()->position() == FixedPosition);
2594 }
2595
2596 static RenderLayer* findAncestorOrSelfNotMatching(PredicateFunction predicate, RenderLayer* layer)
2597 {
2598     RenderLayer* curr = layer;
2599     while (curr && !predicate(curr))
2600         curr = curr->parent();
2601
2602     return curr;
2603 }
2604
2605 RenderLayer* WebPagePrivate::enclosingFixedPositionedAncestorOrSelfIfFixedPositioned(RenderLayer* layer)
2606 {
2607     return findAncestorOrSelfNotMatching(&isFixedPositionedContainer, layer);
2608 }
2609
2610 RenderLayer* WebPagePrivate::enclosingPositionedAncestorOrSelfIfPositioned(RenderLayer* layer)
2611 {
2612     return findAncestorOrSelfNotMatching(&isPositionedContainer, layer);
2613 }
2614
2615 static inline Frame* frameForNode(Node* node)
2616 {
2617     Node* origNode = node;
2618     for (; node; node = node->parentNode()) {
2619         if (RenderObject* renderer = node->renderer()) {
2620             if (renderer->isRenderView()) {
2621                 if (FrameView* view = toRenderView(renderer)->frameView()) {
2622                     if (Frame* frame = view->frame())
2623                         return frame;
2624                 }
2625             }
2626             if (renderer->isWidget()) {
2627                 Widget* widget = toRenderWidget(renderer)->widget();
2628                 if (widget && widget->isFrameView()) {
2629                     if (Frame* frame = static_cast<FrameView*>(widget)->frame())
2630                         return frame;
2631                 }
2632             }
2633         }
2634     }
2635
2636     for (node = origNode; node; node = node->parentNode()) {
2637         if (Document* doc = node->document()) {
2638             if (Frame* frame = doc->frame())
2639                 return frame;
2640         }
2641     }
2642
2643     return 0;
2644 }
2645
2646 static IntRect getNodeWindowRect(Node* node)
2647 {
2648     if (Frame* frame = frameForNode(node)) {
2649         if (FrameView* view = frame->view())
2650             return view->contentsToWindow(node->getRect());
2651     }
2652     ASSERT_NOT_REACHED();
2653     return IntRect();
2654 }
2655
2656 IntRect WebPagePrivate::getRecursiveVisibleWindowRect(ScrollView* view, bool noClipOfMainFrame)
2657 {
2658     ASSERT(m_mainFrame);
2659
2660     // Don't call this function asking to not clip the main frame providing only
2661     // the main frame. All that can be returned is the content rect which
2662     // isn't what this function is for.
2663     if (noClipOfMainFrame && view == m_mainFrame->view()) {
2664         ASSERT_NOT_REACHED();
2665         return IntRect(IntPoint::zero(), view->contentsSize());
2666     }
2667
2668     IntRect visibleWindowRect(view->contentsToWindow(view->visibleContentRect(false)));
2669     if (view->parent() && !(noClipOfMainFrame && view->parent() == m_mainFrame->view())) {
2670         // Intersect with parent visible rect.
2671         visibleWindowRect.intersect(getRecursiveVisibleWindowRect(view->parent(), noClipOfMainFrame));
2672     }
2673     return visibleWindowRect;
2674 }
2675
2676 void WebPagePrivate::assignFocus(Platform::FocusDirection direction)
2677 {
2678     ASSERT((int) Platform::FocusDirectionNone == (int) FocusDirectionNone);
2679     ASSERT((int) Platform::FocusDirectionForward == (int) FocusDirectionForward);
2680     ASSERT((int) Platform::FocusDirectionBackward == (int) FocusDirectionBackward);
2681
2682     // First we clear the focus, since we want to focus either initial or the last
2683     // focusable element in the webpage (according to the TABINDEX), or simply clear
2684     // the focus.
2685     clearFocusNode();
2686
2687     switch (direction) {
2688     case FocusDirectionForward:
2689     case FocusDirectionBackward:
2690         m_page->focusController()->setInitialFocus((FocusDirection) direction, 0);
2691         break;
2692     case FocusDirectionNone:
2693         break;
2694     default:
2695         ASSERT_NOT_REACHED();
2696     }
2697 }
2698
2699 void WebPage::assignFocus(Platform::FocusDirection direction)
2700 {
2701     if (d->m_page->defersLoading())
2702        return;
2703     d->assignFocus(direction);
2704 }
2705
2706 Platform::IntRect WebPagePrivate::focusNodeRect()
2707 {
2708     Frame* frame = focusedOrMainFrame();
2709     if (!frame)
2710         return Platform::IntRect();
2711
2712     Document* doc = frame->document();
2713     FrameView* view = frame->view();
2714     if (!doc || !view || view->needsLayout())
2715         return Platform::IntRect();
2716
2717     IntRect focusRect = rectForNode(doc->focusedNode());
2718     focusRect = adjustRectOffsetForFrameOffset(focusRect, doc->focusedNode());
2719     focusRect = mapToTransformed(focusRect);
2720     clipToTransformedContentsRect(focusRect);
2721     return focusRect;
2722 }
2723
2724 PassRefPtr<Node> WebPagePrivate::contextNode(TargetDetectionStrategy strategy)
2725 {
2726     EventHandler* eventHandler = focusedOrMainFrame()->eventHandler();
2727     const FatFingersResult lastFatFingersResult = m_touchEventHandler->lastFatFingersResult();
2728     bool isTouching = lastFatFingersResult.isValid() && strategy == RectBased;
2729
2730     // Check if we're using LinkToLink and the user is not touching the screen.
2731     if (m_webSettings->doesGetFocusNodeContext() && !isTouching) {
2732         RefPtr<Node> node;
2733         node = m_page->focusController()->focusedOrMainFrame()->document()->focusedNode();
2734         if (node) {
2735             IntRect visibleRect = IntRect(IntPoint(), actualVisibleSize());
2736             if (!visibleRect.intersects(getNodeWindowRect(node.get())))
2737                 return 0;
2738         }
2739         return node.release();
2740     }
2741
2742     // Check for text input.
2743     if (isTouching && lastFatFingersResult.isTextInput())
2744         return lastFatFingersResult.node(FatFingersResult::ShadowContentNotAllowed);
2745
2746     IntPoint contentPos;
2747     if (isTouching)
2748         contentPos = lastFatFingersResult.adjustedPosition();
2749     else
2750         contentPos = mapFromViewportToContents(m_lastMouseEvent.position());
2751
2752     if (strategy == RectBased) {
2753         FatFingersResult result = FatFingers(this, lastFatFingersResult.adjustedPosition(), FatFingers::Text).findBestPoint();
2754         return result.node(FatFingersResult::ShadowContentNotAllowed);
2755     }
2756
2757     HitTestResult result = eventHandler->hitTestResultAtPoint(contentPos, false /*allowShadowContent*/);
2758     return result.innerNode();
2759 }
2760
2761 static inline int distanceBetweenPoints(IntPoint p1, IntPoint p2)
2762 {
2763     // Change int to double, because (dy * dy) can cause int overflow in reality, e.g, (-46709 * -46709).
2764     double dx = static_cast<double>(p1.x() - p2.x());
2765     double dy = static_cast<double>(p1.y() - p2.y());
2766     return sqrt((dx * dx) + (dy * dy));
2767 }
2768
2769 Node* WebPagePrivate::bestNodeForZoomUnderPoint(const IntPoint& point)
2770 {
2771     IntPoint pt = mapFromTransformed(point);
2772     IntRect clickRect(pt.x() - blockClickRadius, pt.y() - blockClickRadius, 2 * blockClickRadius, 2 * blockClickRadius);
2773     Node* originalNode = nodeForZoomUnderPoint(point);
2774     if (!originalNode)
2775         return 0;
2776     Node* node = bestChildNodeForClickRect(originalNode, clickRect);
2777     return node ? adjustedBlockZoomNodeForZoomAndExpandingRatioLimits(node) : adjustedBlockZoomNodeForZoomAndExpandingRatioLimits(originalNode);
2778 }
2779
2780 Node* WebPagePrivate::bestChildNodeForClickRect(Node* parentNode, const IntRect& clickRect)
2781 {
2782     if (!parentNode)
2783         return 0;
2784
2785     int bestDistance = std::numeric_limits<int>::max();
2786
2787     Node* node = parentNode->firstChild();
2788     Node* bestNode = 0;
2789     for (; node; node = node->nextSibling()) {
2790         IntRect rect = rectForNode(node);
2791         if (!clickRect.intersects(rect))
2792             continue;
2793
2794         int distance = distanceBetweenPoints(rect.center(), clickRect.center());
2795         Node* bestChildNode = bestChildNodeForClickRect(node, clickRect);
2796         if (bestChildNode) {
2797             IntRect bestChildRect = rectForNode(bestChildNode);
2798             int bestChildDistance = distanceBetweenPoints(bestChildRect.center(), clickRect.center());
2799             if (bestChildDistance < distance && bestChildDistance < bestDistance) {
2800                 bestNode = bestChildNode;
2801                 bestDistance = bestChildDistance;
2802             } else {
2803                 if (distance < bestDistance) {
2804                     bestNode = node;
2805                     bestDistance = distance;
2806                 }
2807             }
2808         } else {
2809             if (distance < bestDistance) {
2810                 bestNode = node;
2811                 bestDistance = distance;
2812             }
2813         }
2814     }
2815
2816     return bestNode;
2817 }
2818
2819 double WebPagePrivate::maxBlockZoomScale() const
2820 {
2821     return std::min(maximumBlockZoomScale, maximumScale());
2822 }
2823
2824 Node* WebPagePrivate::nodeForZoomUnderPoint(const IntPoint& point)
2825 {
2826     if (!m_mainFrame)
2827         return 0;
2828
2829     HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(mapFromTransformed(point), false);
2830
2831     Node* node = result.innerNonSharedNode();
2832
2833     if (!node)
2834         return 0;
2835
2836     RenderObject* renderer = node->renderer();
2837     while (!renderer) {
2838         node = node->parentNode();
2839         renderer = node->renderer();
2840     }
2841
2842     return node;
2843 }
2844
2845 Node* WebPagePrivate::adjustedBlockZoomNodeForZoomAndExpandingRatioLimits(Node* node)
2846 {
2847     Node* initialNode = node;
2848     RenderObject* renderer = node->renderer();
2849     bool acceptableNodeSize = newScaleForBlockZoomRect(rectForNode(node), 1.0, 0) < maxBlockZoomScale();
2850     IntSize actualVisibleSize = this->actualVisibleSize();
2851
2852     while (!renderer || !acceptableNodeSize) {
2853         node = node->parentNode();
2854         IntRect nodeRect = rectForNode(node);
2855
2856         // Don't choose a node if the width of the node size is very close to the width of the actual visible size,
2857         // as block zoom can do nothing on such kind of node.
2858         if (!node || static_cast<double>(actualVisibleSize.width() - nodeRect.width()) / actualVisibleSize.width() < minimumExpandingRatio)
2859             return initialNode;
2860
2861         renderer = node->renderer();
2862         acceptableNodeSize = newScaleForBlockZoomRect(rectForNode(node), 1.0, 0) < maxBlockZoomScale();
2863     }
2864
2865     return node;
2866 }
2867
2868 bool WebPagePrivate::compareNodesForBlockZoom(Node* n1, Node* n2)
2869 {
2870     if (!n1 || !n2)
2871         return false;
2872
2873     return (n2 == n1) || n2->isDescendantOf(n1);
2874 }
2875
2876 double WebPagePrivate::newScaleForBlockZoomRect(const IntRect& rect, double oldScale, double margin)
2877 {
2878     if (rect.isEmpty())
2879         return std::numeric_limits<double>::max();
2880
2881     ASSERT(rect.width() + margin);
2882
2883     double newScale = oldScale * static_cast<double>(transformedActualVisibleSize().width()) / (rect.width() + margin);
2884
2885     return newScale;
2886 }
2887
2888 IntRect WebPagePrivate::rectForNode(Node* node)
2889 {
2890     if (!node)
2891         return IntRect();
2892
2893     RenderObject* renderer = node->renderer();
2894
2895     if (!renderer)
2896         return IntRect();
2897
2898     // Return rect in un-transformed content coordinates.
2899     IntRect blockRect;
2900
2901     // FIXME: Ensure this works with iframes.
2902     if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled && renderer->isText()) {
2903         RenderBlock* renderBlock = renderer->containingBlock();
2904         int xOffset = 0;
2905         int yOffset = 0;
2906         while (!renderBlock->isRoot()) {
2907             xOffset += renderBlock->x();
2908             yOffset += renderBlock->y();
2909             renderBlock = renderBlock->containingBlock();
2910         }
2911         const RenderText* renderText = toRenderText(renderer);
2912         IntRect linesBox = renderText->linesBoundingBox();
2913         blockRect = IntRect(xOffset + linesBox.x(), yOffset + linesBox.y(), linesBox.width(), linesBox.height());
2914     } else
2915         blockRect = renderer->absoluteClippedOverflowRect();
2916
2917     if (renderer->isText()) {
2918         RenderBlock* rb = renderer->containingBlock();
2919
2920         // Inefficient? Way to find width when floats intersect a block.
2921         int blockWidth = 0;
2922         int lineCount = rb->lineCount();
2923         for (int i = 0; i < lineCount; i++)
2924             blockWidth = max(blockWidth, rb->availableLogicalWidthForLine(i, false));
2925
2926         blockRect.setWidth(blockWidth);
2927         blockRect.setX(blockRect.x() + rb->logicalLeftOffsetForLine(1, false));
2928     }
2929
2930     // Strip off padding.
2931     if (renderer->style()->hasPadding()) {
2932         blockRect.setX(blockRect.x() + renderer->style()->paddingLeft().value());
2933         blockRect.setY(blockRect.y() + renderer->style()->paddingTop().value());
2934         blockRect.setWidth(blockRect.width() - renderer->style()->paddingRight().value());
2935         blockRect.setHeight(blockRect.height() - renderer->style()->paddingBottom().value());
2936     }
2937
2938     return blockRect;
2939 }
2940
2941 IntPoint WebPagePrivate::frameOffset(const Frame* frame) const
2942 {
2943     ASSERT(frame);
2944
2945     // FIXME: This function can be called when page is being destroyed and JS triggers selection change.
2946     // We could break the call chain at upper levels, but I think it is better to check the frame pointer
2947     // here because the pointer is explicitly cleared in WebPage::destroy().
2948     if (!mainFrame())
2949         return IntPoint();
2950
2951     // Convert 0,0 in the frame's coordinate system to window coordinates to
2952     // get the frame's global position, and return this position in the main
2953     // frame's coordinates.  (So the main frame's coordinates will be 0,0.)
2954     return mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint::zero()));
2955 }
2956
2957 IntRect WebPagePrivate::adjustRectOffsetForFrameOffset(const IntRect& rect, const Node* node)
2958 {
2959     if (!node)
2960         return rect;
2961
2962     // Adjust the offset of the rect if it is in an iFrame/frame or set of iFrames/frames.
2963     // FIXME: can we just use frameOffset instead of this big routine?
2964     const Node* tnode = node;
2965     IntRect adjustedRect = rect;
2966     do {
2967         Frame* frame = tnode->document()->frame();
2968         if (!frame)
2969             continue;
2970
2971         Node* ownerNode = static_cast<Node*>(frame->ownerElement());
2972         tnode = ownerNode;
2973         if (ownerNode && (ownerNode->hasTagName(HTMLNames::iframeTag) || ownerNode->hasTagName(HTMLNames::frameTag))) {
2974             IntRect iFrameRect;
2975             do {
2976                 iFrameRect = rectForNode(ownerNode);
2977                 adjustedRect.move(iFrameRect.x(), iFrameRect.y());
2978                 adjustedRect.intersect(iFrameRect);
2979                 ownerNode = ownerNode->parentNode();
2980             } while (iFrameRect.isEmpty() && ownerNode);
2981         } else
2982             break;
2983     } while (tnode = tnode->parentNode());
2984
2985     return adjustedRect;
2986 }
2987
2988 IntRect WebPagePrivate::blockZoomRectForNode(Node* node)
2989 {
2990     if (!node || contentsSize().isEmpty())
2991         return IntRect();
2992
2993     Node* tnode = node;
2994     m_currentBlockZoomAdjustedNode = tnode;
2995
2996     IntRect blockRect = rectForNode(tnode);
2997     IntRect originalRect = blockRect;
2998
2999     int originalArea = originalRect.width() * originalRect.height();
3000     int pageArea = contentsSize().width() * contentsSize().height();
3001     double blockToPageRatio = static_cast<double>(pageArea - originalArea) / pageArea;
3002     double blockExpansionRatio = 5.0 * blockToPageRatio * blockToPageRatio;
3003
3004     if (!tnode->hasTagName(HTMLNames::imgTag) && !tnode->hasTagName(HTMLNames::inputTag) && !tnode->hasTagName(HTMLNames::textareaTag)) {
3005         while (tnode = tnode->parentNode()) {
3006             ASSERT(tnode);
3007             IntRect tRect = rectForNode(tnode);
3008             int tempBlockArea = tRect.width() * tRect.height();
3009             // Don't expand the block if it will be too large relative to the content.
3010             if (static_cast<double>(pageArea - tempBlockArea) / pageArea < minimumExpandingRatio)
3011                 break;
3012             if (tRect.isEmpty())
3013                 continue; // No renderer.
3014             if (tempBlockArea < 1.1 * originalArea)
3015                 continue; // The size of this parent is very close to the child, no need to go to this parent.
3016             // Don't expand the block if the parent node size is already almost the size of actual visible size.
3017             IntSize actualSize = actualVisibleSize();
3018             if (static_cast<double>(actualSize.width() - tRect.width()) / actualSize.width() < minimumExpandingRatio)
3019                 break;
3020             if (tempBlockArea < blockExpansionRatio * originalArea) {
3021                 blockRect = tRect;
3022                 m_currentBlockZoomAdjustedNode = tnode;
3023             } else
3024                 break;
3025         }
3026     }
3027
3028     blockRect = adjustRectOffsetForFrameOffset(blockRect, node);
3029     blockRect = mapToTransformed(blockRect);
3030     clipToTransformedContentsRect(blockRect);
3031
3032 #if DEBUG_BLOCK_ZOOM
3033     if (!m_backingStore->d->isSuspended()) {
3034         // Re-paint the backingstore to screen to erase other annotations.
3035         if (m_backingStore->d->shouldDirectRenderingToWindow())
3036             m_backingStore->d->renderVisibleContents();
3037         else
3038             m_backingStore->d->blitVisibleContents();
3039
3040         // Render a black square over the calculated block and a gray square over the original block for visual inspection.
3041         originalRect = mapToTransformed(originalRect);
3042         clipToTransformedContentsRect(originalRect);
3043         IntRect renderRect = mapFromTransformedContentsToTransformedViewport(blockRect);
3044         IntRect originalRenderRect = mapFromTransformedContentsToTransformedViewport(originalRect);
3045         IntSize viewportSize = transformedViewportSize();
3046         renderRect.intersect(IntRect(0, 0, viewportSize.width(), viewportSize.height()));
3047         originalRenderRect.intersect(IntRect(0, 0, viewportSize.width(), viewportSize.height()));
3048         m_backingStore->d->clearWindow(renderRect, 0, 0, 0);
3049         m_backingStore->d->clearWindow(originalRenderRect, 120, 120, 120);
3050         m_backingStore->d->invalidateWindow(renderRect);
3051     }
3052 #endif
3053
3054     return blockRect;
3055 }
3056
3057 // This function should not be called directly.
3058 // It is called after the animation ends (see above).
3059 void WebPagePrivate::zoomBlock()
3060 {
3061     if (!m_mainFrame)
3062         return;
3063
3064     IntPoint anchor(roundUntransformedPoint(mapFromTransformedFloatPoint(m_finalBlockPoint)));
3065     bool willUseTextReflow = false;
3066
3067 #if ENABLE(VIEWPORT_REFLOW)
3068     willUseTextReflow = m_webPage->settings()->textReflowMode() != WebSettings::TextReflowDisabled;
3069     toggleTextReflowIfEnabledForBlockZoomOnly(m_shouldReflowBlock);
3070     setNeedsLayout();
3071 #endif
3072
3073     TransformationMatrix zoom;
3074     zoom.scale(m_blockZoomFinalScale);
3075     *m_transformationMatrix = zoom;
3076     m_client->resetBitmapZoomScale(m_blockZoomFinalScale);
3077     m_backingStore->d->suspendScreenAndBackingStoreUpdates();
3078     updateViewportSize();
3079
3080 #if ENABLE(VIEWPORT_REFLOW)
3081     requestLayoutIfNeeded();
3082     if (willUseTextReflow && m_shouldReflowBlock) {
3083         IntRect reflowedRect = rectForNode(m_currentBlockZoomAdjustedNode.get());
3084         reflowedRect = adjustRectOffsetForFrameOffset(reflowedRect, m_currentBlockZoomAdjustedNode.get());
3085         reflowedRect.move(roundTransformedPoint(m_finalBlockPointReflowOffset).x(), roundTransformedPoint(m_finalBlockPointReflowOffset).y());
3086         RenderObject* renderer = m_currentBlockZoomAdjustedNode->renderer();
3087         IntPoint topLeftPoint(reflowedRect.location());
3088         if (renderer && renderer->isText()) {
3089             ETextAlign textAlign = renderer->style()->textAlign();
3090             IntPoint textAnchor;
3091             switch (textAlign) {
3092             case CENTER:
3093             case WEBKIT_CENTER:
3094                 textAnchor = IntPoint(reflowedRect.x() + (reflowedRect.width() - actualVisibleSize().width()) / 2, topLeftPoint.y());
3095                 break;
3096             case LEFT:
3097             case WEBKIT_LEFT:
3098                 textAnchor = topLeftPoint;
3099                 break;
3100             case RIGHT:
3101             case WEBKIT_RIGHT:
3102                 textAnchor = IntPoint(reflowedRect.x() + reflowedRect.width() - actualVisibleSize().width(), topLeftPoint.y());
3103                 break;
3104             case TAAUTO:
3105             case JUSTIFY:
3106             default:
3107                 if (renderer->style()->isLeftToRightDirection())
3108                     textAnchor = topLeftPoint;
3109                 else
3110                     textAnchor = IntPoint(reflowedRect.x() + reflowedRect.width() - actualVisibleSize().width(), topLeftPoint.y());
3111                 break;
3112             }
3113             setScrollPosition(textAnchor);
3114         } else {
3115             renderer->style()->isLeftToRightDirection()
3116                 ? setScrollPosition(topLeftPoint)
3117                 : setScrollPosition(IntPoint(reflowedRect.x() + reflowedRect.width() - actualVisibleSize().width(), topLeftPoint.y()));
3118         }
3119     } else if (willUseTextReflow) {
3120         IntRect finalRect = rectForNode(m_currentBlockZoomAdjustedNode.get());
3121         finalRect = adjustRectOffsetForFrameOffset(finalRect, m_currentBlockZoomAdjustedNode.get());
3122         setScrollPosition(IntPoint(0, finalRect.y() + m_finalBlockPointReflowOffset.y()));
3123         resetBlockZoom();
3124     }
3125 #endif
3126     if (!willUseTextReflow) {
3127         setScrollPosition(anchor);
3128         if (!m_shouldReflowBlock)
3129             resetBlockZoom();
3130     }
3131
3132     notifyTransformChanged();
3133     m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::RenderAndBlit);
3134     m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
3135 }
3136
3137 void WebPage::blockZoomAnimationFinished()
3138 {
3139     d->zoomBlock();
3140 }
3141
3142 void WebPagePrivate::resetBlockZoom()
3143 {
3144     m_currentBlockZoomNode = 0;
3145     m_currentBlockZoomAdjustedNode = 0;
3146     m_shouldReflowBlock = false;
3147 }
3148
3149 void WebPage::destroyWebPageCompositor()
3150 {
3151 #if USE(ACCELERATED_COMPOSITING)
3152     // Destroy the layer renderer in a sync command before we destroy the backing store,
3153     // to flush any pending compositing messages on the compositing thread.
3154     // The backing store is indirectly deleted by the 'detachFromParent' call below.
3155     d->syncDestroyCompositorOnCompositingThread();
3156 #endif
3157 }
3158
3159 void WebPage::destroy()
3160 {
3161     // TODO: need to verify if this call needs to be made before calling
3162     // WebPage::destroyWebPageCompositor()
3163     d->m_backingStore->d->suspendScreenAndBackingStoreUpdates();
3164
3165     // Close the backforward list and release the cached pages.
3166     d->m_page->backForward()->close();
3167     pageCache()->releaseAutoreleasedPagesNow();
3168
3169     FrameLoader* loader = d->m_mainFrame->loader();
3170
3171     // Remove main frame's backing store client from the map
3172     // to prevent FrameLoaderClientBlackyBerry::detachFromParent2(),
3173     // which is called by loader->detachFromParent(), deleting it.
3174     // We will delete it in ~WebPagePrivate().
3175     // Reason: loader->detachFromParent() may ping back to backing store
3176     // indirectly through ChromeClientBlackBerry::invalidateContentsAndWindow().
3177     // see RIM PR #93256.
3178     d->removeBackingStoreClientForFrame(d->m_mainFrame);
3179
3180     // Set m_mainFrame to 0 to avoid calls back in to the backingstore during webpage deletion.
3181     d->m_mainFrame = 0;
3182     if (loader)
3183         loader->detachFromParent();
3184
3185     deleteGuardedObject(this);
3186 }
3187
3188 WebPageClient* WebPage::client() const
3189 {
3190     return d->m_client;
3191 }
3192
3193 int WebPage::backForwardListLength() const
3194 {
3195     return d->m_page->getHistoryLength();
3196 }
3197
3198 bool WebPage::canGoBackOrForward(int delta) const
3199 {
3200     return d->m_page->canGoBackOrForward(delta);
3201 }
3202
3203 bool WebPage::goBackOrForward(int delta)
3204 {
3205     if (d->m_page->canGoBackOrForward(delta)) {
3206         d->m_page->goBackOrForward(delta);
3207         return true;
3208     }
3209     return false;
3210 }
3211
3212 void WebPage::goToBackForwardEntry(BackForwardId id)
3213 {
3214     HistoryItem* item = historyItemFromBackForwardId(id);
3215     ASSERT(item);
3216     d->m_page->goToItem(item, FrameLoadTypeIndexedBackForward);
3217 }
3218
3219 void WebPage::reload()
3220 {
3221     d->m_mainFrame->loader()->reload(/* bypassCache */ true);
3222 }
3223
3224 void WebPage::reloadFromCache()
3225 {
3226     d->m_mainFrame->loader()->reload(/* bypassCache */ false);
3227 }
3228
3229 WebSettings* WebPage::settings() const
3230 {
3231     return d->m_webSettings;
3232 }
3233
3234 bool WebPage::isVisible() const
3235 {
3236     return d->m_visible;
3237 }
3238
3239 #if ENABLE(PAGE_VISIBILITY_API)
3240 class DeferredTaskSetPageVisibilityState: public DeferredTask<&WebPagePrivate::m_wouldSetPageVisibilityState> {
3241 public:
3242     explicit DeferredTaskSetPageVisibilityState(WebPagePrivate* webPagePrivate)
3243         : DeferredTaskType(webPagePrivate)
3244     {
3245     }
3246 private:
3247     virtual void performInternal(WebPagePrivate* webPagePrivate)
3248     {
3249         webPagePrivate->setPageVisibilityState();
3250     }
3251 };
3252
3253 void WebPagePrivate::setPageVisibilityState()
3254 {
3255     if (m_page->defersLoading())
3256         m_deferredTasks.append(adoptPtr(new DeferredTaskSetPageVisibilityState(this)));
3257     else {
3258         DeferredTaskSetPageVisibilityState::finishOrCancel(this);
3259
3260         static bool s_initialVisibilityState = true;
3261
3262         m_page->setVisibilityState(m_visible && m_activationState == ActivationActive ? PageVisibilityStateVisible : PageVisibilityStateHidden, s_initialVisibilityState);
3263         s_initialVisibilityState = false;
3264     }
3265 }
3266 #endif
3267
3268 void WebPagePrivate::setVisible(bool visible)
3269 {
3270     m_visible = visible;
3271
3272 #if ENABLE(PAGE_VISIBILITY_API)
3273     setPageVisibilityState();
3274 #endif
3275 }
3276
3277 void WebPage::setVisible(bool visible)
3278 {
3279     if (d->m_visible == visible)
3280         return;
3281
3282     d->setVisible(visible);
3283
3284     if (!visible) {
3285         d->suspendBackingStore();
3286
3287         // Remove this WebPage from the visible pages list.
3288         size_t foundIndex = visibleWebPages()->find(this);
3289         if (foundIndex != WTF::notFound)
3290             visibleWebPages()->remove(foundIndex);
3291
3292         // Return the backing store to the last visible WebPage.
3293         if (BackingStorePrivate::currentBackingStoreOwner() == this && !visibleWebPages()->isEmpty())
3294             visibleWebPages()->last()->d->resumeBackingStore();
3295
3296 #if USE(ACCELERATED_COMPOSITING)
3297         // Root layer commit is not necessary for invisible tabs.
3298         // And release layer resources can reduce memory consumption.
3299         d->suspendRootLayerCommit();
3300 #endif
3301
3302         return;
3303     }
3304
3305 #if USE(ACCELERATED_COMPOSITING)
3306     d->resumeRootLayerCommit();
3307 #endif
3308
3309     // Push this WebPage to the top of the visible pages list.
3310     if (!visibleWebPages()->isEmpty() && visibleWebPages()->last() != this) {
3311         size_t foundIndex = visibleWebPages()->find(this);
3312         if (foundIndex != WTF::notFound)
3313             visibleWebPages()->remove(foundIndex);
3314     }
3315     visibleWebPages()->append(this);
3316
3317     if (BackingStorePrivate::currentBackingStoreOwner()
3318         && BackingStorePrivate::currentBackingStoreOwner() != this)
3319         BackingStorePrivate::currentBackingStoreOwner()->d->suspendBackingStore();
3320
3321     // resumeBackingStore will set the current owner to this webpage.
3322     // If we set the owner prematurely, then the tiles will not be reset.
3323     d->resumeBackingStore();
3324 }
3325
3326 void WebPagePrivate::selectionChanged(Frame* frame)
3327 {
3328     m_inputHandler->selectionChanged();
3329
3330     // FIXME: This is a hack!
3331     // To ensure the selection being changed has its frame 'focused', lets
3332     // set it as focused ourselves (PR #104724).
3333     m_page->focusController()->setFocusedFrame(frame);
3334 }
3335
3336 void WebPagePrivate::updateDelegatedOverlays(bool dispatched)
3337 {
3338     // Track a dispatched message, we don't want to flood the webkit thread.
3339     // There can be as many as one more message enqued as needed but never less.
3340     if (dispatched)
3341         m_updateDelegatedOverlaysDispatched = false;
3342     else if (m_updateDelegatedOverlaysDispatched) {
3343         // Early return if there is message already pending on the webkit thread.
3344         return;
3345     }
3346
3347     if (Platform::webKitThreadMessageClient()->isCurrentThread()) {
3348         // Must be called on the WebKit thread.
3349         if (m_selectionHandler->isSelectionActive())
3350             m_selectionHandler->selectionPositionChanged();
3351         if (m_inspectorOverlay)
3352             m_inspectorOverlay->update();
3353
3354     } else if (m_selectionHandler->isSelectionActive()) {
3355         // Don't bother dispatching to webkit thread if selection and tap highlight are not active.
3356         m_updateDelegatedOverlaysDispatched = true;
3357         Platform::webKitThreadMessageClient()->dispatchMessage(Platform::createMethodCallMessage(&WebPagePrivate::updateDelegatedOverlays, this, true /*dispatched*/));
3358     }
3359 }
3360
3361 void WebPage::setCaretHighlightStyle(Platform::CaretHighlightStyle style)
3362 {
3363 }
3364
3365 bool WebPage::setBatchEditingActive(bool active)
3366 {
3367     return d->m_inputHandler->setBatchEditingActive(active);
3368 }
3369
3370 bool WebPage::setInputSelection(unsigned start, unsigned end)
3371 {
3372     if (d->m_page->defersLoading())
3373         return false;
3374     return d->m_inputHandler->setSelection(start, end);
3375 }
3376
3377 int WebPage::inputCaretPosition() const
3378 {
3379     return d->m_inputHandler->caretPosition();
3380 }
3381
3382 class DeferredTaskPopupListSelectMultiple: public DeferredTask<&WebPagePrivate::m_wouldPopupListSelectMultiple> {
3383 public:
3384     DeferredTaskPopupListSelectMultiple(WebPagePrivate* webPagePrivate, int size, const bool* selecteds) 
3385         : DeferredTaskType(webPagePrivate)
3386     {
3387         webPagePrivate->m_cachedPopupListSelecteds.append(selecteds, size);
3388     }
3389 private:
3390     virtual void performInternal(WebPagePrivate* webPagePrivate)
3391     {
3392         webPagePrivate->m_webPage->popupListClosed(webPagePrivate->m_cachedPopupListSelecteds.size(), webPagePrivate->m_cachedPopupListSelecteds.data());
3393     }
3394 };
3395
3396 class DeferredTaskPopupListSelectSingle: public DeferredTask<&WebPagePrivate::m_wouldPopupListSelectSingle> {
3397 public:
3398     explicit DeferredTaskPopupListSelectSingle(WebPagePrivate* webPagePrivate, int index)
3399         : DeferredTaskType(webPagePrivate)
3400     {
3401         webPagePrivate->m_cachedPopupListSelectedIndex = index;
3402     }
3403 private:
3404     virtual void performInternal(WebPagePrivate* webPagePrivate)
3405     {
3406         webPagePrivate->m_webPage->popupListClosed(webPagePrivate->m_cachedPopupListSelectedIndex);
3407     }
3408 };
3409
3410 void WebPage::popupListClosed(int size, const bool* selecteds)
3411 {
3412     DeferredTaskPopupListSelectSingle::finishOrCancel(d);
3413     if (d->m_page->defersLoading()) {
3414         d->m_deferredTasks.append(adoptPtr(new DeferredTaskPopupListSelectMultiple(d, size, selecteds)));
3415         return;
3416     }
3417     DeferredTaskPopupListSelectMultiple::finishOrCancel(d);
3418     d->m_inputHandler->setPopupListIndexes(size, selecteds);
3419 }
3420
3421 void WebPage::popupListClosed(int index)
3422 {
3423     DeferredTaskPopupListSelectMultiple::finishOrCancel(d);
3424     if (d->m_page->defersLoading()) {
3425         d->m_deferredTasks.append(adoptPtr(new DeferredTaskPopupListSelectSingle(d, index)));
3426         return;
3427     }
3428     DeferredTaskPopupListSelectSingle::finishOrCancel(d);
3429     d->m_inputHandler->setPopupListIndex(index);
3430 }
3431
3432 class DeferredTaskSetDateTimeInput: public DeferredTask<&WebPagePrivate::m_wouldSetDateTimeInput> {
3433 public:
3434     explicit DeferredTaskSetDateTimeInput(WebPagePrivate* webPagePrivate, WebString value)
3435         : DeferredTaskType(webPagePrivate)
3436     {
3437         webPagePrivate->m_cachedDateTimeInput = value;
3438     }
3439 private:
3440     virtual void performInternal(WebPagePrivate* webPagePrivate)
3441     {
3442         webPagePrivate->m_webPage->setDateTimeInput(webPagePrivate->m_cachedDateTimeInput);
3443     }
3444 };
3445
3446 void WebPage::setDateTimeInput(const WebString& value)
3447 {
3448     if (d->m_page->defersLoading()) {
3449         d->m_deferredTasks.append(adoptPtr(new DeferredTaskSetDateTimeInput(d, value)));
3450         return;
3451     }
3452     DeferredTaskSetDateTimeInput::finishOrCancel(d);
3453     d->m_inputHandler->setInputValue(String(value.impl()));
3454 }
3455
3456 class DeferredTaskSetColorInput: public DeferredTask<&WebPagePrivate::m_wouldSetColorInput> {
3457 public:
3458     explicit DeferredTaskSetColorInput(WebPagePrivate* webPagePrivate, WebString value)
3459         : DeferredTaskType(webPagePrivate)
3460     {
3461         webPagePrivate->m_cachedColorInput = value;
3462     }
3463 private:
3464     virtual void performInternal(WebPagePrivate* webPagePrivate)
3465     {
3466         webPagePrivate->m_webPage->setColorInput(webPagePrivate->m_cachedColorInput);
3467     }
3468 };
3469
3470 void WebPage::setColorInput(const WebString& value)
3471 {
3472     if (d->m_page->defersLoading()) {
3473         d->m_deferredTasks.append(adoptPtr(new DeferredTaskSetColorInput(d, value)));
3474         return;
3475     }
3476     DeferredTaskSetColorInput::finishOrCancel(d);
3477     d->m_inputHandler->setInputValue(String(value.impl()));
3478 }
3479
3480 void WebPage::setVirtualViewportSize(int width, int height)
3481 {
3482     d->m_virtualViewportWidth = width;
3483     d->m_virtualViewportHeight = height;
3484 }
3485
3486 void WebPage::resetVirtualViewportOnCommitted(bool reset)
3487 {
3488     d->m_resetVirtualViewportOnCommitted = reset;
3489 }
3490
3491 IntSize WebPagePrivate::recomputeVirtualViewportFromViewportArguments()
3492 {
3493     static const ViewportArguments defaultViewportArguments;
3494     if (m_viewportArguments == defaultViewportArguments)
3495         return IntSize();
3496
3497     int desktopWidth = defaultMaxLayoutSize().width();
3498     int deviceWidth = Platform::Graphics::Screen::primaryScreen()->width();
3499     int deviceHeight = Platform::Graphics::Screen::primaryScreen()->height();
3500     ViewportAttributes result = computeViewportAttributes(m_viewportArguments, desktopWidth, deviceWidth, deviceHeight, m_webSettings->devicePixelRatio(), m_defaultLayoutSize);
3501
3502     setUserScalable(m_webSettings->isUserScalable() && result.userScalable);
3503     if (result.initialScale > 0)
3504         setInitialScale(result.initialScale * result.devicePixelRatio);
3505     if (result.minimumScale > 0)
3506         setMinimumScale(result.minimumScale * result.devicePixelRatio);
3507     if (result.maximumScale > 0)