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