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