Home | History | Annotate | Download | only in WebCoreSupport
      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 
     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 
     77 void ChromeClientAndroid::scheduleCompositingLayerSync()
     78 {
     79     if (!m_syncTimer.isActive())
     80         m_syncTimer.startOneShot(0);
     81 }
     82 
     83 void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization()
     84 {
     85     // This should not be needed
     86 }
     87 
     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 
    102 void ChromeClientAndroid::setWebFrame(android::WebFrame* webframe)
    103 {
    104     Release(m_webFrame);
    105     m_webFrame = webframe;
    106     Retain(m_webFrame);
    107 }
    108 
    109 void ChromeClientAndroid::chromeDestroyed()
    110 {
    111     Release(m_webFrame);
    112     delete this;
    113 }
    114 
    115 void ChromeClientAndroid::setWindowRect(const FloatRect&) { notImplemented(); }
    116 
    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 
    130 FloatRect ChromeClientAndroid::pageRect() { notImplemented(); return FloatRect(); }
    131 
    132 float ChromeClientAndroid::scaleFactor()
    133 {
    134     ASSERT(m_webFrame);
    135     return m_webFrame->density();
    136 }
    137 
    138 #ifdef ANDROID_USER_GESTURE
    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