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