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