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