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 #include "FrameLoaderClientAndroid.h" 30 31 #include "BackForwardList.h" 32 #include "CachedFrame.h" 33 #include "CachedFramePlatformDataAndroid.h" 34 #include "Chrome.h" 35 #include "ChromeClientAndroid.h" 36 #include "DOMImplementation.h" 37 #include "Document.h" 38 #include "DocumentLoader.h" 39 #include "EditorClientAndroid.h" 40 #include "Frame.h" 41 #include "FrameLoader.h" 42 #include "FrameNetworkingContextAndroid.h" 43 #include "FrameTree.h" 44 #include "FrameView.h" 45 #include "GraphicsContext.h" 46 #include "HTMLFrameOwnerElement.h" 47 #include "HTMLPlugInElement.h" 48 #include "HistoryItem.h" 49 #include "IconDatabase.h" 50 #include "MIMETypeRegistry.h" 51 #include "NotImplemented.h" 52 #include "PackageNotifier.h" 53 #include "Page.h" 54 #include "PlatformBridge.h" 55 #include "PlatformGraphicsContext.h" 56 #include "PlatformString.h" 57 #include "PluginDatabase.h" 58 #include "PluginView.h" 59 #include "PluginViewBase.h" 60 #include "ProgressTracker.h" 61 #include "RenderPart.h" 62 #include "RenderView.h" 63 #include "RenderWidget.h" 64 #include "ResourceError.h" 65 #include "ResourceHandle.h" 66 #include "ResourceHandleInternal.h" 67 #include "SelectionController.h" 68 #include "Settings.h" 69 #include "SkCanvas.h" 70 #include "SkRect.h" 71 #include "TextEncoding.h" 72 #include "WebCoreFrameBridge.h" 73 #include "WebHistory.h" 74 #include "WebIconDatabase.h" 75 #include "WebFrameView.h" 76 #include "WebViewClientError.h" 77 #include "WebViewCore.h" 78 #include "autofill/WebAutofill.h" 79 80 #include <androidfw/AssetManager.h> 81 #include <wtf/text/CString.h> 82 83 #define verifiedOk() // Verified that we don't need to implement this. 84 85 extern android::AssetManager* globalAssetManager(); 86 87 namespace android { 88 89 static const int EXTRA_LAYOUT_DELAY = 1000; 90 91 FrameLoaderClientAndroid::FrameLoaderClientAndroid(WebFrame* webframe) 92 : m_frame(NULL) 93 , m_webFrame(webframe) 94 , m_manualLoader(NULL) 95 , m_hasSentResponseToPlugin(false) 96 , m_onDemandPluginsEnabled(false) 97 , m_didReceiveServerRedirect(false) { 98 Retain(m_webFrame); 99 } 100 101 FrameLoaderClientAndroid* FrameLoaderClientAndroid::get(const WebCore::Frame* frame) 102 { 103 return static_cast<FrameLoaderClientAndroid*> (frame->loader()->client()); 104 } 105 106 void FrameLoaderClientAndroid::frameLoaderDestroyed() { 107 registerForIconNotification(false); 108 m_frame = 0; 109 Release(m_webFrame); 110 delete this; 111 } 112 113 bool FrameLoaderClientAndroid::hasWebView() const { 114 // FIXME, 115 // there is one web view per page, or top frame. 116 // as android's view is created from Java side, it is always there. 117 return true; 118 } 119 120 void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) { 121 m_onDemandPluginsEnabled = false; 122 // don't use representation 123 verifiedOk(); 124 } 125 126 void FrameLoaderClientAndroid::forceLayout() { 127 ASSERT(m_frame); 128 m_frame->view()->forceLayout(); 129 // FIXME, should we adjust view size here? 130 m_frame->view()->adjustViewSize(); 131 } 132 133 void FrameLoaderClientAndroid::forceLayoutForNonHTML() { 134 notImplemented(); 135 } 136 137 void FrameLoaderClientAndroid::setCopiesOnScroll() { 138 // this is a hint about whether we need to force redraws, or can 139 // just copy the scrolled content. Since we always force a redraw 140 // anyways, we can ignore this call. 141 verifiedOk(); 142 } 143 144 void FrameLoaderClientAndroid::detachedFromParent2() { 145 // FIXME, ready to detach frame from view 146 } 147 148 void FrameLoaderClientAndroid::detachedFromParent3() { 149 // FIXME, ready to release view 150 notImplemented(); 151 } 152 153 // This function is responsible for associating the "id" with a given 154 // subresource load. The following functions that accept an "id" are 155 // called for each subresource, so they should not be dispatched to the m_frame. 156 void FrameLoaderClientAndroid::assignIdentifierToInitialRequest(unsigned long id, 157 DocumentLoader*, const ResourceRequest&) { 158 notImplemented(); 159 } 160 161 void FrameLoaderClientAndroid::dispatchWillSendRequest(DocumentLoader*, unsigned long id, 162 ResourceRequest&, const ResourceResponse&) { 163 notImplemented(); 164 } 165 166 bool FrameLoaderClientAndroid::shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) 167 { 168 notImplemented(); 169 return false; 170 } 171 172 void FrameLoaderClientAndroid::dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, 173 unsigned long id, const AuthenticationChallenge&) { 174 notImplemented(); 175 } 176 177 void FrameLoaderClientAndroid::dispatchDidCancelAuthenticationChallenge(DocumentLoader*, 178 unsigned long id, const AuthenticationChallenge&) { 179 notImplemented(); 180 } 181 182 void FrameLoaderClientAndroid::dispatchDidReceiveResponse(DocumentLoader*, 183 unsigned long id, const ResourceResponse&) { 184 notImplemented(); 185 } 186 187 void FrameLoaderClientAndroid::dispatchDidReceiveContentLength(DocumentLoader*, 188 unsigned long id, int lengthReceived) { 189 notImplemented(); 190 } 191 192 void FrameLoaderClientAndroid::dispatchDidFinishLoading(DocumentLoader*, 193 unsigned long id) { 194 notImplemented(); 195 } 196 197 void FrameLoaderClientAndroid::dispatchDidFailLoading(DocumentLoader* docLoader, 198 unsigned long id, const ResourceError&) { 199 notImplemented(); 200 } 201 202 bool FrameLoaderClientAndroid::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, 203 const ResourceRequest&, const ResourceResponse&, int length) { 204 notImplemented(); 205 return false; 206 } 207 208 void FrameLoaderClientAndroid::dispatchDidHandleOnloadEvents() { 209 } 210 211 void FrameLoaderClientAndroid::dispatchDidReceiveServerRedirectForProvisionalLoad() { 212 ASSERT(!m_didReceiveServerRedirect); 213 m_didReceiveServerRedirect = true; 214 } 215 216 void FrameLoaderClientAndroid::dispatchDidCancelClientRedirect() { 217 notImplemented(); 218 } 219 220 void FrameLoaderClientAndroid::dispatchWillPerformClientRedirect(const KURL&, 221 double interval, double fireDate) { 222 notImplemented(); 223 } 224 225 void FrameLoaderClientAndroid::dispatchDidChangeLocationWithinPage() { 226 notImplemented(); 227 } 228 229 void FrameLoaderClientAndroid::dispatchDidPushStateWithinPage() 230 { 231 notImplemented(); 232 } 233 234 void FrameLoaderClientAndroid::dispatchDidReplaceStateWithinPage() 235 { 236 notImplemented(); 237 } 238 239 void FrameLoaderClientAndroid::dispatchDidPopStateWithinPage() 240 { 241 notImplemented(); 242 } 243 244 void FrameLoaderClientAndroid::dispatchWillClose() { 245 notImplemented(); 246 } 247 248 void FrameLoaderClientAndroid::dispatchDidReceiveIcon() { 249 ASSERT(m_frame); 250 if (m_frame->tree() && m_frame->tree()->parent()) 251 return; 252 WTF::String url(m_frame->document()->url().string()); 253 // Try to obtain the icon image. 254 // FIXME: This method should not be used from outside WebCore and will be removed. 255 // http://trac.webkit.org/changeset/81484 256 WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16)); 257 // If the request fails, try the original request url. 258 if (!icon) { 259 DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); 260 KURL originalURL = docLoader->originalRequest().url(); 261 // FIXME: This method should not be used from outside WebCore and will be removed. 262 // http://trac.webkit.org/changeset/81484 263 icon = WebCore::iconDatabase().synchronousIconForPageURL(originalURL, WebCore::IntSize(16, 16)); 264 } 265 // There is a bug in webkit where cancelling an icon load is treated as a 266 // failure. When this is fixed, we can ASSERT again that we have an icon. 267 if (icon) { 268 ALOGV("Received icon (%p) for %s", icon, 269 url.utf8().data()); 270 m_webFrame->didReceiveIcon(icon); 271 } else { 272 ALOGV("Icon data for %s unavailable, registering for notification...", 273 url.utf8().data()); 274 registerForIconNotification(); 275 } 276 } 277 278 void FrameLoaderClientAndroid::dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) { 279 ASSERT(m_frame); 280 // Do not report sub frame touch icons 281 if (m_frame->tree() && m_frame->tree()->parent()) 282 return; 283 m_webFrame->didReceiveTouchIconURL(url, precomposed); 284 } 285 286 void FrameLoaderClientAndroid::dispatchDidStartProvisionalLoad() { 287 notImplemented(); 288 } 289 290 void FrameLoaderClientAndroid::dispatchDidReceiveTitle(const StringWithDirection& title) { 291 ASSERT(m_frame); 292 // Used to check for FrameLoadTypeStandard but we only want to send the title for 293 // the top frame and not sub-frames. 294 // FIXME: Use direction of title. 295 if (!m_frame->tree() || !m_frame->tree()->parent()) { 296 m_webFrame->setTitle(title.string()); 297 } 298 } 299 300 void FrameLoaderClientAndroid::dispatchDidCommitLoad() { 301 #if ENABLE(WEB_AUTOFILL) 302 if (m_frame == m_frame->page()->mainFrame()) { 303 EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(m_frame->page()->editorClient()); 304 WebAutofill* autoFill = editorC->getAutofill(); 305 autoFill->reset(); 306 } 307 #endif 308 verifiedOk(); 309 } 310 311 static void loadDataIntoFrame(Frame* frame, KURL baseUrl, const String& url, 312 const String& data) { 313 if (baseUrl.isEmpty()) { 314 baseUrl = blankURL(); 315 } 316 ResourceRequest request(baseUrl); 317 CString cstr = data.utf8(); 318 RefPtr<WebCore::SharedBuffer> buf = WebCore::SharedBuffer::create(cstr.data(), cstr.length()); 319 SubstituteData subData(buf, String("text/html"), String("utf-8"), 320 KURL(KURL(), url)); 321 frame->loader()->load(request, subData, false); 322 } 323 324 void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) { 325 ASSERT(m_frame); 326 // Ignore ErrorInterrupted since it is due to a policy interruption. This 327 // is caused by a decision to download the main resource rather than 328 // display it. 329 if (error.errorCode() == InternalErrorInterrupted 330 || error.errorCode() == InternalErrorCancelled) { 331 // If we decided to download the main resource or if the user cancelled 332 // it, make sure we report that the load is done. 333 didFinishLoad(); 334 return; 335 } 336 337 AssetManager* am = globalAssetManager(); 338 339 // Check to see if the error code was not generated internally 340 WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain; 341 if ((error.errorCode() == ErrorFile || 342 error.errorCode() == ErrorFileNotFound) && 343 (!error.localizedDescription().isEmpty())) { 344 id = WebCore::PlatformBridge::LoadError; 345 } 346 String filename = m_webFrame->getRawResourceFilename(id); 347 if (filename.isEmpty()) 348 return; 349 350 // Grab the error page from the asset manager 351 Asset* a = am->openNonAsset( 352 filename.utf8().data(), Asset::ACCESS_BUFFER); 353 if (!a) 354 return; 355 356 // Take the failing url and encode html entities so javascript urls are not 357 // executed. 358 CString failingUrl = error.failingURL().utf8(); 359 WTF::Vector<char> url; 360 int len = failingUrl.length(); 361 const char* data = failingUrl.data(); 362 for (int i = 0; i < len; i++) { 363 char c = data[i]; 364 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 365 || (c >= '0' && c <= '9')) 366 url.append(c); 367 else { 368 char buf[16]; 369 int res = sprintf(buf, "&#%d;", c); 370 buf[res] = 0; 371 url.append(buf, res); 372 } 373 } 374 // Vector sets up its data buffer lazilly, so if failingUrl is the empty 375 // string, the data buffer will be null. This will result in sanitizedUrl 376 // being null, and the string substitution below will be a no-op. 377 // FIXME: Ideally we'd always have a non-empty URL, or at least improve the 378 // wording of the error page in this case. See http://b/5293782. 379 String sanitizedUrl = url.data() ? String(url.data(), url.size()) : ""; 380 381 // Replace all occurances of %s with the failing url. 382 String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength()); 383 s = s.replace("%s", sanitizedUrl); 384 385 // Replace all occurances of %e with the error text 386 s = s.replace("%e", error.localizedDescription()); 387 388 // Create the request and the substitute data and tell the FrameLoader to 389 // load with the replacement data. 390 // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for 391 // invalidate URL string. 392 loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s); 393 394 // Delete the asset. 395 delete a; 396 397 // Report that the load is finished, since it failed. 398 didFinishLoad(); 399 } 400 401 void FrameLoaderClientAndroid::dispatchDidFailLoad(const ResourceError&) { 402 // called when page is completed with error 403 didFinishLoad(); 404 } 405 406 void FrameLoaderClientAndroid::dispatchDidFinishDocumentLoad() { 407 // called when finishedParsing 408 notImplemented(); 409 } 410 411 void FrameLoaderClientAndroid::dispatchDidFinishLoad() { 412 didFinishLoad(); 413 } 414 415 void FrameLoaderClientAndroid::dispatchDidFirstLayout() { 416 ASSERT(m_frame); 417 // set EXTRA_LAYOUT_DELAY if the loader is not completed yet 418 if (!m_frame->loader()->isComplete()) 419 m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY); 420 // we need to do this here instead of dispatchDidFirstVisuallyNonEmptyLayout 421 // so that about:blank will update the screen. 422 if (!m_frame->tree()->parent()) { 423 // Only need to notify Java side for the top frame 424 WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view()); 425 if (core) 426 core->didFirstLayout(); 427 } 428 } 429 430 void FrameLoaderClientAndroid::dispatchDidFirstVisuallyNonEmptyLayout() 431 { 432 notImplemented(); 433 } 434 435 Frame* FrameLoaderClientAndroid::dispatchCreatePage(const NavigationAction&) { 436 ASSERT(m_frame); 437 #ifdef ANDROID_MULTIPLE_WINDOWS 438 if (m_frame->settings() && m_frame->settings()->supportMultipleWindows()) 439 // Always a user gesture since window.open maps to 440 // ChromeClientAndroid::createWindow 441 return m_webFrame->createWindow(false, true); 442 else 443 #endif 444 // If the client doesn't support multiple windows, just replace the 445 // current frame's contents. 446 return m_frame; 447 } 448 449 void FrameLoaderClientAndroid::dispatchShow() { 450 ASSERT(m_frame); 451 m_frame->view()->invalidate(); 452 } 453 454 455 static bool TreatAsAttachment(const String& content_disposition) { 456 // Some broken sites just send 457 // Content-Disposition: ; filename="file" 458 // screen those out here. 459 if (content_disposition.startsWith(";")) 460 return false; 461 462 if (content_disposition.startsWith("inline", false)) 463 return false; 464 465 // Some broken sites just send 466 // Content-Disposition: filename="file" 467 // without a disposition token... screen those out. 468 if (content_disposition.startsWith("filename", false)) 469 return false; 470 471 // Also in use is Content-Disposition: name="file" 472 if (content_disposition.startsWith("name", false)) 473 return false; 474 475 // We have a content-disposition of "attachment" or unknown. 476 // RFC 2183, section 2.8 says that an unknown disposition 477 // value should be treated as "attachment" 478 return true; 479 } 480 481 void FrameLoaderClientAndroid::dispatchDecidePolicyForResponse(FramePolicyFunction func, 482 const ResourceResponse& response, const ResourceRequest& request) { 483 ASSERT(m_frame); 484 ASSERT(func); 485 if (!func) 486 return; 487 488 PolicyChecker* policy = m_frame->loader()->policyChecker(); 489 490 if (request.isNull()) { 491 (policy->*func)(PolicyIgnore); 492 return; 493 } 494 // Default to Use (display internally). 495 PolicyAction action = PolicyUse; 496 // Check if we should Download instead. 497 const String& content_disposition = response.httpHeaderField("Content-Disposition"); 498 if (!content_disposition.isEmpty() && 499 TreatAsAttachment(content_disposition)) { 500 // Server wants to override our normal policy. 501 // Check to see if we are a sub frame (main frame has no owner element) 502 if (m_frame->ownerElement() != 0) 503 action = PolicyIgnore; 504 else 505 action = PolicyDownload; 506 (policy->*func)(action); 507 return; 508 } 509 510 // Ask if it can be handled internally. 511 if (!canShowMIMEType(response.mimeType())) { 512 // Check to see if we are a sub frame (main frame has no owner element) 513 if (m_frame->ownerElement() != 0) 514 action = PolicyIgnore; 515 else 516 action = PolicyDownload; 517 (policy->*func)(action); 518 return; 519 } 520 // A status code of 204 indicates no content change. Ignore the result. 521 WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); 522 if (docLoader->response().httpStatusCode() == 204) 523 action = PolicyIgnore; 524 (policy->*func)(action); 525 } 526 527 void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func, 528 const NavigationAction& action, const ResourceRequest& request, 529 PassRefPtr<FormState> formState, const String& frameName) { 530 ASSERT(m_frame); 531 ASSERT(func); 532 if (!func) 533 return; 534 535 if (request.isNull()) { 536 (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); 537 return; 538 } 539 540 if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted) 541 m_frame->loader()->resetMultipleFormSubmissionProtection(); 542 543 // If we get to this point it means that a link has a target that was not 544 // found by the frame tree. Instead of creating a new frame, return the 545 // current frame in dispatchCreatePage. 546 if (canHandleRequest(request)) 547 (m_frame->loader()->policyChecker()->*func)(PolicyUse); 548 else 549 (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); 550 } 551 552 void FrameLoaderClientAndroid::cancelPolicyCheck() { 553 notImplemented(); 554 } 555 556 void FrameLoaderClientAndroid::dispatchUnableToImplementPolicy(const ResourceError&) { 557 notImplemented(); 558 } 559 560 void FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction(FramePolicyFunction func, 561 const NavigationAction& action, const ResourceRequest& request, 562 PassRefPtr<FormState> formState) { 563 ASSERT(m_frame); 564 ASSERT(func); 565 if (!func) 566 return; 567 if (request.isNull()) { 568 (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); 569 return; 570 } 571 572 // Reset multiple form submission protection. If this is a resubmission, we check with the 573 // user and reset the protection if they choose to resubmit the form (see WebCoreFrameBridge.cpp) 574 if (action.type() == NavigationTypeFormSubmitted) 575 m_frame->loader()->resetMultipleFormSubmissionProtection(); 576 577 if (action.type() == NavigationTypeFormResubmitted) { 578 m_webFrame->decidePolicyForFormResubmission(func); 579 return; 580 } else 581 (m_frame->loader()->policyChecker()->*func)(PolicyUse); 582 } 583 584 void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) { 585 ASSERT(m_frame); 586 ASSERT(func); 587 (m_frame->loader()->policyChecker()->*func)(PolicyUse); 588 } 589 590 void FrameLoaderClientAndroid::dispatchWillSendSubmitEvent(HTMLFormElement* form) 591 { 592 if (m_webFrame->shouldSaveFormData()) 593 m_webFrame->saveFormData(form); 594 } 595 596 void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) { 597 notImplemented(); 598 } 599 600 void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) { 601 notImplemented(); 602 } 603 604 void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) { 605 ASSERT(m_frame); 606 if (m_manualLoader) { 607 m_manualLoader->didFail(error); 608 m_manualLoader = NULL; 609 m_hasSentResponseToPlugin = false; 610 } else { 611 if (!error.isNull() && error.errorCode() >= InternalErrorLast && error.errorCode() != ERROR_OK) 612 m_webFrame->reportError(error.errorCode(), 613 error.localizedDescription(), error.failingURL()); 614 } 615 } 616 617 // This function is called right before the progress is updated. 618 void FrameLoaderClientAndroid::willChangeEstimatedProgress() { 619 verifiedOk(); 620 } 621 622 // This function is called after the progress has been updated. The bad part 623 // about this is that when a page is completed, this function is called after 624 // the progress has been reset to 0. 625 void FrameLoaderClientAndroid::didChangeEstimatedProgress() { 626 verifiedOk(); 627 } 628 629 // This will give us the initial estimate when the page first starts to load. 630 void FrameLoaderClientAndroid::postProgressStartedNotification() { 631 ASSERT(m_frame); 632 if (m_frame->page()) 633 m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress()); 634 } 635 636 // This will give us any updated progress including the final progress. 637 void FrameLoaderClientAndroid::postProgressEstimateChangedNotification() { 638 ASSERT(m_frame); 639 if (m_frame->page()) 640 m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress()); 641 } 642 643 // This is just a notification that the progress has finished. Don't call 644 // setProgress(1) because postProgressEstimateChangedNotification will do so. 645 void FrameLoaderClientAndroid::postProgressFinishedNotification() { 646 WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view()); 647 if (!m_frame->tree()->parent()) { 648 // only need to notify Java for the top frame 649 core->notifyProgressFinished(); 650 } 651 // notify plugins that the frame has loaded 652 core->notifyPluginsOnFrameLoad(m_frame); 653 } 654 655 void FrameLoaderClientAndroid::setMainFrameDocumentReady(bool) { 656 // this is only interesting once we provide an external API for the DOM 657 notImplemented(); 658 } 659 660 void FrameLoaderClientAndroid::startDownload(const ResourceRequest&) { 661 notImplemented(); 662 } 663 664 void FrameLoaderClientAndroid::willChangeTitle(DocumentLoader*) { 665 verifiedOk(); 666 } 667 668 void FrameLoaderClientAndroid::didChangeTitle(DocumentLoader* loader) { 669 verifiedOk(); 670 } 671 672 void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader) { 673 // Telling the frame we received some data and passing 0 as the data is our 674 // way to get work done that is normally done when the first bit of data is 675 // received, even for the case of a document with no data (like about:blank) 676 if (!m_manualLoader) { 677 committedLoad(docLoader, 0, 0); 678 return; 679 } 680 681 m_manualLoader->didFinishLoading(); 682 m_manualLoader = NULL; 683 m_hasSentResponseToPlugin = false; 684 } 685 686 void FrameLoaderClientAndroid::updateGlobalHistory() { 687 ASSERT(m_frame); 688 689 DocumentLoader* docLoader = m_frame->loader()->documentLoader(); 690 ASSERT(docLoader); 691 692 // Code copied from FrameLoader.cpp:createHistoryItem 693 // Only add this URL to the database if it is a valid page 694 if (docLoader->unreachableURL().isEmpty() 695 && docLoader->response().httpStatusCode() < 400) { 696 m_webFrame->updateVisitedHistory(docLoader->urlForHistory(), false); 697 if (!docLoader->serverRedirectSourceForHistory().isNull()) 698 m_webFrame->updateVisitedHistory(KURL(ParsedURLString, docLoader->serverRedirectDestinationForHistory()), false); 699 } 700 } 701 702 void FrameLoaderClientAndroid::updateGlobalHistoryRedirectLinks() { 703 // Note, do we need to do anything where there is no HistoryItem? If we call 704 // updateGlobalHistory(), we will add bunch of "data:xxx" urls for gmail.com 705 // which is not what we want. Opt to do nothing now. 706 } 707 708 bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const { 709 // hmmm, seems like we might do a more thoughtful check 710 ASSERT(m_frame); 711 return item != NULL; 712 } 713 714 bool FrameLoaderClientAndroid::shouldStopLoadingForHistoryItem(HistoryItem* item) const 715 { 716 return true; 717 } 718 719 void FrameLoaderClientAndroid::didDisplayInsecureContent() 720 { 721 notImplemented(); 722 } 723 724 void FrameLoaderClientAndroid::didRunInsecureContent(SecurityOrigin*, const KURL&) 725 { 726 notImplemented(); 727 } 728 729 void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) { 730 if (!m_manualLoader) 731 loader->commitData(data, length); 732 733 // commit data may have created a manual plugin loader 734 if (m_manualLoader) { 735 if (!m_hasSentResponseToPlugin) { 736 m_manualLoader->didReceiveResponse(loader->response()); 737 // Failure could cause the main document to have an error causing 738 // the manual loader to be reset. 739 if (!m_manualLoader) 740 return; 741 m_hasSentResponseToPlugin = true; 742 } 743 m_manualLoader->didReceiveData(data, length); 744 } 745 } 746 747 ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) { 748 return ResourceError(String(), InternalErrorCancelled, request.url(), String()); 749 } 750 751 ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) { 752 return ResourceError(String(), InternalErrorCannotShowUrl, request.url(), String()); 753 } 754 755 ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) { 756 return ResourceError(String(), InternalErrorInterrupted, request.url(), String()); 757 } 758 759 ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) { 760 return ResourceError(String(), InternalErrorCannotShowMimeType, request.url(), String()); 761 } 762 763 ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) { 764 return ResourceError(String(), InternalErrorFileDoesNotExist, request.url(), String()); 765 } 766 767 ResourceError FrameLoaderClientAndroid::pluginWillHandleLoadError(const ResourceResponse& request) { 768 return ResourceError(String(), InternalErrorPluginWillHandleLoadError, request.url(), String()); 769 } 770 771 bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) { 772 notImplemented(); 773 return false; 774 } 775 776 bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const { 777 // This is called by WebCore to determine if this load can be handled by the 778 // WebView. In general, we delegate to the WebFrame, which may ask the 779 // embedding application whether it wishes to hijack the load. However, we 780 // don't allow this if the load is ... 781 // - An intrapage navigation 782 // - An iframe with a HTTP or HTTPS scheme URL 783 bool canHandle = WebCore::equalIgnoringFragmentIdentifier(request.url(), m_frame->document()->url()) || 784 (request.url().protocol().startsWith("http", false) && m_frame->tree() && m_frame->tree()->parent()) || 785 m_webFrame->canHandleRequest(request); 786 787 // If this is a server-side redirect and the WebView will handle loading it, 788 // notify the WebFrame, which may notify the embedding application that 789 // we're loading a new URL. 790 if (m_didReceiveServerRedirect && canHandle) 791 m_webFrame->loadStarted(m_frame); 792 m_didReceiveServerRedirect = false; 793 794 return canHandle; 795 } 796 797 bool FrameLoaderClientAndroid::canShowMIMEType(const String& mimeType) const { 798 // FIXME: This looks like it has to do with whether or not a type can be 799 // shown "internally" (i.e. inside the browser) regardless of whether 800 // or not the browser is doing the rendering, e.g. a full page plugin. 801 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(mimeType) || 802 MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) || 803 MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || 804 (m_frame && m_frame->settings() 805 && m_frame->settings()->arePluginsEnabled() 806 && PluginDatabase::installedPlugins()->isMIMETypeRegistered( 807 mimeType)) || 808 (DOMImplementation::isTextMIMEType(mimeType) && 809 !mimeType.startsWith("text/vnd")) || 810 DOMImplementation::isXMLMIMEType(mimeType)) 811 return true; 812 return false; 813 } 814 815 bool FrameLoaderClientAndroid::canShowMIMETypeAsHTML(const String& mimeType) const { 816 return false; 817 } 818 819 bool FrameLoaderClientAndroid::representationExistsForURLScheme(const String&) const { 820 // don't use representation 821 verifiedOk(); 822 return false; 823 } 824 825 String FrameLoaderClientAndroid::generatedMIMETypeForURLScheme(const String& URLScheme) const { 826 // FIXME, copy from Apple's port 827 String mimetype("x-apple-web-kit/"); 828 mimetype.append(URLScheme.lower()); 829 return mimetype; 830 } 831 832 void FrameLoaderClientAndroid::frameLoadCompleted() { 833 // copied from Apple port, without this back with sub-frame will trigger ASSERT 834 ASSERT(m_frame); 835 } 836 837 void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) { 838 ASSERT(m_frame); 839 ASSERT(item); 840 // store the current scale (only) for the top frame 841 if (!m_frame->tree()->parent()) { 842 // We should have added a bridge when the child item was added to its 843 // parent. 844 AndroidWebHistoryBridge* bridge = item->bridge(); 845 ASSERT(bridge); 846 WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); 847 bridge->setScale(webViewCore->scale()); 848 bridge->setTextWrapScale(webViewCore->textWrapScale()); 849 } 850 851 WebCore::notifyHistoryItemChanged(item); 852 } 853 854 void FrameLoaderClientAndroid::restoreViewState() { 855 WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); 856 HistoryItem* item = m_frame->loader()->history()->currentItem(); 857 AndroidWebHistoryBridge* bridge = item->bridge(); 858 // restore the scale (only) for the top frame 859 if (!m_frame->tree()->parent()) { 860 webViewCore->restoreScale(bridge->scale(), bridge->textWrapScale()); 861 } 862 } 863 864 void FrameLoaderClientAndroid::dispatchDidAddBackForwardItem(HistoryItem* item) const { 865 ASSERT(m_frame); 866 m_webFrame->addHistoryItem(item); 867 } 868 869 void FrameLoaderClientAndroid::dispatchDidRemoveBackForwardItem(HistoryItem* item) const { 870 ASSERT(m_frame); 871 m_webFrame->removeHistoryItem(0); 872 } 873 874 void FrameLoaderClientAndroid::dispatchDidChangeBackForwardIndex() const { 875 ASSERT(m_frame); 876 BackForwardList* list = m_frame->page()->backForwardList(); 877 ASSERT(list); 878 m_webFrame->updateHistoryIndex(list->backListCount()); 879 } 880 881 void FrameLoaderClientAndroid::provisionalLoadStarted() { 882 ASSERT(m_frame); 883 m_webFrame->loadStarted(m_frame); 884 } 885 886 void FrameLoaderClientAndroid::didFinishLoad() { 887 ASSERT(m_frame); 888 m_frame->document()->setExtraLayoutDelay(0); 889 m_webFrame->didFinishLoad(m_frame); 890 } 891 892 void FrameLoaderClientAndroid::prepareForDataSourceReplacement() { 893 verifiedOk(); 894 } 895 896 PassRefPtr<DocumentLoader> FrameLoaderClientAndroid::createDocumentLoader( 897 const ResourceRequest& request, const SubstituteData& data) { 898 RefPtr<DocumentLoader> loader = DocumentLoader::create(request, data); 899 return loader.release(); 900 } 901 902 void FrameLoaderClientAndroid::setTitle(const StringWithDirection& title, const KURL& url) { 903 // Not needed. dispatchDidReceiveTitle is called immediately after this. 904 // url is used to update the Apple port history items. 905 verifiedOk(); 906 } 907 908 String FrameLoaderClientAndroid::userAgent(const KURL& u) { 909 return m_webFrame->userAgentForURL(&u); 910 } 911 912 void FrameLoaderClientAndroid::savePlatformDataToCachedFrame(WebCore::CachedFrame* cachedFrame) { 913 CachedFramePlatformDataAndroid* platformData = new CachedFramePlatformDataAndroid(m_frame->settings()); 914 cachedFrame->setCachedFramePlatformData(platformData); 915 } 916 917 void FrameLoaderClientAndroid::transitionToCommittedFromCachedFrame(WebCore::CachedFrame* cachedFrame) { 918 CachedFramePlatformDataAndroid* platformData = reinterpret_cast<CachedFramePlatformDataAndroid*>(cachedFrame->cachedFramePlatformData()); 919 #ifdef ANDROID_META_SUPPORT 920 platformData->restoreMetadata(m_frame->settings()); 921 #endif 922 923 #if ENABLE(ANDROID_OVERFLOW_SCROLL) 924 #else 925 WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); 926 927 webViewCore->clearContent(); 928 #endif 929 930 m_webFrame->transitionToCommitted(m_frame); 931 } 932 933 void FrameLoaderClientAndroid::transitionToCommittedForNewPage() { 934 ASSERT(m_frame); 935 936 #ifdef ANDROID_META_SUPPORT 937 // reset metadata settings for the main frame as they are not preserved cross page 938 if (m_frame == m_frame->page()->mainFrame() && m_frame->settings()) 939 m_frame->settings()->resetMetadataSettings(); 940 #endif 941 942 // Save the old WebViewCore before creating a new FrameView. There is one 943 // WebViewCore per page. Each frame, including the main frame and sub frame, 944 // has a 1:1 FrameView and WebFrameView. 945 WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); 946 Retain(webViewCore); 947 948 // Save the old WebFrameView's bounds and apply them to the new WebFrameView 949 RefPtr<WebCore::FrameView> oldFrameView = m_frame->view(); 950 WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (oldFrameView->platformWidget()); 951 IntRect bounds; 952 if (oldWebFrameView) 953 bounds = oldWebFrameView->getBounds(); 954 const float oldZoomFactor = oldFrameView->frame()->textZoomFactor(); 955 m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(), 956 oldFrameView->fixedLayoutSize(), oldFrameView->useFixedLayout()); 957 if (oldZoomFactor != 1.0f && oldZoomFactor != m_frame->textZoomFactor()) { 958 m_frame->setTextZoomFactor(oldZoomFactor); 959 } 960 961 if (oldWebFrameView) { 962 IntRect visBounds = oldWebFrameView->getVisibleBounds(); 963 IntRect windowBounds = oldWebFrameView->getWindowBounds(); 964 // Create a new WebFrameView for the new FrameView 965 WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore); 966 newFrameView->setLocation(bounds.x(), bounds.y()); 967 newFrameView->setSize(bounds.width(), bounds.height()); 968 newFrameView->setVisibleSize(visBounds.width(), visBounds.height()); 969 newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height()); 970 // newFrameView attaches itself to FrameView which Retains the reference, so 971 // call Release for newFrameView 972 Release(newFrameView); 973 } 974 // WebFrameView Retains webViewCore, so call Release for webViewCore 975 Release(webViewCore); 976 977 m_webFrame->transitionToCommitted(m_frame); 978 } 979 980 void FrameLoaderClientAndroid::dispatchDidBecomeFrameset(bool) 981 { 982 } 983 984 bool FrameLoaderClientAndroid::canCachePage() const { 985 return true; 986 } 987 988 void FrameLoaderClientAndroid::download(ResourceHandle* handle, const ResourceRequest&, 989 const ResourceRequest&, const ResourceResponse&) { 990 // Get the C++ side of the load listener and tell it to handle the download 991 handle->getInternal()->m_loader->downloadFile(); 992 } 993 994 WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL& url, const String& name, 995 HTMLFrameOwnerElement* ownerElement, const String& referrer, 996 bool allowsScrolling, int marginWidth, int marginHeight) 997 { 998 Frame* parent = ownerElement->document()->frame(); 999 FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(m_webFrame); 1000 RefPtr<Frame> pFrame = Frame::create(parent->page(), ownerElement, loaderC); 1001 Frame* newFrame = pFrame.get(); 1002 loaderC->setFrame(newFrame); 1003 // Append the subframe to the parent and set the name of the subframe. The name must be set after 1004 // appending the child so that the name becomes unique. 1005 parent->tree()->appendChild(newFrame); 1006 newFrame->tree()->setName(name); 1007 // Create a new FrameView and WebFrameView for the child frame to draw into. 1008 RefPtr<FrameView> frameView = FrameView::create(newFrame); 1009 // Attach the frameView to the newFrame. 1010 newFrame->setView(frameView); 1011 newFrame->init(); 1012 newFrame->selection()->setFocused(true); 1013 ALOGV("::WebCore:: createSubFrame returning %p", newFrame); 1014 1015 // The creation of the frame may have run arbitrary JavaScript that removed it from the page already. 1016 if (!pFrame->page()) 1017 return 0; 1018 1019 parent->loader()->loadURLIntoChildFrame(url, referrer, pFrame.get()); 1020 1021 // onLoad may cuase the frame to be removed from the document. Allow the RefPtr to delete the child frame. 1022 if (!pFrame->tree()->parent()) 1023 return NULL; 1024 1025 return pFrame.release(); 1026 } 1027 1028 // YouTube flash url path starts with /v/ 1029 static const char slash_v_slash[] = { '/', 'v', '/' }; 1030 static const char slash_e_slash[] = { '/', 'e', '/' }; 1031 1032 static bool isValidYouTubeVideo(const String& path) 1033 { 1034 if (!charactersAreAllASCII(path.characters(), path.length())) 1035 return false; 1036 unsigned int len = path.length(); 1037 if (len <= sizeof(slash_v_slash)) // check for more than just /v/ 1038 return false; 1039 CString str = path.lower().utf8(); 1040 const char* data = str.data(); 1041 // Youtube flash url can start with /v/ or /e/ 1042 if (memcmp(data, slash_v_slash, sizeof(slash_v_slash)) != 0) 1043 if (memcmp(data, slash_e_slash, sizeof(slash_e_slash)) != 0) 1044 return false; 1045 // Start after /v/ 1046 for (unsigned int i = sizeof(slash_v_slash); i < len; i++) { 1047 char c = data[i]; 1048 // Check for alpha-numeric characters only. 1049 if (WTF::isASCIIAlphanumeric(c) || c == '_' || c == '-') 1050 continue; 1051 // The url can have more parameters such as &hl=en after the video id. 1052 // Once we start seeing extra parameters we can return true. 1053 return c == '&' && i > sizeof(slash_v_slash); 1054 } 1055 return true; 1056 } 1057 1058 static bool isYouTubeUrl(const KURL& url, const String& mimeType) 1059 { 1060 String host = url.host(); 1061 bool youtube = host.endsWith("youtube.com") 1062 || host.endsWith("youtube-nocookie.com"); 1063 return youtube && isValidYouTubeVideo(url.path()) 1064 && equalIgnoringCase(mimeType, "application/x-shockwave-flash"); 1065 } 1066 1067 static bool isYouTubeInstalled() { 1068 return WebCore::packageNotifier().isPackageInstalled("com.google.android.youtube"); 1069 } 1070 1071 // Use PluginViewBase rather than an Android specific sub class as we do not require any 1072 // Android specific functionality; this just renders a placeholder which will later 1073 // activate the real plugin. 1074 class PluginToggleWidget : public PluginViewBase { 1075 public: 1076 PluginToggleWidget(Frame* parent, const IntSize& size, 1077 HTMLPlugInElement* elem, const KURL& url, 1078 const WTF::Vector<String>& paramNames, 1079 const WTF::Vector<String>& paramValues, const String& mimeType, 1080 bool loadManually) 1081 : PluginViewBase(0) 1082 , m_parent(parent) 1083 , m_size(size) 1084 , m_element(elem) 1085 , m_url(url) 1086 , m_paramNames(paramNames) 1087 , m_paramValues(paramValues) 1088 , m_mimeType(mimeType) 1089 , m_loadManually(loadManually) 1090 { 1091 resize(size); 1092 } 1093 1094 virtual void paint(GraphicsContext* ctx, const IntRect& rect) 1095 { 1096 // Most of this code is copied from PluginView::paintMissingPluginIcon 1097 // with slight modification. 1098 1099 static RefPtr<Image> image; 1100 if (!image) { 1101 image = Image::loadPlatformResource("togglePlugin"); 1102 } 1103 1104 IntRect imageRect(x(), y(), image->width(), image->height()); 1105 1106 int xOffset = (width() - imageRect.width()) >> 1; 1107 int yOffset = (height() - imageRect.height()) >> 1; 1108 1109 imageRect.move(xOffset, yOffset); 1110 1111 if (!rect.intersects(imageRect)) 1112 return; 1113 1114 // FIXME: We need to clip similarly to paintMissingPluginIcon but it is 1115 // way screwed up right now. It has something to do with how we tell 1116 // webkit the scroll position and it causes the placeholder to get 1117 // clipped very badly. http://b/issue?id=2533303 1118 1119 ctx->save(); 1120 ctx->clip(frameRect()); 1121 1122 ctx->setFillColor(Color::white, ColorSpaceDeviceRGB); 1123 ctx->fillRect(frameRect()); 1124 if (frameRect().contains(imageRect)) { 1125 // Leave a 2 pixel padding. 1126 const int pixelWidth = 2; 1127 IntRect innerRect = frameRect(); 1128 innerRect.inflate(-pixelWidth); 1129 // Draw a 2 pixel light gray border. 1130 ctx->setStrokeColor(Color::lightGray, ColorSpaceDeviceRGB); 1131 ctx->strokeRect(innerRect, pixelWidth); 1132 } 1133 1134 // Draw the image in the center 1135 ctx->drawImage(image.get(), ColorSpaceDeviceRGB, imageRect.location()); 1136 ctx->restore(); 1137 } 1138 1139 virtual void handleEvent(Event* event) 1140 { 1141 if (event->type() != eventNames().clickEvent) 1142 return; 1143 1144 Frame* frame = m_parent->page()->mainFrame(); 1145 while (frame) { 1146 RenderView* view = frame->contentRenderer(); 1147 const HashSet<RenderWidget*> widgets = view->widgets(); 1148 HashSet<RenderWidget*>::const_iterator it = widgets.begin(); 1149 HashSet<RenderWidget*>::const_iterator end = widgets.end(); 1150 for (; it != end; ++it) { 1151 Widget* widget = (*it)->widget(); 1152 // PluginWidget is used only with PluginToggleWidget 1153 if (widget && widget->isPluginViewBase()) { 1154 PluginToggleWidget* ptw = 1155 static_cast<PluginToggleWidget*>(widget); 1156 ptw->swapPlugin(*it); 1157 } 1158 } 1159 frame = frame->tree()->traverseNext(); 1160 } 1161 } 1162 1163 void swapPlugin(RenderWidget* renderer) { 1164 typedef FrameLoaderClientAndroid FLCA; 1165 FLCA* client = static_cast<FLCA*>(m_parent->loader()->client()); 1166 client->enableOnDemandPlugins(); 1167 WTF::PassRefPtr<PluginView> prpWidget = 1168 PluginView::create(m_parent.get(), 1169 m_size, 1170 m_element, 1171 m_url, 1172 m_paramNames, 1173 m_paramValues, 1174 m_mimeType, 1175 m_loadManually); 1176 RefPtr<Widget> myProtector(this); 1177 prpWidget->focusPluginElement(); 1178 renderer->setWidget(prpWidget); 1179 } 1180 1181 private: 1182 void invalidateRect(const IntRect& rect) { } 1183 1184 RefPtr<Frame> m_parent; 1185 IntSize m_size; 1186 HTMLPlugInElement* m_element; 1187 KURL m_url; 1188 WTF::Vector<String> m_paramNames; 1189 WTF::Vector<String> m_paramValues; 1190 String m_mimeType; 1191 bool m_loadManually; 1192 }; 1193 1194 WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createPlugin( 1195 const IntSize& size, 1196 HTMLPlugInElement* element, 1197 const KURL& url, 1198 const WTF::Vector<String>& names, 1199 const WTF::Vector<String>& values, 1200 const String& mimeType, 1201 bool loadManually) { 1202 WTF::PassRefPtr<PluginView> prpWidget = 0; 1203 #ifdef ANDROID_PLUGINS 1204 // This is copied from PluginView.cpp. We need to determine if a plugin 1205 // will be found before doing some of the work in PluginView. 1206 String mimeTypeCopy = mimeType; 1207 PluginPackage* plugin = 1208 PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); 1209 if (!plugin && PluginDatabase::installedPlugins()->refresh()) { 1210 mimeTypeCopy = mimeType; 1211 plugin = PluginDatabase::installedPlugins()->findPlugin(url, 1212 mimeTypeCopy); 1213 } 1214 Settings* settings = m_frame->settings(); 1215 // Do the placeholder if plugins are on-demand and there is a plugin for the 1216 // given mime type. 1217 if (settings && settings->arePluginsOnDemand() && plugin && 1218 !m_onDemandPluginsEnabled) { 1219 return adoptRef(new PluginToggleWidget(m_frame, size, element, url, 1220 names, values, mimeType, loadManually)); 1221 } 1222 prpWidget = PluginView::create(m_frame, 1223 size, 1224 element, 1225 url, 1226 names, 1227 values, 1228 mimeType, 1229 loadManually); 1230 // Return the plugin if it was loaded successfully. Otherwise, fallback to 1231 // the youtube placeholder if possible. No need to check prpWidget as 1232 // PluginView::create will create a PluginView for missing plugins. 1233 // Note: this check really only checks if the plugin was found and not if 1234 // the plugin was loaded. 1235 if (prpWidget->status() == PluginStatusLoadedSuccessfully) 1236 return prpWidget; 1237 #endif 1238 // Create an iframe for youtube urls. 1239 if (isYouTubeUrl(url, mimeType) && isYouTubeInstalled()) { 1240 WTF::RefPtr<Frame> frame = createFrame(blankURL(), String(), element, 1241 String(), false, 0, 0); 1242 if (frame) { 1243 // grab everything after /v/ 1244 String videoId = url.path().substring(sizeof(slash_v_slash)); 1245 // Extract just the video id 1246 unsigned videoIdEnd = 0; 1247 for (; videoIdEnd < videoId.length(); videoIdEnd++) { 1248 if (videoId[videoIdEnd] == '&') { 1249 videoId = videoId.left(videoIdEnd); 1250 break; 1251 } 1252 } 1253 AssetManager* am = globalAssetManager(); 1254 Asset* a = am->open("webkit/youtube.html", 1255 Asset::ACCESS_BUFFER); 1256 if (!a) 1257 return NULL; 1258 String s = String((const char*)a->getBuffer(false), a->getLength()); 1259 s = s.replace("VIDEO_ID", videoId); 1260 delete a; 1261 loadDataIntoFrame(frame.get(), 1262 KURL(ParsedURLString, "file:///android_asset/webkit/"), String(), s); 1263 // Transfer ownership to a local refptr. 1264 WTF::RefPtr<Widget> widget(frame->view()); 1265 return widget.release(); 1266 } 1267 } 1268 return prpWidget; 1269 } 1270 1271 void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) { 1272 // Do not redirect data if the Widget is our plugin placeholder. 1273 if (pluginWidget->isPluginView()) { 1274 m_manualLoader = static_cast<PluginView*>(pluginWidget); 1275 } 1276 } 1277 1278 WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, HTMLAppletElement*, 1279 const KURL& baseURL, const WTF::Vector<String>& paramNames, 1280 const WTF::Vector<String>& paramValues) { 1281 // don't support widget yet 1282 notImplemented(); 1283 return 0; 1284 } 1285 1286 void FrameLoaderClientAndroid::didTransferChildFrameToNewDocument(WebCore::Page*) 1287 { 1288 ASSERT(m_frame); 1289 // m_webFrame points to the WebFrame for the page that our frame previously 1290 // belonged to. If the frame now belongs to a new page, we need to update 1291 // m_webFrame to point to the WebFrame for the new page. 1292 Page* newPage = m_frame->page(); 1293 if (newPage != m_webFrame->page()) { 1294 ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(newPage->chrome()->client()); 1295 Release(m_webFrame); 1296 m_webFrame = chromeClient->webFrame(); 1297 Retain(m_webFrame); 1298 } 1299 } 1300 1301 void FrameLoaderClientAndroid::transferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*) 1302 { 1303 notImplemented(); 1304 } 1305 1306 // This function is used by the <OBJECT> element to determine the type of 1307 // the contents and work out if it can render it. 1308 ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages) { 1309 return FrameLoader::defaultObjectContentType(url, mimeType, shouldPreferPlugInsForImages); 1310 } 1311 1312 // This function allows the application to set the correct CSS media 1313 // style. Android could use it to set the media style 'handheld'. Safari 1314 // may use it to set the media style to 'print' when the user wants to print 1315 // a particular web page. 1316 String FrameLoaderClientAndroid::overrideMediaType() const { 1317 notImplemented(); 1318 return String(); 1319 } 1320 1321 // This function is used to re-attach Javascript<->native code classes. 1322 void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) 1323 { 1324 if (world != mainThreadNormalWorld()) 1325 return; 1326 1327 ASSERT(m_frame); 1328 ALOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n", 1329 m_frame, m_frame->document()->url().string().ascii().data()); 1330 m_webFrame->windowObjectCleared(m_frame); 1331 } 1332 1333 void FrameLoaderClientAndroid::documentElementAvailable() { 1334 } 1335 1336 // functions new to Jun-07 tip of tree merge: 1337 ResourceError FrameLoaderClientAndroid::blockedError(ResourceRequest const& request) { 1338 return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String()); 1339 } 1340 1341 // functions new to Nov-07 tip of tree merge: 1342 void FrameLoaderClientAndroid::didPerformFirstNavigation() const { 1343 // This seems to be just a notification that the UI can listen to, to 1344 // know if the user has performed first navigation action. 1345 // It is called from 1346 // void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) 1347 // "Navigation" here means a transition from one page to another that 1348 // ends up in the back/forward list. 1349 } 1350 1351 void FrameLoaderClientAndroid::registerForIconNotification(bool listen) { 1352 if (listen) 1353 WebIconDatabase::RegisterForIconNotification(this); 1354 else 1355 WebIconDatabase::UnregisterForIconNotification(this); 1356 } 1357 1358 // This is the WebIconDatabaseClient method for receiving a notification when we 1359 // get the icon for the page. 1360 void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) { 1361 // This call must happen before dispatchDidReceiveIcon since that method 1362 // may register for icon notifications again since the icon data may have 1363 // to be read from disk. 1364 registerForIconNotification(false); 1365 KURL u(ParsedURLString, pageUrl); 1366 if (equalIgnoringFragmentIdentifier(u, m_frame->document()->url())) { 1367 dispatchDidReceiveIcon(); 1368 } 1369 } 1370 1371 void FrameLoaderClientAndroid::dispatchDidChangeIcons() { 1372 notImplemented(); 1373 } 1374 1375 PassRefPtr<FrameNetworkingContext> FrameLoaderClientAndroid::createNetworkingContext() 1376 { 1377 return FrameNetworkingContextAndroid::create(getFrame()); 1378 } 1379 1380 } 1381