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