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