• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2007, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #define LOG_TAG "WebCore"
27 
28 #include "config.h"
29 
30 #include "ApplicationCacheStorage.h"
31 #include "ChromeClientAndroid.h"
32 #include "CString.h"
33 #include "DatabaseTracker.h"
34 #include "Document.h"
35 #include "PlatformString.h"
36 #include "FloatRect.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameView.h"
40 #include "Geolocation.h"
41 #include "GraphicsLayerAndroid.h"
42 #include "Page.h"
43 #include "Screen.h"
44 #include "ScriptController.h"
45 #include "WebCoreFrameBridge.h"
46 #include "WebCoreViewBridge.h"
47 #include "WebViewCore.h"
48 #include "WindowFeatures.h"
49 #include "Settings.h"
50 
51 namespace android {
52 
53 #if ENABLE(DATABASE)
54 static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota);
55 #endif
56 
57 #if USE(ACCELERATED_COMPOSITING)
58 
syncTimerFired(Timer<ChromeClientAndroid> * client)59 void ChromeClientAndroid::syncTimerFired(Timer<ChromeClientAndroid>* client)
60 {
61     if (!m_rootGraphicsLayer)
62         return;
63 
64     if (m_webFrame) {
65         FrameView* frameView = m_webFrame->page()->mainFrame()->view();
66         if (frameView && frameView->syncCompositingStateRecursive()) {
67             GraphicsLayerAndroid* androidGraphicsLayer =
68                     static_cast<GraphicsLayerAndroid*>(m_rootGraphicsLayer);
69             if (androidGraphicsLayer) {
70                 androidGraphicsLayer->sendImmediateRepaint();
71                 androidGraphicsLayer->notifyClientAnimationStarted();
72             }
73         }
74     }
75 }
76 
scheduleCompositingLayerSync()77 void ChromeClientAndroid::scheduleCompositingLayerSync()
78 {
79     if (!m_syncTimer.isActive())
80         m_syncTimer.startOneShot(0);
81 }
82 
setNeedsOneShotDrawingSynchronization()83 void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization()
84 {
85     // This should not be needed
86 }
87 
attachRootGraphicsLayer(WebCore::Frame * frame,WebCore::GraphicsLayer * layer)88 void ChromeClientAndroid::attachRootGraphicsLayer(WebCore::Frame* frame, WebCore::GraphicsLayer* layer)
89 {
90     m_rootGraphicsLayer = layer;
91     if (!layer) {
92         WebViewCore::getWebViewCore(frame->view())->setUIRootLayer(0);
93         return;
94     }
95     WebCore::GraphicsLayerAndroid* androidGraphicsLayer = static_cast<GraphicsLayerAndroid*>(layer);
96     androidGraphicsLayer->setFrame(frame);
97     scheduleCompositingLayerSync();
98 }
99 
100 #endif
101 
setWebFrame(android::WebFrame * webframe)102 void ChromeClientAndroid::setWebFrame(android::WebFrame* webframe)
103 {
104     Release(m_webFrame);
105     m_webFrame = webframe;
106     Retain(m_webFrame);
107 }
108 
chromeDestroyed()109 void ChromeClientAndroid::chromeDestroyed()
110 {
111     Release(m_webFrame);
112     delete this;
113 }
114 
setWindowRect(const FloatRect &)115 void ChromeClientAndroid::setWindowRect(const FloatRect&) { notImplemented(); }
116 
windowRect()117 FloatRect ChromeClientAndroid::windowRect() {
118     ASSERT(m_webFrame);
119     if (!m_webFrame)
120         return FloatRect();
121     FrameView* frameView = m_webFrame->page()->mainFrame()->view();
122     if (!frameView)
123         return FloatRect();
124     const WebCoreViewBridge* bridge = frameView->platformWidget();
125     const IntRect& rect = bridge->getWindowBounds();
126     FloatRect fRect(rect.x(), rect.y(), rect.width(), rect.height());
127     return fRect;
128 }
129 
pageRect()130 FloatRect ChromeClientAndroid::pageRect() { notImplemented(); return FloatRect(); }
131 
scaleFactor()132 float ChromeClientAndroid::scaleFactor()
133 {
134     ASSERT(m_webFrame);
135     return m_webFrame->density();
136 }
137 
138 #ifdef ANDROID_USER_GESTURE
focus(bool userGesture)139 void ChromeClientAndroid::focus(bool userGesture) {
140 #else
141 void ChromeClientAndroid::focus() {
142     // The old behavior was to always allow javascript to focus a window. If we
143     // turn off ANDROID_USER_GESTURE, go back to the old behavior by forcing
144     // userGesture to be true.
145     bool userGesture = true;
146 #endif
147     ASSERT(m_webFrame);
148     // Ask the application to focus this WebView.
149     if (userGesture)
150         m_webFrame->requestFocus();
151 }
152 void ChromeClientAndroid::unfocus() { notImplemented(); }
153 
154 bool ChromeClientAndroid::canTakeFocus(FocusDirection) { notImplemented(); return false; }
155 void ChromeClientAndroid::takeFocus(FocusDirection) { notImplemented(); }
156 
157 void ChromeClientAndroid::focusedNodeChanged(Node*) { notImplemented(); }
158 
159 Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&,
160         const WindowFeatures& features)
161 {
162     ASSERT(frame);
163 #ifdef ANDROID_MULTIPLE_WINDOWS
164     if (frame->settings() && !(frame->settings()->supportMultipleWindows()))
165         // If the client doesn't support multiple windows, just return the current page
166         return frame->page();
167 #endif
168 
169     WTF::PassRefPtr<WebCore::Screen> screen = WebCore::Screen::create(frame);
170     bool dialog = features.dialog || !features.resizable
171             || (features.heightSet && features.height < screen.get()->height()
172                     && features.widthSet && features.width < screen.get()->width())
173             || (!features.menuBarVisible && !features.statusBarVisible
174                     && !features.toolBarVisible && !features.locationBarVisible
175                     && !features.scrollbarsVisible);
176     // fullscreen definitely means no dialog
177     if (features.fullscreen)
178         dialog = false;
179     WebCore::Frame* newFrame = m_webFrame->createWindow(dialog,
180             frame->script()->processingUserGesture(mainThreadNormalWorld()));
181     if (newFrame) {
182         WebCore::Page* page = newFrame->page();
183         page->setGroupName(frame->page()->groupName());
184         return page;
185     }
186     return NULL;
187 }
188 
189 void ChromeClientAndroid::show() { notImplemented(); }
190 
191 bool ChromeClientAndroid::canRunModal() { notImplemented(); return false; }
192 void ChromeClientAndroid::runModal() { notImplemented(); }
193 
194 void ChromeClientAndroid::setToolbarsVisible(bool) { notImplemented(); }
195 bool ChromeClientAndroid::toolbarsVisible() { notImplemented(); return false; }
196 
197 void ChromeClientAndroid::setStatusbarVisible(bool) { notImplemented(); }
198 bool ChromeClientAndroid::statusbarVisible() { notImplemented(); return false; }
199 
200 void ChromeClientAndroid::setScrollbarsVisible(bool) { notImplemented(); }
201 bool ChromeClientAndroid::scrollbarsVisible() { notImplemented(); return false; }
202 
203 void ChromeClientAndroid::setMenubarVisible(bool) { notImplemented(); }
204 bool ChromeClientAndroid::menubarVisible() { notImplemented(); return false; }
205 
206 void ChromeClientAndroid::setResizable(bool) { notImplemented(); }
207 
208 // This function is called by the JavaScript bindings to print usually an error to
209 // a message console. Pass the message to the java side so that the client can
210 // handle it as it sees fit.
211 void ChromeClientAndroid::addMessageToConsole(MessageSource, MessageType, MessageLevel msgLevel, const String& message, unsigned int lineNumber, const String& sourceID) {
212     android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->addMessageToConsole(message, lineNumber, sourceID, msgLevel);
213 }
214 
215 bool ChromeClientAndroid::canRunBeforeUnloadConfirmPanel() { return true; }
216 bool ChromeClientAndroid::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) {
217     String url = frame->document()->documentURI();
218     return android::WebViewCore::getWebViewCore(frame->view())->jsUnload(url, message);
219 }
220 
221 void ChromeClientAndroid::closeWindowSoon()
222 {
223     ASSERT(m_webFrame);
224     Page* page = m_webFrame->page();
225     Frame* mainFrame = page->mainFrame();
226     // This will prevent javascript cross-scripting during unload
227     page->setGroupName(String());
228     // Stop loading but do not send the unload event
229     mainFrame->loader()->stopLoading(UnloadEventPolicyNone);
230     // Cancel all pending loaders
231     mainFrame->loader()->stopAllLoaders();
232     // Remove all event listeners so that no javascript can execute as a result
233     // of mouse/keyboard events.
234     mainFrame->document()->removeAllEventListeners();
235     // Close the window.
236     m_webFrame->closeWindow(android::WebViewCore::getWebViewCore(mainFrame->view()));
237 }
238 
239 void ChromeClientAndroid::runJavaScriptAlert(Frame* frame, const String& message)
240 {
241     String url = frame->document()->documentURI();
242 
243     android::WebViewCore::getWebViewCore(frame->view())->jsAlert(url, message);
244 }
245 
246 bool ChromeClientAndroid::runJavaScriptConfirm(Frame* frame, const String& message)
247 {
248     String url = frame->document()->documentURI();
249 
250     return android::WebViewCore::getWebViewCore(frame->view())->jsConfirm(url, message);
251 }
252 
253 /* This function is called for the javascript method Window.prompt(). A dialog should be shown on
254  * the screen with an input put box. First param is the text, the second is the default value for
255  * the input box, third is return param. If the function returns true, the value set in the third parameter
256  * is provided to javascript, else null is returned to the script.
257  */
258 bool ChromeClientAndroid::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
259 {
260     String url = frame->document()->documentURI();
261     return android::WebViewCore::getWebViewCore(frame->view())->jsPrompt(url, message, defaultValue, result);
262 }
263 void ChromeClientAndroid::setStatusbarText(const String&) { notImplemented(); }
264 
265 // This is called by the JavaScript interpreter when a script has been running for a long
266 // time. A dialog should be shown to the user asking them if they would like to cancel the
267 // Javascript. If true is returned, the script is cancelled.
268 // To make a device more responsive, we default to return true to disallow long running script.
269 // This implies that some of scripts will not be completed.
270 bool ChromeClientAndroid::shouldInterruptJavaScript() {
271   FrameView* frameView = m_webFrame->page()->mainFrame()->view();
272   return android::WebViewCore::getWebViewCore(frameView)->jsInterrupt();
273 }
274 
275 bool ChromeClientAndroid::tabsToLinks() const { return false; }
276 
277 IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); }
278 
279 // new to change 38068 (Nov 6, 2008)
280 void ChromeClientAndroid::repaint(const IntRect& rect, bool contentChanged,
281         bool immediate, bool repaintContentOnly) {
282     notImplemented();
283 // was in ScrollViewAndroid::update() : needs to be something like:
284 //    android::WebViewCore::getWebViewCore(this)->contentInvalidate(rect);
285 }
286 
287 // new to change 38068 (Nov 6, 2008)
288 void ChromeClientAndroid::scroll(const IntSize& scrollDelta,
289         const IntRect& rectToScroll, const IntRect& clipRect) {
290     notImplemented();
291 }
292 
293 // new to change 38068 (Nov 6, 2008)
294 IntPoint ChromeClientAndroid::screenToWindow(const IntPoint&) const {
295     notImplemented();
296     return IntPoint();
297 }
298 
299 // new to change 38068 (Nov 6, 2008)
300 IntRect ChromeClientAndroid::windowToScreen(const IntRect&) const {
301     notImplemented();
302     return IntRect();
303 }
304 
305 PlatformPageClient ChromeClientAndroid::platformPageClient() const {
306     Page* page = m_webFrame->page();
307     Frame* mainFrame = page->mainFrame();
308     FrameView* view = mainFrame->view();
309     PlatformWidget viewBridge = view->platformWidget();
310     return viewBridge;
311 }
312 
313 void ChromeClientAndroid::contentsSizeChanged(Frame*, const IntSize&) const
314 {
315     notImplemented();
316 }
317 
318 void ChromeClientAndroid::scrollRectIntoView(const IntRect&, const ScrollView*) const
319 {
320     notImplemented();
321 }
322 
323 void ChromeClientAndroid::formStateDidChange(const Node*)
324 {
325     notImplemented();
326 }
327 
328 void ChromeClientAndroid::scrollbarsModeDidChange() const
329 {
330     notImplemented();
331 }
332 
333 void ChromeClientAndroid::mouseDidMoveOverElement(const HitTestResult&, unsigned int) {}
334 void ChromeClientAndroid::setToolTip(const String&, TextDirection) {}
335 void ChromeClientAndroid::print(Frame*) {}
336 
337 /*
338  * This function is called on the main (webcore) thread by SQLTransaction::deliverQuotaIncreaseCallback.
339  * The way that the callback mechanism is designed inside SQLTransaction means that there must be a new quota
340  * (which may be equal to the old quota if the user did not allow more quota) when this function returns. As
341  * we call into the browser thread to ask what to do with the quota, we block here and get woken up when the
342  * browser calls the native WebViewCore::SetDatabaseQuota method with the new quota value.
343  */
344 #if ENABLE(DATABASE)
345 void ChromeClientAndroid::exceededDatabaseQuota(Frame* frame, const String& name)
346 {
347     SecurityOrigin* origin = frame->document()->securityOrigin();
348     DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker();
349 
350     // We want to wait on a new quota from the UI thread. Reset the m_newQuota variable to represent we haven't received a new quota.
351     m_newQuota = -1;
352 
353     // This origin is being tracked and has exceeded it's quota. Call into
354     // the Java side of things to inform the user.
355     unsigned long long currentQuota = 0;
356     if (tracker.hasEntryForOrigin(origin))
357         currentQuota = tracker.quotaForOrigin(origin);
358 
359     unsigned long long estimatedSize = 0;
360 
361     // Only update estimatedSize if we are trying to create a a new database, i.e. the usage for the database is 0.
362     if (tracker.usageForDatabase(name, origin) == 0)
363         estimatedSize = tracker.detailsForNameAndOrigin(name, origin).expectedUsage();
364 
365     android::WebViewCore::getWebViewCore(frame->view())->exceededDatabaseQuota(frame->document()->documentURI(), name, currentQuota, estimatedSize);
366 
367     // We've sent notification to the browser so now wait for it to come back.
368     m_quotaThreadLock.lock();
369     while (m_newQuota == -1) {
370         m_quotaThreadCondition.wait(m_quotaThreadLock);
371     }
372     m_quotaThreadLock.unlock();
373 
374     // If new quota is unavailable, we may be able to resolve the situation by shrinking the quota of an origin that asked for a lot but is only using a little.
375     // If we find such a site, shrink it's quota and ask Java to try again.
376 
377     if ((unsigned long long) m_newQuota == currentQuota && !m_triedToReclaimDBQuota) {
378         m_triedToReclaimDBQuota = true; // we should only try this once per quota overflow.
379         unsigned long long reclaimedQuotaBytes = tryToReclaimDatabaseQuota(origin);
380 
381         // If we were able to free up enough space, try asking Java again.
382         // Otherwise, give up and deny the new database. :(
383         if (reclaimedQuotaBytes >= estimatedSize) {
384             exceededDatabaseQuota(frame, name);
385             return;
386         }
387     }
388 
389     // Update the DatabaseTracker with the new quota value (if the user declined
390     // new quota, this may equal the old quota)
391     tracker.setQuota(origin, m_newQuota);
392     m_triedToReclaimDBQuota = false;
393 }
394 
395 static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota) {
396     DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker();
397     Vector<RefPtr<SecurityOrigin> > origins;
398     tracker.origins(origins);
399     unsigned long long reclaimedQuotaBytes = 0;
400     for (unsigned i = 0; i < origins.size(); i++) {
401         SecurityOrigin* originToReclaimFrom = origins[i].get();
402 
403         // Don't try to reclaim from the origin that has exceeded its quota.
404         if (originToReclaimFrom->equal(originNeedingQuota))
405             continue;
406 
407         unsigned long long originUsage = tracker.usageForOrigin(originToReclaimFrom);
408         unsigned long long originQuota = tracker.quotaForOrigin(originToReclaimFrom);
409         // If the origin has a quota that is more than it's current usage +1MB, shrink it.
410         static const int ONE_MB = 1 * 1024 * 1024;
411         if (originUsage + ONE_MB < originQuota) {
412             unsigned long long newQuota = originUsage + ONE_MB;
413             tracker.setQuota(originToReclaimFrom, newQuota);
414             reclaimedQuotaBytes += originQuota - newQuota;
415         }
416     }
417     return reclaimedQuotaBytes;
418 }
419 #endif
420 
421 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
422 void ChromeClientAndroid::reachedMaxAppCacheSize(int64_t spaceNeeded)
423 {
424     // Set m_newQuota before calling into the Java side. If we do this after,
425     // we could overwrite the result passed from the Java side and deadlock in the
426     // wait call below.
427     m_newQuota = -1;
428     Page* page = m_webFrame->page();
429     Frame* mainFrame = page->mainFrame();
430     FrameView* view = mainFrame->view();
431     android::WebViewCore::getWebViewCore(view)->reachedMaxAppCacheSize(spaceNeeded);
432 
433     // We've sent notification to the browser so now wait for it to come back.
434     m_quotaThreadLock.lock();
435     while (m_newQuota == -1) {
436        m_quotaThreadCondition.wait(m_quotaThreadLock);
437     }
438     m_quotaThreadLock.unlock();
439     if (m_newQuota > 0) {
440         WebCore::cacheStorage().setMaximumSize(m_newQuota);
441         // Now the app cache will retry the saving the previously failed cache.
442     }
443 }
444 #endif
445 
446 void ChromeClientAndroid::populateVisitedLinks()
447 {
448     Page* page = m_webFrame->page();
449     Frame* mainFrame = page->mainFrame();
450     FrameView* view = mainFrame->view();
451     android::WebViewCore::getWebViewCore(view)->populateVisitedLinks(&page->group());
452 }
453 
454 void ChromeClientAndroid::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation)
455 {
456     ASSERT(geolocation);
457     if (!m_geolocationPermissions) {
458         m_geolocationPermissions = new GeolocationPermissions(android::WebViewCore::getWebViewCore(frame->view()),
459                                                               m_webFrame->page()->mainFrame());
460     }
461     m_geolocationPermissions->queryPermissionState(frame);
462 }
463 
464 void ChromeClientAndroid::cancelGeolocationPermissionRequestForFrame(Frame* frame)
465 {
466     if (m_geolocationPermissions)
467         m_geolocationPermissions->cancelPermissionStateQuery(frame);
468 }
469 
470 void ChromeClientAndroid::provideGeolocationPermissions(const String &origin, bool allow, bool remember)
471 {
472     ASSERT(m_geolocationPermissions);
473     m_geolocationPermissions->providePermissionState(origin, allow, remember);
474 }
475 
476 void ChromeClientAndroid::storeGeolocationPermissions()
477 {
478     GeolocationPermissions::maybeStorePermanentPermissions();
479 }
480 
481 void ChromeClientAndroid::onMainFrameLoadStarted()
482 {
483     if (m_geolocationPermissions.get())
484         m_geolocationPermissions->resetTemporaryPermissionStates();
485 }
486 
487 void ChromeClientAndroid::runOpenPanel(Frame* frame,
488         PassRefPtr<FileChooser> chooser)
489 {
490     android::WebViewCore* core = android::WebViewCore::getWebViewCore(
491             frame->view());
492     core->openFileChooser(chooser);
493 }
494 
495 bool ChromeClientAndroid::setCursor(PlatformCursorHandle)
496 {
497     notImplemented();
498     return false;
499 }
500 
501 void ChromeClientAndroid::wakeUpMainThreadWithNewQuota(long newQuota) {
502     MutexLocker locker(m_quotaThreadLock);
503     m_newQuota = newQuota;
504     m_quotaThreadCondition.signal();
505 }
506 
507 #if ENABLE(TOUCH_EVENTS)
508 void ChromeClientAndroid::needTouchEvents(bool needTouchEvents)
509 {
510     FrameView* frameView = m_webFrame->page()->mainFrame()->view();
511     android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
512     if (core)
513         core->needTouchEvents(needTouchEvents);
514 }
515 #endif
516 
517 }
518