[BlackBerry] Add WebPageCompositor class to BlackBerry WebKit API
[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_rootLayerCommitTimer(adoptPtr(new Timer<WebPagePrivate>(this, &WebPagePrivate::rootLayerCommitTimerFired)))
332     , m_needsOneShotDrawingSynchronization(false)
333     , m_needsCommit(false)
334     , m_suspendRootLayerCommit(false)
335 #endif
336     , m_pendingOrientation(-1)
337     , m_fullscreenVideoNode(0)
338     , m_hasInRegionScrollableAreas(false)
339     , m_updateDelegatedOverlaysDispatched(false)
340 {
341     static bool isInitialized = false;
342     if (!isInitialized) {
343         isInitialized = true;
344         BlackBerry::Platform::DeviceInfo::instance();
345         defaultUserAgent();
346     }
347 }
348
349 WebPage::WebPage(WebPageClient* client, const WebString& pageGroupName, const Platform::IntRect& rect)
350 {
351     globalInitialize();
352     d = new WebPagePrivate(this, client, rect);
353     d->init(pageGroupName);
354 }
355
356 WebPagePrivate::~WebPagePrivate()
357 {
358     // Hand the backingstore back to another owner if necessary.
359     m_webPage->setVisible(false);
360     if (BackingStorePrivate::currentBackingStoreOwner() == m_webPage)
361         BackingStorePrivate::setCurrentBackingStoreOwner(0);
362
363     delete m_webSettings;
364     m_webSettings = 0;
365
366     delete m_backingStoreClient;
367     m_backingStoreClient = 0;
368     m_backingStore = 0;
369
370     delete m_page;
371     m_page = 0;
372
373     delete m_transformationMatrix;
374     m_transformationMatrix = 0;
375
376     delete m_inPageSearchManager;
377     m_inPageSearchManager = 0;
378
379     delete m_selectionHandler;
380     m_selectionHandler = 0;
381
382     delete m_inputHandler;
383     m_inputHandler = 0;
384
385     delete m_touchEventHandler;
386     m_touchEventHandler = 0;
387
388 #if ENABLE_DRT
389     delete m_dumpRenderTree;
390     m_dumpRenderTree = 0;
391 #endif
392 }
393
394 WebPage::~WebPage()
395 {
396     delete d;
397     d = 0;
398 }
399
400 Page* WebPagePrivate::core(const WebPage* webPage)
401 {
402     return webPage->d->m_page;
403 }
404
405 void WebPagePrivate::init(const WebString& pageGroupName)
406 {
407     ChromeClientBlackBerry* chromeClient = new ChromeClientBlackBerry(this);
408     ContextMenuClientBlackBerry* contextMenuClient = 0;
409 #if ENABLE(CONTEXT_MENUS)
410     contextMenuClient = new ContextMenuClientBlackBerry();
411 #endif
412     EditorClientBlackBerry* editorClient = new EditorClientBlackBerry(this);
413     DragClientBlackBerry* dragClient = 0;
414 #if ENABLE(DRAG_SUPPORT)
415     dragClient = new DragClientBlackBerry();
416 #endif
417     InspectorClientBlackBerry* inspectorClient = 0;
418 #if ENABLE(INSPECTOR)
419     inspectorClient = new InspectorClientBlackBerry(this);
420 #endif
421
422     FrameLoaderClientBlackBerry* frameLoaderClient = new FrameLoaderClientBlackBerry();
423
424     Page::PageClients pageClients;
425     pageClients.chromeClient = chromeClient;
426     pageClients.contextMenuClient = contextMenuClient;
427     pageClients.editorClient = editorClient;
428     pageClients.dragClient = dragClient;
429     pageClients.inspectorClient = inspectorClient;
430
431     // Note the object will be destroyed when the page is destroyed.
432 #if ENABLE_DRT
433     if (getenv("drtRun"))
434         pageClients.geolocationClient = new GeolocationClientMock();
435     else
436 #endif
437         pageClients.geolocationClient = m_geolocationClient = new GeolocationControllerClientBlackBerry(this);
438 #else
439     pageClients.geolocationClient = m_geolocationClient;
440
441     m_page = new Page(pageClients);
442     WebCore::provideDeviceOrientationTo(m_page, new DeviceOrientationClientBlackBerry(this));
443     WebCore::provideDeviceMotionTo(m_page, new DeviceMotionClientBlackBerry(this));
444
445 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
446     WebCore::provideNotification(m_page, NotificationPresenterImpl::instance());
447 #endif
448
449 #if ENABLE_DRT
450     // In case running in DumpRenderTree mode set the controller to mock provider.
451     if (getenv("drtRun"))
452         static_cast<GeolocationClientMock*>(pageClients.geolocationClient)->setController(m_page->geolocationController());
453 #endif
454
455     m_page->setCustomHTMLTokenizerChunkSize(256);
456     m_page->setCustomHTMLTokenizerTimeDelay(0.3);
457
458     m_webSettings = WebSettings::createFromStandardSettings();
459     m_webSettings->setUserAgentString(defaultUserAgent());
460
461     // FIXME: We explicitly call setDelegate() instead of passing ourself in createFromStandardSettings()
462     // so that we only get one didChangeSettings() callback when we set the page group name. This causes us
463     // to make a copy of the WebSettings since some WebSettings method make use of the page group name.
464     // Instead, we shouldn't be storing the page group name in WebSettings.
465     m_webSettings->setDelegate(this);
466     m_webSettings->setPageGroupName(pageGroupName);
467
468     RefPtr<Frame> newFrame = Frame::create(m_page, /* HTMLFrameOwnerElement* */ 0, frameLoaderClient);
469
470     m_mainFrame = newFrame.get();
471     frameLoaderClient->setFrame(m_mainFrame, this);
472     m_mainFrame->init();
473
474 #if ENABLE(WEBGL)
475     Platform::Settings* settings = Platform::Settings::get();
476     m_page->settings()->setWebGLEnabled(settings && settings->isWebGLSupported());
477 #endif
478 #if ENABLE(ACCELERATED_2D_CANVAS)
479     m_page->settings()->setCanvasUsesAcceleratedDrawing(true);
480     m_page->settings()->setAccelerated2dCanvasEnabled(true);
481 #endif
482 #if ENABLE(VIEWPORT_REFLOW)
483     m_page->settings()->setTextReflowEnabled(m_webSettings->textReflowMode() == WebSettings::TextReflowEnabled);
484 #endif
485
486     m_page->settings()->setUseHixie76WebSocketProtocol(false);
487     m_page->settings()->setInteractiveFormValidationEnabled(true);
488     m_page->settings()->setAllowUniversalAccessFromFileURLs(false);
489     m_page->settings()->setAllowFileAccessFromFileURLs(false);
490
491     m_backingStoreClient = BackingStoreClient::create(m_mainFrame, /* parent frame */ 0, m_webPage);
492     // The direct access to BackingStore is left here for convenience since it
493     // is owned by BackingStoreClient and then deleted by its destructor.
494     m_backingStore = m_backingStoreClient->backingStore();
495
496     m_page->settings()->setSpatialNavigationEnabled(m_webSettings->isSpatialNavigationEnabled());
497     blockClickRadius = int(roundf(0.35 * Platform::Graphics::Screen::primaryScreen()->pixelsPerInch(0).width())); // The clicked rectangle area should be a fixed unit of measurement.
498
499     m_page->settings()->setDelegateSelectionPaint(true);
500
501 #if ENABLE(REQUEST_ANIMATION_FRAME)
502     m_page->windowScreenDidChange((PlatformDisplayID)0);
503 #endif
504
505 #if ENABLE(WEB_TIMING)
506     m_page->settings()->setMemoryInfoEnabled(true);
507 #endif
508 }
509
510 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)
511 {
512     stopCurrentLoad();
513
514     String urlString(url);
515     if (urlString.startsWith("vs:", false)) {
516         urlString = urlString.substring(3);
517         m_mainFrame->setInViewSourceMode(true);
518     } else
519         m_mainFrame->setInViewSourceMode(false);
520
521     KURL kurl = parseUrl(urlString);
522     if (protocolIs(kurl, "javascript")) {
523         // Never run javascript while loading is deferred.
524         if (m_page->defersLoading()) {
525             FrameLoaderClientBlackBerry* frameLoaderClient = static_cast<FrameLoaderClientBlackBerry*>(m_mainFrame->loader()->client());
526             frameLoaderClient->setDeferredManualScript(kurl);
527         } else
528             m_mainFrame->script()->executeIfJavaScriptURL(kurl, DoNotReplaceDocumentIfJavaScriptURL);
529         return;
530     }
531
532     if (isInitial)
533         NetworkManager::instance()->setInitialURL(kurl);
534
535     ResourceRequest request(kurl, "" /* referrer */);
536     request.setToken(networkToken);
537     if (isInitial || mustHandleInternally)
538         request.setMustHandleInternally(true);
539     request.setHTTPMethod(method);
540     request.setCachePolicy(toWebCoreCachePolicy(cachePolicy));
541     if (overrideContentType)
542         request.setOverrideContentType(overrideContentType);
543
544     if (data)
545         request.setHTTPBody(FormData::create(data, dataLength));
546
547     for (unsigned i = 0; i + 1 < headersLength; i += 2)
548         request.addHTTPHeaderField(headers[i], headers[i + 1]);
549
550     if (forceDownload)
551         request.setForceDownload(true);
552
553     m_mainFrame->loader()->load(request, "" /* name */, false);
554 }
555
556 void WebPage::load(const char* url, const char* networkToken, bool isInitial)
557 {
558     d->load(url, networkToken, "GET", Platform::NetworkRequest::UseProtocolCachePolicy, 0, 0, 0, 0, isInitial, false);
559 }
560
561 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)
562 {
563     d->load(url, networkToken, method, cachePolicy, data, dataLength, headers, headersLength, false, mustHandleInternally, false, "");
564 }
565
566 void WebPage::loadFile(const char* path, const char* overrideContentType)
567 {
568     std::string fileUrl(path);
569     if (!fileUrl.find("/"))
570         fileUrl.insert(0, "file://");
571     else if (fileUrl.find("file:///"))
572         return;
573
574     d->load(fileUrl.c_str(), 0, "GET", Platform::NetworkRequest::UseProtocolCachePolicy, 0, 0, 0, 0, false, false, false, overrideContentType);
575 }
576
577 void WebPage::download(const Platform::NetworkRequest& request)
578 {
579     d->load(request.getUrlRef().c_str(), 0, "GET", Platform::NetworkRequest::UseProtocolCachePolicy, 0, 0, 0, 0, false, false, true, "");
580 }
581
582 void WebPagePrivate::loadString(const char* string, const char* baseURL, const char* contentType, const char* failingURL)
583 {
584     KURL kurl = parseUrl(baseURL);
585     ResourceRequest request(kurl);
586     WTF::RefPtr<SharedBuffer> buffer
587         = SharedBuffer::create(string, strlen(string));
588     SubstituteData substituteData(buffer,
589                                   extractMIMETypeFromMediaType(contentType),
590                                   extractCharsetFromMediaType(contentType),
591                                   failingURL ? parseUrl(failingURL) : KURL());
592     m_mainFrame->loader()->load(request, substituteData, false);
593 }
594
595 void WebPage::loadString(const char* string, const char* baseURL, const char* mimeType, const char* failingURL)
596 {
597     d->loadString(string, baseURL, mimeType, failingURL);
598 }
599
600 bool WebPagePrivate::executeJavaScript(const char* script, JavaScriptDataType& returnType, WebString& returnValue)
601 {
602     ScriptValue result = m_mainFrame->script()->executeScript(String::fromUTF8(script), false);
603     JSC::JSValue value = result.jsValue();
604     if (!value) {
605         returnType = JSException;
606         return false;
607     }
608
609     JSC::ExecState* exec = m_mainFrame->script()->globalObject(mainThreadNormalWorld())->globalExec();
610     JSGlobalContextRef context = toGlobalRef(exec);
611
612     JSType type = JSValueGetType(context, toRef(exec, value));
613
614     switch (type) {
615     case kJSTypeNull:
616         returnType = JSNull;
617         break;
618     case kJSTypeBoolean:
619         returnType = JSBoolean;
620         break;
621     case kJSTypeNumber:
622         returnType = JSNumber;
623         break;
624     case kJSTypeString:
625         returnType = JSString;
626         break;
627     case kJSTypeObject:
628         returnType = JSObject;
629         break;
630     case kJSTypeUndefined:
631     default:
632         returnType = JSUndefined;
633         break;
634     }
635
636     if (returnType == JSBoolean || returnType == JSNumber || returnType == JSString || returnType == JSObject) {
637         String str = result.toString(exec);
638         returnValue = WebString(str.impl());
639     }
640
641     return true;
642 }
643
644 bool WebPage::executeJavaScript(const char* script, JavaScriptDataType& returnType, WebString& returnValue)
645 {
646     return d->executeJavaScript(script, returnType, returnValue);
647 }
648
649 bool WebPagePrivate::executeJavaScriptInIsolatedWorld(const ScriptSourceCode& sourceCode, JavaScriptDataType& returnType, WebString& returnValue)
650 {
651     if (!m_isolatedWorld)
652         m_isolatedWorld = m_mainFrame->script()->createWorld();
653
654     // Use evaluateInWorld to avoid canExecuteScripts check.
655     ScriptValue result = m_mainFrame->script()->evaluateInWorld(sourceCode, m_isolatedWorld.get());
656     JSC::JSValue value = result.jsValue();
657     if (!value) {
658         returnType = JSException;
659         return false;
660     }
661
662     JSC::ExecState* exec = m_mainFrame->script()->globalObject(m_isolatedWorld.get())->globalExec();
663     JSGlobalContextRef context = toGlobalRef(exec);
664
665     JSType type = JSValueGetType(context, toRef(exec, value));
666
667     switch (type) {
668     case kJSTypeNull:
669         returnType = JSNull;
670         break;
671     case kJSTypeBoolean:
672         returnType = JSBoolean;
673         break;
674     case kJSTypeNumber:
675         returnType = JSNumber;
676         break;
677     case kJSTypeString:
678         returnType = JSString;
679         break;
680     case kJSTypeObject:
681         returnType = JSObject;
682         break;
683     case kJSTypeUndefined:
684     default:
685         returnType = JSUndefined;
686         break;
687     }
688
689     if (returnType == JSBoolean || returnType == JSNumber || returnType == JSString || returnType == JSObject) {
690         String str = result.toString(exec);
691         returnValue = WebString(str.impl());
692     }
693
694     return true;
695 }
696
697 bool WebPage::executeJavaScriptInIsolatedWorld(const std::wstring& script, JavaScriptDataType& returnType, WebString& returnValue)
698 {
699     // On our platform wchar_t is unsigned int and UChar is unsigned short
700     // so we have to convert using ICU conversion function
701     int lengthCopied = 0;
702     UErrorCode error = U_ZERO_ERROR;
703     const int length = script.length() + 1 /*null termination char*/;
704     UChar data[length];
705
706     // FIXME: PR 138162 is giving U_INVALID_CHAR_FOUND error.
707     u_strFromUTF32(data, length, &lengthCopied, reinterpret_cast<const UChar32*>(script.c_str()), script.length(), &error);
708     BLACKBERRY_ASSERT(error == U_ZERO_ERROR);
709     if (error != U_ZERO_ERROR) {
710         Platform::logAlways(Platform::LogLevelCritical, "WebPage::executeJavaScriptInIsolatedWorld failed to convert UTF16 to JavaScript!");
711         return false;
712     }
713     String str = String(data, lengthCopied);
714     ScriptSourceCode sourceCode(str, KURL());
715     return d->executeJavaScriptInIsolatedWorld(sourceCode, returnType, returnValue);
716 }
717
718 bool WebPage::executeJavaScriptInIsolatedWorld(const char* script, JavaScriptDataType& returnType, WebString& returnValue)
719 {
720     ScriptSourceCode sourceCode(String::fromUTF8(script), KURL());
721     return d->executeJavaScriptInIsolatedWorld(sourceCode, returnType, returnValue);
722 }
723
724 void WebPagePrivate::stopCurrentLoad()
725 {
726     // This function should contain all common code triggered by WebPage::load
727     // (which stops any load in progress before starting the new load) and
728     // WebPage::stoploading (the entry point for the client to stop the load
729     // explicitly). If it should only be done while stopping the load
730     // explicitly, it goes in WebPage::stopLoading, not here.
731     m_mainFrame->loader()->stopAllLoaders();
732
733     // Cancel any deferred script that hasn't been processed yet.
734     FrameLoaderClientBlackBerry* frameLoaderClient = static_cast<FrameLoaderClientBlackBerry*>(m_mainFrame->loader()->client());
735     frameLoaderClient->setDeferredManualScript(KURL());
736 }
737
738 void WebPage::stopLoading()
739 {
740     d->stopCurrentLoad();
741 }
742
743 static void closeURLRecursively(Frame* frame)
744 {
745     // Do not create more frame please.
746     FrameLoaderClientBlackBerry* frameLoaderClient = static_cast<FrameLoaderClientBlackBerry*>(frame->loader()->client());
747     frameLoaderClient->suppressChildFrameCreation();
748
749     frame->loader()->closeURL();
750
751     Vector<RefPtr<Frame>, 10> childFrames;
752
753     for (RefPtr<Frame> childFrame = frame->tree()->firstChild(); childFrame; childFrame = childFrame->tree()->nextSibling())
754         childFrames.append(childFrame);
755
756     unsigned size = childFrames.size();
757     for (unsigned i = 0; i < size; i++)
758         closeURLRecursively(childFrames[i].get());
759 }
760
761 void WebPagePrivate::prepareToDestroy()
762 {
763     // Before the client starts tearing itself down, dispatch the unload event
764     // so it can take effect while all the client's state (e.g. scroll position)
765     // is still present.
766     closeURLRecursively(m_mainFrame);
767 }
768
769 void WebPage::prepareToDestroy()
770 {
771     d->prepareToDestroy();
772 }
773
774 void WebPagePrivate::setLoadState(LoadState state)
775 {
776     if (m_loadState == state)
777         return;
778
779     bool isFirstLoad = m_loadState == None;
780
781     // See RIM Bug #1068.
782     if (state == Finished && m_mainFrame && m_mainFrame->document())
783         m_mainFrame->document()->updateStyleIfNeeded();
784
785     m_loadState = state;
786
787 #if DEBUG_WEBPAGE_LOAD
788     Platform::log(Platform::LogLevelInfo, "WebPagePrivate::setLoadState %d", state);
789 #endif
790
791     switch (m_loadState) {
792     case Provisional:
793         if (isFirstLoad) {
794             // Paints the visible backingstore as white to prevent initial checkerboard on
795             // the first blit.
796             if (m_backingStore->d->renderVisibleContents() && !m_backingStore->d->isSuspended() && !m_backingStore->d->shouldDirectRenderingToWindow())
797                 m_backingStore->d->blitVisibleContents();
798         }
799         break;
800     case Committed:
801         {
802             unscheduleZoomAboutPoint();
803
804 #if ENABLE(ACCELERATED_2D_CANVAS)
805             if (m_page->settings()->canvasUsesAcceleratedDrawing()) {
806                 // Free GPU resources as we're on a new page.
807                 // This will help us to free memory pressure.
808                 SharedGraphicsContext3D::get()->makeContextCurrent();
809                 GrContext* grContext = Platform::Graphics::getGrContext();
810                 grContext->freeGpuResources();
811             }
812 #endif
813
814 #if USE(ACCELERATED_COMPOSITING)
815             // FIXME: compositor may only be touched on the compositing thread.
816             // However, it's created/destroyed by a sync command so this is harmless.
817             if (m_compositor) {
818                 m_compositor->setLayoutRectForCompositing(IntRect());
819                 m_compositor->setContentsSizeForCompositing(IntSize());
820             }
821 #endif
822             m_previousContentsSize = IntSize();
823             m_backingStore->d->resetRenderQueue();
824             m_backingStore->d->resetTiles(true /* resetBackground */);
825             m_backingStore->d->setScrollingOrZooming(false, false /* shouldBlit */);
826             m_userPerformedManualZoom = false;
827             m_userPerformedManualScroll = false;
828             m_shouldUseFixedDesktopMode = false;
829             if (m_resetVirtualViewportOnCommitted) { // For DRT.
830                 m_virtualViewportWidth = 0;
831                 m_virtualViewportHeight = 0;
832             }
833             if (m_webSettings->viewportWidth() > 0) {
834                 m_virtualViewportWidth = m_webSettings->viewportWidth();
835                 m_virtualViewportHeight = m_defaultLayoutSize.height();
836             }
837             // Check if we have already process the meta viewport tag, this only happens on history navigation
838             if (!m_didRestoreFromPageCache) {
839                 m_viewportArguments = ViewportArguments();
840                 m_userScalable = m_webSettings->isUserScalable();
841                 resetScales();
842             } else {
843                 IntSize virtualViewport = recomputeVirtualViewportFromViewportArguments();
844                 m_webPage->setVirtualViewportSize(virtualViewport.width(), virtualViewport.height());
845             }
846
847 #if ENABLE(EVENT_MODE_METATAGS)
848             didReceiveCursorEventMode(ProcessedCursorEvents);
849             didReceiveTouchEventMode(ProcessedTouchEvents);
850 #endif
851
852             // If it's a outmost SVG document, we use FixedDesktop mode, otherwise
853             // we default to Mobile mode. For example, using FixedDesktop mode to
854             // render http://www.croczilla.com/bits_and_pieces/svg/samples/tiger/tiger.svg
855             // is user-experience friendly.
856             if (m_page->mainFrame()->document()->isSVGDocument()) {
857                 setShouldUseFixedDesktopMode(true);
858                 setViewMode(FixedDesktop);
859             } else
860                 setViewMode(Mobile);
861
862             // Reset block zoom and reflow.
863             resetBlockZoom();
864 #if ENABLE(VIEWPORT_REFLOW)
865             toggleTextReflowIfEnabledForBlockZoomOnly();
866 #endif
867
868             // Notify InputHandler of state change.
869             m_inputHandler->enableInputMode(false);
870
871             // Set the scroll to origin here and notify the client since we'll be
872             // zooming below without any real contents yet thus the contents size
873             // we report to the client could make our current scroll position invalid.
874             setScrollPosition(IntPoint::zero());
875             notifyTransformedScrollChanged();
876
877             // Paints the visible backingstore as white. Note it is important we do
878             // this strictly after re-setting the scroll position to origin and resetting
879             // the scales otherwise the visible contents calculation is wrong and we
880             // can end up blitting artifacts instead. See: RIM Bug #401.
881             if (m_backingStore->d->renderVisibleContents() && !m_backingStore->d->isSuspended() && !m_backingStore->d->shouldDirectRenderingToWindow())
882                 m_backingStore->d->blitVisibleContents();
883
884             zoomToInitialScaleOnLoad();
885
886             // Update cursor status.
887             updateCursor();
888
889 #if USE(ACCELERATED_COMPOSITING)
890             // Don't render compositing contents from previous page.
891             resetCompositingSurface();
892 #endif
893             break;
894         }
895     case Finished:
896     case Failed:
897         // Notify client of the initial zoom change.
898         m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
899         m_backingStore->d->updateTiles(true /* updateVisible */, false /* immediate */);
900         break;
901     default:
902         break;
903     }
904 }
905
906 double WebPagePrivate::clampedScale(double scale) const
907 {
908     if (scale < minimumScale())
909         return minimumScale();
910     if (scale > maximumScale())
911         return maximumScale();
912     return scale;
913 }
914
915 bool WebPagePrivate::shouldZoomAboutPoint(double scale, const FloatPoint&, bool enforceScaleClamping, double* clampedScale)
916 {
917     if (!m_mainFrame->view())
918         return false;
919
920     if (enforceScaleClamping)
921         scale = this->clampedScale(scale);
922
923     ASSERT(clampedScale);
924     *clampedScale = scale;
925
926     if (currentScale() == scale) {
927         // Make sure backingstore updates resume from pinch zoom in the case where the final zoom level doesn't change.
928         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::None);
929         m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
930         return false;
931     }
932
933     return true;
934 }
935
936 bool WebPagePrivate::zoomAboutPoint(double unclampedScale, const FloatPoint& anchor, bool enforceScaleClamping, bool forceRendering, bool isRestoringZoomLevel)
937 {
938     if (!isRestoringZoomLevel) {
939         // Clear any existing block zoom.  (If we are restoring a saved zoom level on page load,
940         // there is guaranteed to be no existing block zoom and we don't want to clear m_shouldReflowBlock.)
941         resetBlockZoom();
942     }
943
944     // The reflow and block zoom stuff here needs to happen regardless of
945     // whether we shouldZoomAboutPoint.
946 #if ENABLE(VIEWPORT_REFLOW)
947     toggleTextReflowIfEnabledForBlockZoomOnly(m_shouldReflowBlock);
948     if (m_page->settings()->isTextReflowEnabled() && m_mainFrame->view())
949         setNeedsLayout();
950 #endif
951
952     double scale;
953     if (!shouldZoomAboutPoint(unclampedScale, anchor, enforceScaleClamping, &scale)) {
954         if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled) {
955             m_currentPinchZoomNode = 0;
956             m_anchorInNodeRectRatio = FloatPoint(-1, -1);
957         }
958         return false;
959     }
960     TransformationMatrix zoom;
961     zoom.scale(scale);
962
963 #if DEBUG_WEBPAGE_LOAD
964     if (loadState() < Finished)
965         Platform::log(Platform::LogLevelInfo, "WebPagePrivate::zoomAboutPoint scale %f anchor (%f, %f)", scale, anchor.x(), anchor.y());
966 #endif
967
968     // Our current scroll position in float.
969     FloatPoint scrollPosition = this->scrollPosition();
970
971     // Anchor offset from scroll position in float.
972     FloatPoint anchorOffset(anchor.x() - scrollPosition.x(), anchor.y() - scrollPosition.y());
973
974     // The horizontal scaling factor and vertical scaling factor should be equal
975     // to preserve aspect ratio of content.
976     ASSERT(m_transformationMatrix->m11() == m_transformationMatrix->m22());
977
978     // Need to invert the previous transform to anchor the viewport.
979     double inverseScale = scale / m_transformationMatrix->m11();
980
981     // Actual zoom.
982     *m_transformationMatrix = zoom;
983
984     // Suspend all screen updates to the backingstore.
985     m_backingStore->d->suspendScreenAndBackingStoreUpdates();
986
987     updateViewportSize();
988
989     IntPoint newScrollPosition(IntPoint(max(0, static_cast<int>(roundf(anchor.x() - anchorOffset.x() / inverseScale))),
990                                         max(0, static_cast<int>(roundf(anchor.y() - anchorOffset.y() / inverseScale)))));
991
992     if (m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled) {
993         // This is a hack for email which has reflow always turned on.
994         m_mainFrame->view()->setNeedsLayout();
995         requestLayoutIfNeeded();
996         if (m_currentPinchZoomNode)
997             newScrollPosition = calculateReflowedScrollPosition(anchorOffset, scale == minimumScale() ? 1 : inverseScale);
998          m_currentPinchZoomNode = 0;
999          m_anchorInNodeRectRatio = FloatPoint(-1, -1);
1000     }
1001
1002     setScrollPosition(newScrollPosition);
1003
1004     notifyTransformChanged();
1005
1006     bool isLoading = this->isLoading();
1007
1008     // We need to invalidate all tiles both visible and non-visible if we're loading.
1009     m_backingStore->d->updateTiles(isLoading /* updateVisible */, false /* immediate */);
1010
1011     m_client->resetBitmapZoomScale(m_transformationMatrix->m11());
1012
1013     bool shouldRender = !isLoading || m_userPerformedManualZoom || forceRendering;
1014     bool shouldClearVisibleZoom = isLoading && shouldRender;
1015
1016     if (shouldClearVisibleZoom) {
1017         // If we are loading and rendering then we need to clear the render queue's
1018         // visible zoom jobs as they will be irrelevant with the render below.
1019         m_backingStore->d->clearVisibleZoom();
1020     }
1021
1022     // Clear window to make sure there are no artifacts.
1023     if (shouldRender) {
1024         m_backingStore->d->clearWindow();
1025         // Resume all screen updates to the backingstore and render+blit visible contents to screen.
1026         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::RenderAndBlit);
1027     } else {
1028         // Resume all screen updates to the backingstore but do not blit to the screen because we not rendering.
1029         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::None);
1030     }
1031
1032     m_client->zoomChanged(m_webPage->isMinZoomed(), m_webPage->isMaxZoomed(), !shouldZoomOnEscape(), currentScale());
1033
1034     return true;
1035 }
1036
1037 IntPoint WebPagePrivate::calculateReflowedScrollPosition(const FloatPoint& anchorOffset, double inverseScale)
1038 {
1039     // Should only be invoked when text reflow is enabled.
1040     ASSERT(m_webPage->settings()->textReflowMode() == WebSettings::TextReflowEnabled);
1041
1042     int offsetY = 0;
1043     int offsetX = 0;
1044
1045     IntRect nodeRect = rectForNode(m_currentPinchZoomNode.get());
1046
1047     if (m_currentPinchZoomNode->renderer() && m_anchorInNodeRectRatio.y() >= 0) {
1048         offsetY = nodeRect.height() * m_anchorInNodeRectRatio.y();
1049         if (m_currentPinchZoomNode->renderer()->isImage() && m_anchorInNodeRectRatio.x() > 0)
1050             offsetX = nodeRect.width() * m_anchorInNodeRectRatio.x() - anchorOffset.x() / inverseScale;
1051     }
1052
1053     IntRect reflowedRect = adjustRectOffsetForFrameOffset(nodeRect, m_currentPinchZoomNode.get());
1054
1055     return IntPoint(max(0, static_cast<int>(roundf(reflowedRect.x() + offsetX))),
1056                     max(0, static_cast<int>(roundf(reflowedRect.y() + offsetY - anchorOffset.y() / inverseScale))));
1057 }
1058
1059 bool WebPagePrivate::scheduleZoomAboutPoint(double unclampedScale, const FloatPoint& anchor, bool enforceScaleClamping, bool forceRendering)
1060 {
1061     double scale;
1062     if (!shouldZoomAboutPoint(unclampedScale, anchor, enforceScaleClamping, &scale)) {
1063         // We could be back to the right zoom level before the timer has
1064         // timed out, because of wiggling back and forth. Stop the timer.
1065         unscheduleZoomAboutPoint();
1066         return false;
1067     }
1068
1069     // For some reason, the bitmap zoom wants an anchor in backingstore coordinates!
1070     // this is different from zoomAboutPoint, which wants content coordinates.
1071     // See RIM Bug #641.
1072
1073     FloatPoint transformedAnchor = mapToTransformedFloatPoint(anchor);
1074     FloatPoint transformedScrollPosition = mapToTransformedFloatPoint(scrollPosition());
1075
1076     // Prohibit backingstore from updating the window overtop of the bitmap.
1077     m_backingStore->d->suspendScreenAndBackingStoreUpdates();
1078
1079     // Need to invert the previous transform to anchor the viewport.
1080     double zoomFraction = scale / transformationMatrix()->m11();
1081
1082     // Anchor offset from scroll position in float.
1083     FloatPoint anchorOffset(transformedAnchor.x() - transformedScrollPosition.x(),
1084                             transformedAnchor.y() - transformedScrollPosition.y());
1085
1086     IntPoint srcPoint(
1087         static_cast<int>(roundf(transformedAnchor.x() - anchorOffset.x() / zoomFraction)),
1088         static_cast<int>(roundf(transformedAnchor.y() - anchorOffset.y() / zoomFraction)));
1089
1090     const IntRect viewportRect = IntRect(IntPoint::zero(), transformedViewportSize());
1091     const IntRect dstRect = viewportRect;
1092
1093     // This is the rect to pass as the actual source rect in the backingstore
1094     // for the transform given by zoom.
1095     IntRect srcRect(srcPoint.x(),
1096                     srcPoint.y(),
1097                     viewportRect.width() / zoomFraction,
1098                     viewportRect.height() / zoomFraction);
1099     m_backingStore->d->blitContents(dstRect, srcRect);
1100
1101     m_delayedZoomArguments.scale = scale;
1102     m_delayedZoomArguments.anchor = anchor;
1103     m_delayedZoomArguments.enforceScaleClamping = enforceScaleClamping;
1104     m_delayedZoomArguments.forceRendering = forceRendering;
1105     m_delayedZoomTimer->startOneShot(delayedZoomInterval);
1106
1107     return true;
1108 }
1109
1110 void WebPagePrivate::unscheduleZoomAboutPoint()
1111 {
1112     if (m_delayedZoomTimer->isActive())
1113         m_backingStore->d->resumeScreenAndBackingStoreUpdates(BackingStore::None);
1114
1115     m_delayedZoomTimer->stop();
1116 }
1117
1118 void WebPagePrivate::zoomAboutPointTimerFired(Timer<WebPagePrivate>*)
1119 {
1120     zoomAboutPoint(m_delayedZoomArguments.scale, m_delayedZoomArguments.anchor, m_delayedZoomArguments.enforceScaleClamping, m_delayedZoomArguments.forceRendering);
1121 }
1122
1123 void WebPagePrivate::setNeedsLayout()
1124 {
1125     FrameView* view = m_mainFrame->view();
1126     ASSERT(view);
1127     view->setNeedsLayout();
1128 }
1129
1130 void WebPagePrivate::requestLayoutIfNeeded() const
1131 {
1132     FrameView* view = m_mainFrame->view();
1133     ASSERT(view);
1134     view->updateLayoutAndStyleIfNeededRecursive();
1135     ASSERT(!view->needsLayout());
1136 }
1137
1138 IntPoint WebPagePrivate::scrollPosition() const
1139 {
1140     return m_backingStoreClient->scrollPosition();
1141 }
1142
1143 IntPoint WebPagePrivate::maximumScrollPosition() const
1144 {
1145     return m_backingStoreClient->maximumScrollPosition();
1146 }
1147
1148 void WebPagePrivate::setScrollPosition(const IntPoint& pos)
1149 {
1150     m_backingStoreClient->setScrollPosition(pos);
1151 }
1152
1153 // Setting the scroll position is in transformed coordinates.
1154 void WebPage::setScrollPosition(const Platform::IntPoint& point)
1155 {
1156     if (d->transformedPointEqualsUntransformedPoint(point, d->scrollPosition()))
1157         return;
1158
1159     // If the user recently performed an event, this new scroll position
1160     // could possibly be a result of that. Or not, this is just a heuristic.
1161     if (currentTime() - d->m_lastUserEventTimestamp < manualScrollInterval)
1162         d->m_userPerformedManualScroll = true;
1163
1164     d->m_backingStoreClient->setIsClientGeneratedScroll(true);
1165     d->m_mainFrame->view()->setCanOverscroll(true);
1166     d->setScrollPosition(d->mapFromTransformed(point));
1167     d->m_mainFrame->view()->setCanOverscroll(false);
1168     d->m_backingStoreClient->setIsClientGeneratedScroll(false);
1169 }
1170
1171 bool WebPagePrivate::shouldSendResizeEvent()
1172 {
1173     if (!m_mainFrame->document())
1174         return false;
1175
1176     // PR#96865 : Provide an option to always send resize events, regardless of the loading
1177     //            status. The scenario for this are Sapphire applications which tend to
1178     //            maintain an open GET request to the server. This open GET results in
1179     //            webkit thinking that content is still arriving when at the application
1180     //            level it is considered fully loaded.
1181     //
1182     //            NOTE: Care must be exercised in the use of this option, as it bypasses
1183     //                  the sanity provided in 'isLoadingInAPISense()' below.
1184     //
1185     static const bool unrestrictedResizeEvents = Platform::Settings::get()->unrestrictedResizeEvents();
1186     if (unrestrictedResizeEvents)
1187         return true;
1188
1189     // Don't send the resize event if the document is loading. Some pages automatically reload
1190     // when the window is resized; Safari on iPhone often resizes the window while setting up its
1191     // viewport. This obviously can cause problems.
1192     DocumentLoader* documentLoader = m_mainFrame->loader()->documentLoader();
1193     if (documentLoader && documentLoader->isLoadingInAPISense())
1194         return false;
1195
1196     return true;
1197 }
1198
1199 void WebPagePrivate::willDeferLoading()
1200 {
1201     m_client->willDeferLoading();
1202 }
1203
1204 void WebPagePrivate::didResumeLoading()
1205 {
1206     m_client->didResumeLoading();
1207 }
1208
1209 bool WebPagePrivate::scrollBy(int deltaX, int deltaY, bool scrollMainFrame)
1210 {
1211     IntSize delta(deltaX, deltaY);
1212     if (!scrollMainFrame) {
1213         // We need to work around the fact that ::map{To,From}Transformed do not
1214         // work well with negative values, like a negative width or height of an IntSize.
1215         IntSize copiedDelta(IntSize(abs(delta.width()), abs(delta.height())));
1216         IntSize untransformedCopiedDelta = mapFromTransformed(copiedDelta);
1217         delta = IntSize(
1218             delta.width() < 0 ? -untransformedCopiedDelta.width() : untransformedCopiedDelta.width(),
1219             delta.height() < 0 ? -untransformedCopiedDelta.height(): untransformedCopiedDelta.height());
1220
1221         if (m_inRegionScrollStartingNode) {
1222             if (scrollNodeRecursively(m_inRegionScrollStartingNode.get(), delta)) {
1223                 m_selectionHandler->selectionPositionChanged();
1224                 // FIXME: We have code in place to handle scrolling and clipping tap highlight
1225                 // on in-region scrolling. As soon as it is fast enough (i.e. we have it backed by
1226                 // a backing store), we can reliably make use of it in the real world.
1227                 // m_touchEventHandler->drawTapHighlight();
1228                 return true;
1229             }
1230         }
1231
1232         return false;
1233     }
1234
1235     setScrollPosition(scrollPosition() + delta);
1236     return true;
1237 }
1238
1239 bool WebPage::scrollBy(const Platform::IntSize& delta, bool scrollMainFrame)
1240 {
1241     d->m_backingStoreClient->setIsClientGeneratedScroll(true);
1242     bool b = d->scrollBy(delta.width(), delta.height(), scrollMainFrame);
1243     d->m_backingStoreClient->setIsClientGeneratedScroll(false);
1244     return b;
1245 }
1246
1247 void WebPagePrivate::notifyInRegionScrollStatusChanged(bool status)
1248 {
1249     if (!status && m_inRegionScrollStartingNode) {
1250         enqueueRenderingOfClippedContentOfScrollableNodeAfterInRegionScrolling(m_inRegionScrollStartingNode.get());
1251         m_inRegionScrollStartingNode = 0;
1252     }
1253 }
1254
1255 void WebPage::notifyInRegionScrollStatusChanged(bool status)
1256 {
1257     d->notifyInRegionScrollStatusChanged(status);
1258 }
1259
1260 void WebPagePrivate::enqueueRenderingOfClippedContentOfScrollableNodeAfterInRegionScrolling(Node* scrolledNode)
1261 {
1262     ASSERT(scrolledNode);
1263     if (scrolledNode->isDocumentNode()) {
1264         Frame* frame = static_cast<const Document*>(scrolledNode)->frame();
1265         ASSERT(frame);
1266         if (!frame)
1267             return;
1268         ASSERT(frame != m_mainFrame);
1269         FrameView* view = frame->view();
1270         if (!view)
1271             return;
1272
1273         // Steps:
1274         // #1 - Get frame rect in contents coords.
1275         // #2 - Get the clipped scrollview rect in contents coords.
1276         // #3 - Take transform into account for 1 and 2.
1277         // #4 - Subtract 2 from 1, so we know exactly which areas of the frame
1278         //      are offscreen, and need async repainting.
1279         FrameView* mainFrameView = m_mainFrame->view();
1280         ASSERT(mainFrameView);
1281         IntRect frameRect = view->frameRect();
1282         frameRect = frame->tree()->parent()->view()->contentsToWindow(frameRect);
1283         frameRect = mainFrameView->windowToContents(frameRect);
1284
1285         IntRect visibleWindowRect = getRecursiveVisibleWindowRect(view);
1286         IntRect visibleContentsRect = mainFrameView->windowToContents(visibleWindowRect);
1287
1288         IntRect transformedFrameRect = mapToTransformed(frameRect);
1289         IntRect transformedVisibleContentsRect = mapToTransformed(visibleContentsRect);
1290
1291         Platform::IntRectRegion offscreenRegionOfIframe
1292             = Platform::IntRectRegion::subtractRegions(Platform::IntRect(transformedFrameRect), Platform::IntRect(transformedVisibleContentsRect));
1293
1294         if (!offscreenRegionOfIframe.isEmpty())
1295             m_backingStore->d->m_renderQueue->addToQueue(RenderQueue::RegularRender, offscreenRegionOfIframe.rects());
1296     }
1297 }
1298
1299 void WebPagePrivate::setHasInRegionScrollableAreas(bool b)
1300 {
1301     if (b != m_hasInRegionScrollableAreas)
1302         m_hasInRegionScrollableAreas = b;
1303 }
1304
1305 IntSize WebPagePrivate::viewportSize() const
1306 {
1307     return mapFromTransformed(transformedViewportSize());
1308 }
1309
1310 IntSize WebPagePrivate::actualVisibleSize() const
1311 {
1312     return mapFromTransformed(transformedActualVisibleSize());
1313 }
1314
1315 bool WebPagePrivate::hasVirtualViewport() const
1316 {
1317     return m_virtualViewportWidth && m_virtualViewportHeight;
1318 }
1319
1320 void WebPagePrivate::updateViewportSize(bool setFixedReportedSize, bool sendResizeEvent)
1321 {
1322     ASSERT(m_mainFrame->view());
1323     if (setFixedReportedSize)
1324         m_mainFrame->view()->setFixedReportedSize(actualVisibleSize());
1325
1326     IntRect frameRect = IntRect(scrollPosition(), viewportSize());
1327     if (frameRect != m_mainFrame->view()->frameRect()) {
1328         m_mainFrame->view()->setFrameRect(frameRect);
1329         m_mainFrame->view()->adjustViewSize();
1330     }
1331
1332     // We're going to need to send a resize event to JavaScript because
1333     // innerWidth and innerHeight depend on fixed reported size.
1334     // This is how we support mobile pages where JavaScript resizes
1335     // the page in order to get around the fixed layout size, e.g.
1336     // google maps when it detects a mobile user agent.
1337     if (sendResizeEvent && shouldSendResizeEvent())
1338         m_mainFrame->eventHandler()->sendResizeEvent();
1339
1340     // When the actual visible size changes, we also
1341     // need to reposition fixed elements.
1342     m_mainFrame->view()->repaintFixedElementsAfterScrolling();
1343 }
1344
1345 FloatPoint WebPagePrivate::centerOfVisibleContentsRect() const
1346 {
1347     // The visible contents rect in float.
1348     FloatRect visibleContentsRect = this->visibleContentsRect();
1349
1350     // The center of the visible contents rect in float.
1351     return FloatPoint(visibleContentsRect.x() + visibleContentsRect.width() / 2.0,
1352                       visibleContentsRect.y() + visibleContentsRect.height() / 2.0);
1353 }
1354
1355 IntRect WebPagePrivate::visibleContentsRect() const
1356 {
1357     return m_backingStoreClient->visibleContentsRect();
1358 }
1359
1360 IntSize WebPagePrivate::contentsSize() const
1361 {
1362     if (!m_mainFrame->view())
1363         return IntSize();
1364
1365     return m_backingStoreClient->contentsSize();
1366 }
1367
1368 IntSize WebPagePrivate::absoluteVisibleOverflowSize() const
1369 {
1370     if (!m_mainFrame->contentRenderer())
1371         return IntSize();
1372
1373     return IntSize(m_mainFrame->contentRenderer()->rightAbsoluteVisibleOverflow(), m_mainFrame->contentRenderer()->bottomAbsoluteVisibleOverflow());
1374 }
1375
1376 void WebPagePrivate::contentsSizeChanged(const IntSize& contentsSize)
1377 {
1378     if (m_previousContentsSize == contentsSize)
1379         return;
1380
1381     // This should only occur in the middle of layout so we set a flag here and
1382     // handle it at the end of the layout.
1383     m_contentsSizeChanged = true;
1384
1385 #if DEBUG_WEBPAGE_LOAD
1386     Platform::log(Platform::LogLevelInfo, "WebPagePrivate::contentsSizeChanged %dx%d", contentsSize.width(), contentsSize.height());
1387 #endif
1388 }
1389
1390 void WebPagePrivate::layoutFinished()
1391 {
1392     if (!m_contentsSizeChanged && !m_overflowExceedsContentsSize)
1393         return;
1394
1395     m_contentsSizeChanged = false; // Toggle to turn off notification again.
1396     m_overflowExceedsContentsSize = false;
1397
1398     if (contentsSize().isEmpty())
1399         return;
1400
1401     // The call to zoomToInitialScaleOnLoad can cause recursive layout when called from
1402     // the middle of a layout, but the recursion is limited by detection code in
1403     // setViewMode() and mitigation code in fixedLayoutSize().
1404     if (didLayoutExceedMaximumIterations()) {
1405         notifyTransformedContentsSizeChanged();
1406         return;
1407     }
1408
1409     // Temporarily save the m_previousContentsSize here before updating it (in
1410     // notifyTransformedContentsSizeChanged()) so we can compare if our contents
1411     // shrunk afterwards.
1412     IntSize previousContentsSize = m_previousContentsSize;
1413
1414     m_nestedLayoutFinishedCount++;
1415
1416     if (loadState() == Committed)
1417         zoomToInitialScaleOnLoad();
1418     else if (loadState() != None)
1419         notifyTransformedContentsSizeChanged();
1420
1421     m_nestedLayoutFinishedCount--;
1422
1423     if (!m_nestedLayoutFinishedCount) {
1424         // When the contents shrinks, there is a risk that we
1425         // will be left at a scroll position that lies outside of the
1426         // contents rect. Since we allow overscrolling and neglect
1427         // to clamp overscroll in order to retain input focus (RIM Bug #414)
1428         // we need to clamp somewhere, and this is where we know the
1429         // contents size has changed.
1430
1431         if (contentsSize() != previousContentsSize) {
1432
1433             IntPoint newScrollPosition = scrollPosition();
1434
1435             if (contentsSize().height() < previousContentsSize.height()) {
1436                 IntPoint scrollPositionWithHeightShrunk = IntPoint(newScrollPosition.x(), maximumScrollPosition().y());
1437                 newScrollPosition = newScrollPosition.shrunkTo(scrollPositionWithHeightShrunk);
1438             }
1439
1440             if (contentsSize().width() < previousContentsSize.width()) {
1441                 IntPoint scrollPositionWithWidthShrunk = IntPoint(maximumScrollPosition().x(), newScrollPosition.y());
1442                 newScrollPosition = newScrollPosition.shrunkTo(scrollPositionWithWidthShrunk);
1443             }
1444
1445             if (newScrollPosition != scrollPosition()) {
1446                 setScrollPosition(newScrollPosition);
1447                 notifyTransformedScrollChanged();
1448             }
1449         }
1450     }
1451 }
1452
1453 void WebPagePrivate::zoomToInitialScaleOnLoad()
1454 {
1455 #if DEBUG_WEBPAGE_LOAD
1456     Platform::log(Platform::LogLevelInfo, "WebPagePrivate::zoomToInitialScaleOnLoad");
1457 #endif
1458
1459     bool needsLayout = false;
1460
1461     // If the contents width exceeds the viewport width set to desktop mode.
1462     if (m_shouldUseFixedDesktopMode)
1463         needsLayout = setViewMode(FixedDesktop);
1464     else
1465         needsLayout = setViewMode(Desktop);
1466
1467     if (needsLayout) {
1468         // This can cause recursive layout...
1469         setNeedsLayout();
1470     }
1471
1472     if (contentsSize().isEmpty()) {
1473 #if DEBUG_WEBPAGE_LOAD
1474         Platform::log(Platform::LogLevelInfo, "WebPagePrivate::zoomToInitialScaleOnLoad content is empty!");
1475 #endif
1476         requestLayoutIfNeeded();
1477         m_client->resetBitmapZoomScale(currentScale());
1478         notifyTransformedContentsSizeChanged();
1479         return;
1480     }
1481
1482     bool performedZoom = false;
1483     bool shouldZoom = !m_userPerformedManualZoom;
1484
1485     // If this load should restore view state, don't zoom to initial scale
1486     // but instead let the HistoryItem's saved viewport reign supreme.
1487     if (m_mainFrame && m_mainFrame->loader() && m_mainFrame->loader()->shouldRestoreScrollPositionAndViewState())
1488         shouldZoom = false;
1489
1490     if (shouldZoom && loadState() == Committed) {
1491         // Preserve at top and at left position, to avoid scrolling
1492         // to a non top-left position for web page with viewport meta tag
1493         // that specifies an initial-scale that is zoomed in.
1494         FloatPoint anchor = centerOfVisibleContentsRect();
1495         if (!scrollPosition().x())
1496             anchor.setX(0);
1497         if (!scrollPosition().y())
1498             anchor.setY(0);
1499         performedZoom = zoomAboutPoint(initialScale(), anchor);
1500     }
1501
1502     // zoomAboutPoint above can also toggle setNeedsLayout and cause recursive layout...
1503     requestLayoutIfNeeded();
1504
1505     if (!performedZoom) {
1506         // We only notify if we didn't perform zoom, because zoom will notify on
1507         // its own...
1508         m_client->resetBitmapZoomScale(currentScale());
1509         notifyTransformedContentsSizeChanged();
1510     }
1511 }
1512
1513 double WebPagePrivate::zoomToFitScale() const
1514 {
1515     // We must clamp the contents for this calculation so that we do not allow an
1516     // arbitrarily small zoomToFitScale much like we clamp the fixedLayoutSize()
1517     // so that we do not have arbitrarily large layout size.
1518     // If we have a specified viewport, we may need to be able to zoom out more.
1519     int contentWidth = std::min(contentsSize().width(), std::max(m_virtualViewportWidth, static_cast<int>(defaultMaxLayoutSize().width())));
1520
1521     // defaultMaxLayoutSize().width() is a safeguard for excessively large page layouts that
1522     // is too restrictive for image documents. In this case, the document width is sufficient.
1523     Document* doc = m_page->mainFrame()->document();
1524     if (doc && doc->isImageDocument())
1525        contentWidth = contentsSize().width();
1526
1527     // If we have a virtual viewport and its aspect ratio caused content to layout
1528     // wider than the default layout aspect ratio we need to zoom to fit the content height
1529     // in order to avoid showing a grey area below the web page.
1530     // Without virtual viewport we can never get into this situation.
1531     if (hasVirtualViewport()) {
1532         int contentHeight = std::min(contentsSize().height(), std::max(m_virtualViewportHeight, static_cast<int>(defaultMaxLayoutSize().height())));
1533
1534         // Aspect ratio check without division.
1535         if (contentWidth * m_defaultLayoutSize.height() > contentHeight * m_defaultLayoutSize.width())
1536             return contentHeight > 0 ? static_cast<double>(m_defaultLayoutSize.height()) / contentHeight : 1.0;
1537     }
1538
1539     return contentWidth > 0.0 ? static_cast<double>(m_actualVisibleWidth) / contentWidth : 1.0;
1540 }
1541
1542 double WebPage::zoomToFitScale() const
1543 {
1544     return d->zoomToFitScale();
1545 }
1546
1547 double WebPagePrivate::initialScale() const
1548 {
1549     if (m_initialScale > 0.0)
1550         return m_initialScale;
1551
1552     if (m_webSettings->isZoomToFitOnLoad())
1553         return zoomToFitScale();
1554
1555     return 1.0;
1556 }
1557
1558 double WebPage::initialScale() const
1559 {
1560     return d->initialScale();
1561 }
1562
1563 void WebPage::initializeIconDataBase()
1564 {
1565     IconDatabaseClientBlackBerry::getInstance()->initIconDatabase(d->m_webSettings);
1566 }
1567
1568 bool WebPage::isUserScalable() const
1569 {
1570     return d->isUserScalable();
1571 }
1572
1573 double WebPage::currentScale() const
1574 {
1575     return d->currentScale();
1576 }
1577
1578 void WebPage::setInitialScale(double initialScale)
1579 {
1580     d->setInitialScale(initialScale);
1581 }
1582
1583 double WebPage::minimumScale() const
1584 {
1585     return d->minimumScale();
1586 }
1587
1588 void WebPage::setMinimumScale(double minimumScale)
1589 {
1590     d->setMinimumScale(minimumScale);
1591 }
1592
1593 void WebPage::setMaximumScale(double maximumScale)
1594 {
1595     d->setMaximumScale(maximumScale);
1596 }
1597
1598 double WebPagePrivate::maximumScale() const
1599 {
1600     if (m_maximumScale >= zoomToFitScale() && m_maximumScale >= m_minimumScale)
1601         return m_maximumScale;
1602
1603     return hasVirtualViewport() ? std::max<double>(zoomToFitScale(), 4.0) : 4.0;
1604 }
1605
1606 double WebPage::maximumScale() const
1607 {
1608     return d->maximumScale();
1609 }
1610
1611 void WebPagePrivate::resetScales()
1612 {
1613     TransformationMatrix identity;
1614     *m_transformationMatrix = identity;
1615     m_initialScale = m_webSettings->initialScale() > 0 ? m_webSettings->initialScale() : -1.0;
1616     m_minimumScale = -1.0;
1617     m_maximumScale = -1.0;
1618
1619     // We have to let WebCore know about updated framerect now that we've
1620     // reset our scales. See: RIM Bug #401.
1621     updateViewportSize();
1622 }
1623
1624 IntPoint WebPagePrivate::transformedScrollPosition() const
1625 {
1626     return m_backingStoreClient->transformedScrollPosition();
1627 }
1628
1629 // Returned scroll position is in transformed coordinates.
1630 Platform::IntPoint WebPage::scrollPosition() const
1631 {
1632     return d->transformedScrollPosition();
1633 }
1634
1635 IntPoint WebPagePrivate::transformedMaximumScrollPosition() const
1636 {
1637     return m_backingStoreClient->transformedMaximumScrollPosition();
1638 }
1639
1640 IntSize WebPagePrivate::transformedActualVisibleSize() const
1641 {
1642     return IntSize(m_actualVisibleWidth, m_actualVisibleHeight);
1643 }
1644
1645 Platform::IntSize WebPage::viewportSize() const
1646 {
1647     return d->transformedActualVisibleSize();
1648 }
1649
1650 IntSize WebPagePrivate::transformedViewportSize() const
1651 {
1652     return Platform::Graphics::Screen::primaryScreen()->size();
1653 }
1654
1655 IntRect WebPagePrivate::transformedVisibleContentsRect() const
1656 {
1657     // Usually this would be mapToTransformed(visibleContentsRect()), but
1658     // that results in rounding errors because we already set the WebCore
1659     // viewport size from our original transformedViewportSize().
1660     // Instead, we only transform the scroll position and take the
1661     // viewport size as it is, which ensures that e.g. blitting operations
1662     // always cover the whole widget/screen.
1663     return IntRect(transformedScrollPosition(), transformedViewportSize());
1664 }
1665
1666 IntSize WebPagePrivate::transformedContentsSize() const
1667 {
1668     // mapToTransformed() functions use this method to crop their results,
1669     // so we can't make use of them here. While we want rounding inside page
1670     // boundaries to extend rectangles and round points, we need to crop the
1671     // contents size to the floored values so that we don't try to display
1672     // or report points that are not fully covered by the actual float-point
1673     // contents rectangle.
1674     const IntSize untransformedContentsSize = contentsSize();
1675     const FloatPoint transformedBottomRight = m_transformationMatrix->mapPoint(
1676         FloatPoint(untransformedContentsSize.width(), untransformedContentsSize.height()));
1677     return IntSize(floorf(transformedBottomRight.x()), floorf(transformedBottomRight.y()));
1678 }
1679
1680 IntPoint WebPagePrivate::mapFromContentsToViewport(const IntPoint& point) const
1681 {
1682     return m_backingStoreClient->mapFromContentsToViewport(point);
1683 }
1684
1685 IntPoint WebPagePrivate::mapFromViewportToContents(const IntPoint& point) const
1686 {
1687     return m_backingStoreClient->mapFromViewportToContents(point);
1688 }
1689
1690 IntRect WebPagePrivate::mapFromContentsToViewport(const IntRect& rect) const
1691 {
1692     return m_backingStoreClient->mapFromContentsToViewport(rect);
1693 }
1694
1695 IntRect WebPagePrivate::mapFromViewportToContents(const IntRect& rect) const
1696 {
1697     return m_backingStoreClient->mapFromViewportToContents(rect);
1698 }
1699
1700 IntPoint WebPagePrivate::mapFromTransformedContentsToTransformedViewport(const IntPoint& point) const
1701 {
1702     return m_backingStoreClient->mapFromTransformedContentsToTransformedViewport(point);
1703 }
1704
1705 IntPoint WebPagePrivate::mapFromTransformedViewportToTransformedContents(const IntPoint& point) const
1706 {
1707     return m_backingStoreClient->mapFromTransformedViewportToTransformedContents(point);
1708 }
1709
1710 IntRect WebPagePrivate::mapFromTransformedContentsToTransformedViewport(const IntRect& rect) const
1711 {
1712     return m_backingStoreClient->mapFromTransformedContentsToTransformedViewport(rect);
1713 }
1714
1715 IntRect WebPagePrivate::mapFromTransformedViewportToTransformedContents(const IntRect& rect) const
1716 {
1717     return m_backingStoreClient->mapFromTransformedViewportToTransformedContents(rect);
1718 }
1719
1720 // NOTE: PIXEL ROUNDING!
1721 // Accurate back-and-forth rounding is not possible with information loss
1722 // by integer points and sizes, so we always expand the resulting mapped
1723 // float rectangles to the nearest integer. For points, we always use
1724 // floor-rounding in mapToTransformed() so that we don't have to crop to
1725 // the (floor'd) transformed contents size.
1726 static inline IntPoint roundTransformedPoint(const FloatPoint &point)
1727 {
1728     // Maps by rounding half towards zero.
1729     return IntPoint(static_cast<int>(floorf(point.x())), static_cast<int>(floorf(point.y())));
1730 }
1731
1732 static inline IntPoint roundUntransformedPoint(const FloatPoint &point)
1733 {
1734     // Maps by rounding half away from zero.
1735     return IntPoint(static_cast<int>(ceilf(point.x())), static_cast<int>(ceilf(point.y())));
1736 }
1737
1738 IntPoint WebPagePrivate::mapToTransformed(const IntPoint& point) const
1739 {
1740     return roundTransformedPoint(m_transformationMatrix->mapPoint(FloatPoint(point)));
1741 }
1742
1743 FloatPoint WebPagePrivate::mapToTransformedFloatPoint(const FloatPoint& point) const
1744 {
1745     return m_transformationMatrix->mapPoint(point);
1746 }
1747
1748 IntPoint WebPagePrivate::mapFromTransformed(const IntPoint& point) const
1749 {
1750     return roundUntransformedPoint(m_transformationMatrix->inverse().mapPoint(FloatPoint(point)));
1751 }
1752
1753 FloatPoint WebPagePrivate::mapFromTransformedFloatPoint(const FloatPoint& point) const
1754 {
1755     return m_transformationMatrix->inverse().mapPoint(point);
1756 }
1757
1758 FloatRect WebPagePrivate::mapFromTransformedFloatRect(const FloatRect& rect) const
1759 {
1760     return m_transformationMatrix->inverse().mapRect(rect);
1761 }
1762
1763 IntSize WebPagePrivate::mapToTransformed(const IntSize& size) const
1764 {
1765     return mapToTransformed(IntRect(IntPoint::zero(), size)).size();
1766 }
1767
1768 IntSize WebPagePrivate::mapFromTransformed(const IntSize& size) const
1769 {
1770     return mapFromTransformed(IntRect(IntPoint::zero(), size)).size();
1771 }
1772
1773 IntRect WebPagePrivate::mapToTransformed(const IntRect& rect) const
1774 {
1775     return enclosingIntRect(m_transformationMatrix->mapRect(FloatRect(rect)));
1776 }
1777
1778 // Use this in conjunction with mapToTransformed(IntRect), in most cases.
1779 void WebPagePrivate::clipToTransformedContentsRect(IntRect& rect) const
1780 {
1781     rect.intersect(IntRect(IntPoint::zero(), transformedContentsSize()));
1782 }
1783
1784 IntRect WebPagePrivate::mapFromTransformed(const IntRect& rect) const
1785 {
1786     return enclosingIntRect(m_transformationMatrix->inverse().mapRect(FloatRect(rect)));
1787 }
1788
1789 bool WebPagePrivate::transformedPointEqualsUntransformedPoint(const IntPoint& transformedPoint, const IntPoint& untransformedPoint)
1790 {
1791     // Scaling down is always more accurate than scaling up.
1792     if (m_transformationMatrix->a() > 1.0)
1793         return transformedPoint == mapToTransformed(untransformedPoint);
1794
1795     return mapFromTransformed(transformedPoint) == untransformedPoint;
1796 }
1797
1798 void WebPagePrivate::notifyTransformChanged()
1799 {
1800     notifyTransformedContentsSizeChanged();
1801     notifyTransformedScrollChanged();
1802
1803     m_backingStore->d->transformChanged();
1804 }
1805
1806 void WebPagePrivate::notifyTransformedContentsSizeChanged()
1807 {
1808     // We mark here as the last reported content size we sent to the client.
1809     m_previousContentsSize = contentsSize();
1810
1811     const IntSize size = transformedContentsSize();
1812     m_backingStore->d->contentsSizeChanged(size);
1813     m_client->contentsSizeChanged(size);
1814     m_selectionHandler->selectionPositionChanged();
1815 }
1816
1817 void WebPagePrivate::notifyTransformedScrollChanged()
1818 {
1819     const IntPoint pos = transformedScrollPosition();
1820     m_backingStore->d->scrollChanged(pos);
1821     m_client->scrollChanged(pos);
1822 }
1823
1824 bool WebPagePrivate::setViewMode(ViewMode mode)
1825 {
1826     if (!m_mainFrame->view())
1827         return false;
1828
1829     m_viewMode = mode;
1830
1831     // If we're in the middle of a nested layout with a recursion count above
1832     // some maximum threshold, then our algorithm for finding the minimum content
1833     // width of a given page has become dependent on the visible width.
1834     //
1835     // We need to find some method to ensure that we don't experience excessive
1836     // and even infinite recursion. This can even happen with valid html. The
1837     // former can happen when we run into inline text with few candidates for line
1838     // break. The latter can happen for instance if the page has a negative margin
1839     // set against the right border. Note: this is valid by spec and can lead to
1840     // a situation where there is no value for which the content width will ensure
1841     // no horizontal scrollbar.
1842     // Example: LayoutTests/css1/box_properties/margin.html
1843     //
1844     // In order to address such situations when we detect a recursion above some
1845     // maximum threshold we snap our fixed layout size to a defined quantum increment.
1846     // Eventually, either the content width will be satisfied to ensure no horizontal
1847     // scrollbar or this increment will run into the maximum layout size and the
1848     // recursion will necessarily end.
1849     bool snapToIncrement = didLayoutExceedMaximumIterations();
1850
1851     IntSize currentSize = m_mainFrame->view()->fixedLayoutSize();
1852     IntSize newSize = fixedLayoutSize(snapToIncrement);
1853     if (currentSize == newSize)
1854         return false;
1855
1856     // FIXME: Temp solution. We'll get back to this.
1857     if (m_nestedLayoutFinishedCount) {
1858         double widthChange = fabs(double(newSize.width() - currentSize.width()) / currentSize.width());
1859         double heightChange = fabs(double(newSize.height() - currentSize.height()) / currentSize.height());
1860         if (widthChange < 0.05 && heightChange < 0.05)
1861             return false;
1862     }
1863
1864     m_mainFrame->view()->setUseFixedLayout(useFixedLayout());
1865     m_mainFrame->view()->setFixedLayoutSize(newSize);
1866     return true; // Needs re-layout!
1867 }
1868
1869 void WebPagePrivate::setCursor(PlatformCursor handle)
1870 {
1871     if (m_currentCursor.type() != handle.type()) {
1872         m_currentCursor = handle;
1873         m_client->cursorChanged(handle.type(), handle.url().c_str(), handle.hotspot().x(), handle.hotspot().y());
1874     }
1875 }
1876
1877 Platform::NetworkStreamFactory* WebPagePrivate::networkStreamFactory()
1878 {
1879     return m_client->networkStreamFactory();
1880 }
1881
1882 Platform::Graphics::Window* WebPagePrivate::platformWindow() const
1883 {
1884     return m_client->window();
1885 }
1886
1887 void WebPagePrivate::setPreventsScreenDimming(bool keepAwake)
1888 {
1889     if (keepAwake) {
1890         if (!m_preventIdleDimmingCount)
1891             m_client->setPreventsScreenIdleDimming(true);
1892         m_preventIdleDimmingCount++;
1893     } else if (m_preventIdleDimmingCount > 0) {
1894         m_preventIdleDimmingCount--;
1895         if (!m_preventIdleDimmingCount)
1896             m_client->setPreventsScreenIdleDimming(false);
1897     } else
1898         ASSERT_NOT_REACHED(); // SetPreventsScreenIdleDimming(false) called too many times.
1899 }
1900
1901 void WebPagePrivate::showVirtualKeyboard(bool showKeyboard)
1902 {
1903     m_client->showVirtualKeyboard(showKeyboard);
1904 }
1905
1906 void WebPagePrivate::ensureContentVisible(bool centerInView)
1907 {
1908     m_inputHandler->ensureFocusElementVisible(centerInView);
1909 }
1910
1911 void WebPagePrivate::zoomToContentRect(const IntRect& rect)
1912 {
1913     // Don't scale if the user is not supposed to scale.
1914     if (!isUserScalable())
1915         return;
1916
1917     FloatPoint anchor = FloatPoint(rect.width() / 2.0 + rect.x(), rect.height() / 2.0 + rect.y());
1918     IntSize viewSize = viewportSize();
1919
1920     // Calculate the scale required to scale that dimension to fit.
1921     double scaleH = (double)viewSize.width() / (double)rect.width();
1922     double scaleV = (double)viewSize.height() / (double)rect.height();
1923
1924     // Choose the smaller scale factor so that all of the content is visible.
1925     zoomAboutPoint(min(scaleH, scaleV), anchor);
1926 }
1927
1928 void WebPagePrivate::registerPlugin(PluginView* plugin, bool shouldRegister)
1929 {
1930     if (shouldRegister)
1931         m_pluginViews.add(plugin);
1932     else
1933         m_pluginViews.remove(plugin);
1934 }
1935
1936 #define FOR_EACH_PLUGINVIEW(pluginViews) \
1937     HashSet<PluginView*>::const_iterator it = pluginViews.begin(); \
1938     HashSet<PluginView*>::const_iterator last = pluginViews.end(); \
1939     for (; it != last; ++it)
1940
1941 void WebPagePrivate::notifyPageOnLoad()
1942 {
1943     FOR_EACH_PLUGINVIEW(m_pluginViews)
1944         (*it)->handleOnLoadEvent();
1945 }
1946
1947 bool WebPagePrivate::shouldPluginEnterFullScreen(PluginView* plugin, const char* windowUniquePrefix)
1948 {
1949     return m_client->shouldPluginEnterFullScreen();
1950 }
1951
1952 void WebPagePrivate::didPluginEnterFullScreen(PluginView* plugin, const char* windowUniquePrefix)
1953 {
1954     m_fullScreenPluginView = plugin;
1955     m_client->didPluginEnterFullScreen();
1956
1957     if (!m_client->window())
1958         return;
1959
1960     Platform::Graphics::Window::setTransparencyDiscardFilter(windowUniquePrefix);
1961     m_client->window()->setSensitivityFullscreenOverride(true);
1962 }
1963
1964 void WebPagePrivate::didPluginExitFullScreen(PluginView* plugin, const char* windowUniquePrefix)
1965 {
1966     m_fullScreenPluginView = 0;
1967     m_client->didPluginExitFullScreen();
1968
1969     if (!m_client->window())
1970         return;
1971
1972     Platform::Graphics::Window::setTransparencyDiscardFilter(0);
1973     m_client->window()->setSensitivityFullscreenOverride(false);
1974 }
1975
1976 void WebPagePrivate::onPluginStartBackgroundPlay(PluginView* plugin, const char* windowUniquePrefix)
1977 {
1978     m_client->onPluginStartBackgroundPlay();
1979 }
1980
1981 void WebPagePrivate::onPluginStopBackgroundPlay(PluginView* plugin, const char* windowUniquePrefix)
1982 {
1983     m_client->onPluginStopBackgroundPlay();
1984 }
1985
1986 bool WebPagePrivate::lockOrientation(bool landscape)
1987 {
1988     return m_client->lockOrientation(landscape);
1989 }
1990
1991 void WebPagePrivate::unlockOrientation()
1992 {
1993     return m_client->unlockOrientation();
1994 }
1995
1996 int WebPagePrivate::orientation() const
1997 {
1998 #if ENABLE(ORIENTATION_EVENTS)
1999     return m_mainFrame->orientation();
2000 #else
2001 #error ORIENTATION_EVENTS must be defined.
2002 // Or a copy of the orientation value will have to be stored in these objects.
2003 #endif
2004 }
2005
2006 double WebPagePrivate::currentZoomFactor() const
2007 {
2008     return currentScale();
2009 }
2010
2011 int WebPagePrivate::showAlertDialog(WebPageClient::AlertType atype)
2012 {
2013     return m_client->showAlertDialog(atype);
2014 }
2015
2016 bool WebPagePrivate::isActive() const
2017 {
2018     return m_client->isActive();
2019 }
2020
2021 bool WebPagePrivate::authenticationChallenge(const KURL& url, const ProtectionSpace& protectionSpace, Credential& inputCredential)
2022 {
2023     WebString username;
2024     WebString password;
2025
2026 #if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)
2027     if (!m_webSettings->isPrivateBrowsingEnabled())
2028         credentialManager().autofillAuthenticationChallenge(protectionSpace, username, password);
2029 #endif
2030
2031     bool isConfirmed = m_client->authenticationChallenge(protectionSpace.realm().characters(), protectionSpace.realm().length(), username, password);
2032
2033 #if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)
2034     Credential credential(username, password, CredentialPersistencePermanent);
2035     if (!m_webSettings->isPrivateBrowsingEnabled())
2036         credentialManager().saveCredentialIfConfirmed(this, CredentialTransformData(url, protectionSpace, inputCredential));
2037 #else
2038     Credential credential(username, password, CredentialPersistenceNone);
2039 #endif
2040     inputCredential = credential;
2041     return isConfirmed;
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);