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 "DatabaseTracker.h"
33#include "Document.h"
34#include "PlatformString.h"
35#include "FloatRect.h"
36#include "Frame.h"
37#include "FrameLoader.h"
38#include "FrameView.h"
39#include "Geolocation.h"
40#include "GraphicsLayerAndroid.h"
41#include "HTMLMediaElement.h"
42#include "HTMLNames.h"
43#include "Icon.h"
44#include "LayerAndroid.h"
45#include "Page.h"
46#include "PopupMenuAndroid.h"
47#include "RenderLayer.h"
48#include "RenderLayerCompositor.h"
49#include "ScriptController.h"
50#include "SearchPopupMenuAndroid.h"
51#include "WebCoreFrameBridge.h"
52#include "WebCoreViewBridge.h"
53#include "WebViewCore.h"
54#include "WindowFeatures.h"
55#include "Settings.h"
56#include "UserGestureIndicator.h"
57#include <wtf/text/CString.h>
58
59namespace android {
60
61#if ENABLE(DATABASE)
62static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota);
63#endif
64
65#if USE(ACCELERATED_COMPOSITING)
66
67WebCore::GraphicsLayer* ChromeClientAndroid::layersSync()
68{
69    if (m_rootGraphicsLayer && m_needsLayerSync && m_webFrame) {
70        // We may have more than one frame, so let's first update all of them
71        // (webkit may want to update the GraphicsLayer tree, and we do *not* want
72        // to find this out when we are painting, as it means we could be summarily
73        // deallocated while painting...)
74        GraphicsLayerAndroid* rootLayer = static_cast<GraphicsLayerAndroid*>(m_rootGraphicsLayer);
75        Vector<const RenderLayer*> listRootLayers;
76        rootLayer->gatherRootLayers(listRootLayers);
77
78        for (unsigned int i = 0; i < listRootLayers.size(); i++) {
79            RenderLayer* layer = const_cast<RenderLayer*>(listRootLayers[i]);
80            layer->compositor()->updateCompositingLayers();
81        }
82
83        Frame* frame = m_webFrame->page()->mainFrame();
84        if (FrameView* frameView = frame->view())
85            frameView->syncCompositingStateIncludingSubframes();
86    }
87    m_needsLayerSync = false;
88    return m_rootGraphicsLayer;
89}
90
91void ChromeClientAndroid::scheduleCompositingLayerSync()
92{
93    if (m_needsLayerSync)
94        return;
95    m_needsLayerSync = true;
96    WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view());
97    if (webViewCore)
98        webViewCore->contentDraw();
99}
100
101void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization()
102{
103    // This should not be needed
104}
105
106void ChromeClientAndroid::attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* layer)
107{
108    // frame is not used in Android as we should only get root graphics layer for the main frame
109    m_rootGraphicsLayer = layer;
110    if (!layer)
111        return;
112    scheduleCompositingLayerSync();
113}
114
115#endif
116
117void ChromeClientAndroid::setWebFrame(android::WebFrame* webframe)
118{
119    Release(m_webFrame);
120    m_webFrame = webframe;
121    Retain(m_webFrame);
122}
123
124void ChromeClientAndroid::chromeDestroyed()
125{
126    Release(m_webFrame);
127    delete this;
128}
129
130void ChromeClientAndroid::setWindowRect(const FloatRect&) { notImplemented(); }
131
132FloatRect ChromeClientAndroid::windowRect() {
133    ASSERT(m_webFrame);
134    if (!m_webFrame)
135        return FloatRect();
136    FrameView* frameView = m_webFrame->page()->mainFrame()->view();
137    if (!frameView)
138        return FloatRect();
139    const WebCoreViewBridge* bridge = frameView->platformWidget();
140    const IntRect& rect = bridge->getWindowBounds();
141    FloatRect fRect(rect.x(), rect.y(), rect.width(), rect.height());
142    return fRect;
143}
144
145FloatRect ChromeClientAndroid::pageRect() { notImplemented(); return FloatRect(); }
146
147float ChromeClientAndroid::scaleFactor()
148{
149    ASSERT(m_webFrame);
150    return m_webFrame->density();
151}
152
153void ChromeClientAndroid::focus()
154{
155    ASSERT(m_webFrame);
156    bool isUserGesture = UserGestureIndicator::processingUserGesture();
157
158    // Ask the application to focus this WebView if the action is intiated by the user
159    if (isUserGesture)
160        m_webFrame->requestFocus();
161}
162void ChromeClientAndroid::unfocus() { notImplemented(); }
163
164bool ChromeClientAndroid::canTakeFocus(FocusDirection direction)
165{
166    return android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->chromeCanTakeFocus(direction);
167}
168
169void ChromeClientAndroid::takeFocus(FocusDirection direction)
170{
171    android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->chromeTakeFocus(direction);
172}
173
174void ChromeClientAndroid::focusedNodeChanged(Node* node)
175{
176    android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->focusNodeChanged(node);
177}
178
179void ChromeClientAndroid::focusedFrameChanged(Frame*) { notImplemented(); }
180
181Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&,
182        const WindowFeatures& features, const NavigationAction&)
183{
184    ASSERT(frame);
185#ifdef ANDROID_MULTIPLE_WINDOWS
186    if (frame->settings() && !(frame->settings()->supportMultipleWindows()))
187        // If the client doesn't support multiple windows, just return the current page
188        return frame->page();
189#endif
190
191    FloatRect window = windowRect();
192    bool dialog = features.dialog || !features.resizable
193            || (features.heightSet && features.height < window.height()
194                    && features.widthSet && features.width < window.width())
195            || (!features.menuBarVisible && !features.statusBarVisible
196                    && !features.toolBarVisible && !features.locationBarVisible
197                    && !features.scrollbarsVisible);
198    // fullscreen definitely means no dialog
199    if (features.fullscreen)
200        dialog = false;
201    WebCore::Frame* newFrame = m_webFrame->createWindow(dialog,
202            ScriptController::processingUserGesture());
203    if (newFrame) {
204        WebCore::Page* page = newFrame->page();
205        page->setGroupName(frame->page()->groupName());
206        return page;
207    }
208    return NULL;
209}
210
211void ChromeClientAndroid::show() { notImplemented(); }
212
213bool ChromeClientAndroid::canRunModal() { notImplemented(); return false; }
214void ChromeClientAndroid::runModal() { notImplemented(); }
215
216void ChromeClientAndroid::setToolbarsVisible(bool) { notImplemented(); }
217bool ChromeClientAndroid::toolbarsVisible() { notImplemented(); return false; }
218
219void ChromeClientAndroid::setStatusbarVisible(bool) { notImplemented(); }
220bool ChromeClientAndroid::statusbarVisible() { notImplemented(); return false; }
221
222void ChromeClientAndroid::setScrollbarsVisible(bool) { notImplemented(); }
223bool ChromeClientAndroid::scrollbarsVisible() { notImplemented(); return false; }
224
225void ChromeClientAndroid::setMenubarVisible(bool) { notImplemented(); }
226bool ChromeClientAndroid::menubarVisible() { notImplemented(); return false; }
227
228void ChromeClientAndroid::setResizable(bool) { notImplemented(); }
229
230#if ENABLE(CONTEXT_MENUS)
231void ChromeClientAndroid::showContextMenu() { notImplemented(); }
232#endif
233
234// This function is called by the JavaScript bindings to print usually an error to
235// a message console. Pass the message to the java side so that the client can
236// handle it as it sees fit.
237void ChromeClientAndroid::addMessageToConsole(MessageSource, MessageType, MessageLevel msgLevel, const String& message, unsigned int lineNumber, const String& sourceID) {
238    android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->addMessageToConsole(message, lineNumber, sourceID, msgLevel);
239}
240
241void ChromeClientAndroid::formDidBlur(const WebCore::Node* node) { notImplemented(); }
242bool ChromeClientAndroid::canRunBeforeUnloadConfirmPanel() { return true; }
243bool ChromeClientAndroid::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) {
244    String url = frame->document()->documentURI();
245    return android::WebViewCore::getWebViewCore(frame->view())->jsUnload(url, message);
246}
247
248void ChromeClientAndroid::closeWindowSoon()
249{
250    ASSERT(m_webFrame);
251    Page* page = m_webFrame->page();
252    Frame* mainFrame = page->mainFrame();
253    // This will prevent javascript cross-scripting during unload
254    page->setGroupName(String());
255    // Stop loading but do not send the unload event
256    mainFrame->loader()->stopLoading(UnloadEventPolicyNone);
257    // Cancel all pending loaders
258    mainFrame->loader()->stopAllLoaders();
259    // Remove all event listeners so that no javascript can execute as a result
260    // of mouse/keyboard events.
261    mainFrame->document()->removeAllEventListeners();
262    // Close the window.
263    m_webFrame->closeWindow(android::WebViewCore::getWebViewCore(mainFrame->view()));
264}
265
266void ChromeClientAndroid::runJavaScriptAlert(Frame* frame, const String& message)
267{
268    String url = frame->document()->documentURI();
269
270    android::WebViewCore::getWebViewCore(frame->view())->jsAlert(url, message);
271}
272
273bool ChromeClientAndroid::runJavaScriptConfirm(Frame* frame, const String& message)
274{
275    String url = frame->document()->documentURI();
276
277    return android::WebViewCore::getWebViewCore(frame->view())->jsConfirm(url, message);
278}
279
280/* This function is called for the javascript method Window.prompt(). A dialog should be shown on
281 * the screen with an input put box. First param is the text, the second is the default value for
282 * the input box, third is return param. If the function returns true, the value set in the third parameter
283 * is provided to javascript, else null is returned to the script.
284 */
285bool ChromeClientAndroid::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
286{
287    String url = frame->document()->documentURI();
288    return android::WebViewCore::getWebViewCore(frame->view())->jsPrompt(url, message, defaultValue, result);
289}
290void ChromeClientAndroid::setStatusbarText(const String&) { notImplemented(); }
291
292// This is called by the JavaScript interpreter when a script has been running for a long
293// time. A dialog should be shown to the user asking them if they would like to cancel the
294// Javascript. If true is returned, the script is cancelled.
295// To make a device more responsive, we default to return true to disallow long running script.
296// This implies that some of scripts will not be completed.
297bool ChromeClientAndroid::shouldInterruptJavaScript() {
298  FrameView* frameView = m_webFrame->page()->mainFrame()->view();
299  return android::WebViewCore::getWebViewCore(frameView)->jsInterrupt();
300}
301
302KeyboardUIMode ChromeClientAndroid::keyboardUIMode()
303{
304    return KeyboardAccessTabsToLinks;
305}
306
307IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); }
308
309void ChromeClientAndroid::invalidateWindow(const IntRect&, bool)
310{
311    notImplemented();
312}
313
314void ChromeClientAndroid::invalidateContentsAndWindow(const IntRect& updateRect, bool /*immediate*/)
315{
316    notImplemented();
317}
318
319void ChromeClientAndroid::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
320{
321    notImplemented();
322}
323
324// new to change 38068 (Nov 6, 2008)
325void ChromeClientAndroid::scroll(const IntSize& scrollDelta,
326        const IntRect& rectToScroll, const IntRect& clipRect) {
327    notImplemented();
328}
329
330// new to change 38068 (Nov 6, 2008)
331IntPoint ChromeClientAndroid::screenToWindow(const IntPoint&) const {
332    notImplemented();
333    return IntPoint();
334}
335
336// new to change 38068 (Nov 6, 2008)
337IntRect ChromeClientAndroid::windowToScreen(const IntRect&) const {
338    notImplemented();
339    return IntRect();
340}
341
342PlatformPageClient ChromeClientAndroid::platformPageClient() const {
343    Page* page = m_webFrame->page();
344    Frame* mainFrame = page->mainFrame();
345    FrameView* view = mainFrame->view();
346    PlatformWidget viewBridge = view->platformWidget();
347    return viewBridge;
348}
349
350void ChromeClientAndroid::contentsSizeChanged(Frame*, const IntSize&) const
351{
352    notImplemented();
353}
354
355void ChromeClientAndroid::scrollRectIntoView(const IntRect&, const ScrollView*) const
356{
357    notImplemented();
358}
359
360void ChromeClientAndroid::formStateDidChange(const Node*)
361{
362    notImplemented();
363}
364
365void ChromeClientAndroid::scrollbarsModeDidChange() const
366{
367    notImplemented();
368}
369
370void ChromeClientAndroid::dispatchViewportDataDidChange(const ViewportArguments& input) const {
371#ifdef ANDROID_META_SUPPORT
372    const ViewportArguments emptyArgument;
373    if (input == emptyArgument) {
374        // Empty Argument is for a page with no viewport meta tag; so reset everything.
375        m_webFrame->page()->settings()->resetMetadataSettings();
376    }
377    Document* doc = m_webFrame->page()->mainFrame()->document();
378    if (!doc->ownerElement()) {
379        FrameView* view = doc->view();
380        if (view)
381            PlatformBridge::updateViewport(view);
382    }
383#endif
384}
385
386void ChromeClientAndroid::mouseDidMoveOverElement(const HitTestResult&, unsigned int) {}
387void ChromeClientAndroid::setToolTip(const String&, TextDirection) {}
388void ChromeClientAndroid::print(Frame*) {}
389
390/*
391 * This function is called on the main (webcore) thread by SQLTransaction::deliverQuotaIncreaseCallback.
392 * The way that the callback mechanism is designed inside SQLTransaction means that there must be a new quota
393 * (which may be equal to the old quota if the user did not allow more quota) when this function returns. As
394 * we call into the browser thread to ask what to do with the quota, we block here and get woken up when the
395 * browser calls the native WebViewCore::SetDatabaseQuota method with the new quota value.
396 */
397#if ENABLE(DATABASE)
398void ChromeClientAndroid::exceededDatabaseQuota(Frame* frame, const String& name)
399{
400    SecurityOrigin* origin = frame->document()->securityOrigin();
401    DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker();
402
403    m_isNewQuotaSet = false;
404
405    // This origin is being tracked and has exceeded it's quota. Call into
406    // the Java side of things to inform the user.
407    unsigned long long currentQuota = 0;
408    if (tracker.hasEntryForOrigin(origin))
409        currentQuota = tracker.quotaForOrigin(origin);
410
411    unsigned long long estimatedSize = 0;
412
413    // Only update estimatedSize if we are trying to create a a new database, i.e. the usage for the database is 0.
414    if (tracker.usageForDatabase(name, origin) == 0)
415        estimatedSize = tracker.detailsForNameAndOrigin(name, origin).expectedUsage();
416
417    if (android::WebViewCore::getWebViewCore(frame->view())->exceededDatabaseQuota(frame->document()->documentURI(), name, currentQuota, estimatedSize)) {
418        // We've sent notification to the browser so now wait for it to come back.
419        m_quotaThreadLock.lock();
420        while (!m_isNewQuotaSet) {
421            m_quotaThreadCondition.wait(m_quotaThreadLock);
422        }
423        m_quotaThreadLock.unlock();
424    } else {
425        // We failed to send the message to the UI thread to request a new quota,
426        // so just use the current quota as a default.
427        m_newQuota = currentQuota;
428    }
429
430    if (m_newQuota < currentQuota)
431        m_newQuota = currentQuota;
432
433    // If new quota is unavailable, we may be able to resolve the situation by
434    // shrinking the quota of an origin that asked for a lot but is only using a
435    // little. If we find such a site, shrink it's quota and ask Java to try
436    // again.
437    if (m_newQuota == currentQuota && !m_triedToReclaimDBQuota) {
438        m_triedToReclaimDBQuota = true; // we should only try this once per quota overflow.
439        unsigned long long reclaimedQuotaBytes = tryToReclaimDatabaseQuota(origin);
440
441        // If we were able to free up enough space, try asking Java again.
442        // Otherwise, give up and deny the new database. :(
443        if (reclaimedQuotaBytes >= estimatedSize) {
444            exceededDatabaseQuota(frame, name);
445            return;
446        }
447    }
448
449    // Update the DatabaseTracker with the new quota value (if the user declined
450    // new quota, this may equal the old quota)
451    tracker.setQuota(origin, m_newQuota);
452    m_triedToReclaimDBQuota = false;
453}
454
455static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota) {
456    DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker();
457    Vector<RefPtr<SecurityOrigin> > origins;
458    tracker.origins(origins);
459    unsigned long long reclaimedQuotaBytes = 0;
460    for (unsigned i = 0; i < origins.size(); i++) {
461        SecurityOrigin* originToReclaimFrom = origins[i].get();
462
463        // Don't try to reclaim from the origin that has exceeded its quota.
464        if (originToReclaimFrom->equal(originNeedingQuota))
465            continue;
466
467        unsigned long long originUsage = tracker.usageForOrigin(originToReclaimFrom);
468        unsigned long long originQuota = tracker.quotaForOrigin(originToReclaimFrom);
469        // If the origin has a quota that is more than it's current usage +1MB, shrink it.
470        static const int ONE_MB = 1 * 1024 * 1024;
471        if (originUsage + ONE_MB < originQuota) {
472            unsigned long long newQuota = originUsage + ONE_MB;
473            tracker.setQuota(originToReclaimFrom, newQuota);
474            reclaimedQuotaBytes += originQuota - newQuota;
475        }
476    }
477    return reclaimedQuotaBytes;
478}
479#endif
480
481#if ENABLE(OFFLINE_WEB_APPLICATIONS)
482void ChromeClientAndroid::reachedMaxAppCacheSize(int64_t spaceNeeded)
483{
484    m_isNewQuotaSet = false;
485    Page* page = m_webFrame->page();
486    Frame* mainFrame = page->mainFrame();
487    FrameView* view = mainFrame->view();
488
489    // If we fail to send the message to the UI thread to request a new quota,
490    // there's nothing to do.
491    if (!android::WebViewCore::getWebViewCore(view)->reachedMaxAppCacheSize(spaceNeeded))
492        return;
493
494    // We've sent notification to the browser so now wait for it to come back.
495    m_quotaThreadLock.lock();
496    while (!m_isNewQuotaSet) {
497       m_quotaThreadCondition.wait(m_quotaThreadLock);
498    }
499    m_quotaThreadLock.unlock();
500    if (m_newQuota > 0) {
501        WebCore::cacheStorage().setMaximumSize(m_newQuota);
502        // Now the app cache will retry the saving the previously failed cache.
503    }
504}
505#endif
506
507void ChromeClientAndroid::populateVisitedLinks()
508{
509    Page* page = m_webFrame->page();
510    Frame* mainFrame = page->mainFrame();
511    FrameView* view = mainFrame->view();
512    android::WebViewCore::getWebViewCore(view)->populateVisitedLinks(&page->group());
513}
514
515void ChromeClientAndroid::runOpenPanel(Frame* frame,
516        PassRefPtr<FileChooser> chooser)
517{
518    android::WebViewCore* core = android::WebViewCore::getWebViewCore(
519            frame->view());
520    core->openFileChooser(chooser);
521}
522
523void ChromeClientAndroid::chooseIconForFiles(const Vector<WTF::String>&, FileChooser*)
524{
525    notImplemented();
526}
527
528void ChromeClientAndroid::setCursor(const Cursor&)
529{
530    notImplemented();
531}
532
533void ChromeClientAndroid::wakeUpMainThreadWithNewQuota(long long newQuota) {
534    MutexLocker locker(m_quotaThreadLock);
535    m_newQuota = newQuota < 0 ? 0 : newQuota;
536    m_isNewQuotaSet = true;
537    m_quotaThreadCondition.signal();
538}
539
540#if ENABLE(TOUCH_EVENTS)
541void ChromeClientAndroid::needTouchEvents(bool needTouchEvents)
542{
543    FrameView* frameView = m_webFrame->page()->mainFrame()->view();
544    android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
545    if (core)
546        core->needTouchEvents(needTouchEvents);
547}
548#endif
549
550bool ChromeClientAndroid::selectItemWritingDirectionIsNatural()
551{
552    return false;
553}
554
555bool ChromeClientAndroid::selectItemAlignmentFollowsMenuWritingDirection()
556{
557    return false;
558}
559
560PassRefPtr<PopupMenu> ChromeClientAndroid::createPopupMenu(PopupMenuClient* client) const
561{
562    return adoptRef(new PopupMenuAndroid(static_cast<ListPopupMenuClient*>(client)));
563}
564
565PassRefPtr<SearchPopupMenu> ChromeClientAndroid::createSearchPopupMenu(PopupMenuClient*) const
566{
567    return adoptRef(new SearchPopupMenuAndroid);
568}
569
570void ChromeClientAndroid::reachedApplicationCacheOriginQuota(SecurityOrigin*)
571{
572    notImplemented();
573}
574
575#if ENABLE(VIDEO)
576bool ChromeClientAndroid::supportsFullscreenForNode(const Node* node)
577{
578      return node->hasTagName(HTMLNames::videoTag);
579}
580
581void ChromeClientAndroid::enterFullscreenForNode(Node* node)
582{
583      if (!node->hasTagName(HTMLNames::videoTag))
584          return;
585
586      HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(node);
587
588      FrameView* frameView = m_webFrame->page()->mainFrame()->view();
589      android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
590      if (core)
591          core->enterFullscreenForVideoLayer();
592
593      MediaPlayer* player = videoElement->player();
594      if (player) {
595          // We need to use the same document object as the
596          // MediaPlayerPrivateAndroid::onStopFullscreen().
597          Document* doc = player->mediaPlayerClient()->mediaPlayerOwningDocument();
598          if (doc)
599              doc->webkitWillEnterFullScreenForElement(videoElement);
600          // Now the player is responsible to trigger to the java side for
601          // entering full screen mode.
602          player->enterFullscreenMode();
603      }
604}
605
606void ChromeClientAndroid::exitFullscreenForNode(Node* node)
607{
608    FrameView* frameView = m_webFrame->page()->mainFrame()->view();
609    android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
610    if (core)
611        core->exitFullscreenVideo();
612    return;
613}
614#endif
615
616#if ENABLE(FULLSCREEN_API)
617void ChromeClientAndroid::exitFullScreenForElement(Element* element)
618{
619    if (!element)
620        return;
621
622    HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(element);
623    videoElement->exitFullscreen();
624}
625#endif
626
627}
628