| /* |
| * Copyright 2007, The Android Open Source Project |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #define LOG_TAG "WebCore" |
| |
| #include "config.h" |
| #include "FrameLoaderClientAndroid.h" |
| |
| #include "BackForwardList.h" |
| #include "CachedFrame.h" |
| #include "CachedFramePlatformDataAndroid.h" |
| #include "Chrome.h" |
| #include "ChromeClientAndroid.h" |
| #include "DOMImplementation.h" |
| #include "Document.h" |
| #include "DocumentLoader.h" |
| #include "EditorClientAndroid.h" |
| #include "Frame.h" |
| #include "FrameLoader.h" |
| #include "FrameNetworkingContextAndroid.h" |
| #include "FrameTree.h" |
| #include "FrameView.h" |
| #include "GraphicsContext.h" |
| #include "HTMLFrameOwnerElement.h" |
| #include "HTMLPlugInElement.h" |
| #include "HistoryItem.h" |
| #include "IconDatabase.h" |
| #include "MIMETypeRegistry.h" |
| #include "NotImplemented.h" |
| #include "PackageNotifier.h" |
| #include "Page.h" |
| #include "PlatformBridge.h" |
| #include "PlatformGraphicsContext.h" |
| #include "PlatformString.h" |
| #include "PluginDatabase.h" |
| #include "PluginView.h" |
| #include "PluginViewBase.h" |
| #include "ProgressTracker.h" |
| #include "RenderPart.h" |
| #include "RenderView.h" |
| #include "RenderWidget.h" |
| #include "ResourceError.h" |
| #include "ResourceHandle.h" |
| #include "ResourceHandleInternal.h" |
| #include "SelectionController.h" |
| #include "Settings.h" |
| #include "SkCanvas.h" |
| #include "SkRect.h" |
| #include "TextEncoding.h" |
| #include "WebCoreFrameBridge.h" |
| #include "WebHistory.h" |
| #include "WebIconDatabase.h" |
| #include "WebFrameView.h" |
| #include "WebViewClientError.h" |
| #include "WebViewCore.h" |
| #include "autofill/WebAutofill.h" |
| |
| #include <androidfw/AssetManager.h> |
| #include <wtf/text/CString.h> |
| |
| #define verifiedOk() // Verified that we don't need to implement this. |
| |
| extern android::AssetManager* globalAssetManager(); |
| |
| namespace android { |
| |
| static const int EXTRA_LAYOUT_DELAY = 1000; |
| |
| FrameLoaderClientAndroid::FrameLoaderClientAndroid(WebFrame* webframe) |
| : m_frame(NULL) |
| , m_webFrame(webframe) |
| , m_manualLoader(NULL) |
| , m_hasSentResponseToPlugin(false) |
| , m_onDemandPluginsEnabled(false) |
| , m_didReceiveServerRedirect(false) { |
| Retain(m_webFrame); |
| } |
| |
| FrameLoaderClientAndroid* FrameLoaderClientAndroid::get(const WebCore::Frame* frame) |
| { |
| return static_cast<FrameLoaderClientAndroid*> (frame->loader()->client()); |
| } |
| |
| void FrameLoaderClientAndroid::frameLoaderDestroyed() { |
| registerForIconNotification(false); |
| m_frame = 0; |
| Release(m_webFrame); |
| delete this; |
| } |
| |
| bool FrameLoaderClientAndroid::hasWebView() const { |
| // FIXME, |
| // there is one web view per page, or top frame. |
| // as android's view is created from Java side, it is always there. |
| return true; |
| } |
| |
| void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) { |
| m_onDemandPluginsEnabled = false; |
| // don't use representation |
| verifiedOk(); |
| } |
| |
| void FrameLoaderClientAndroid::forceLayout() { |
| ASSERT(m_frame); |
| m_frame->view()->forceLayout(); |
| // FIXME, should we adjust view size here? |
| m_frame->view()->adjustViewSize(); |
| } |
| |
| void FrameLoaderClientAndroid::forceLayoutForNonHTML() { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::setCopiesOnScroll() { |
| // this is a hint about whether we need to force redraws, or can |
| // just copy the scrolled content. Since we always force a redraw |
| // anyways, we can ignore this call. |
| verifiedOk(); |
| } |
| |
| void FrameLoaderClientAndroid::detachedFromParent2() { |
| // FIXME, ready to detach frame from view |
| } |
| |
| void FrameLoaderClientAndroid::detachedFromParent3() { |
| // FIXME, ready to release view |
| notImplemented(); |
| } |
| |
| // This function is responsible for associating the "id" with a given |
| // subresource load. The following functions that accept an "id" are |
| // called for each subresource, so they should not be dispatched to the m_frame. |
| void FrameLoaderClientAndroid::assignIdentifierToInitialRequest(unsigned long id, |
| DocumentLoader*, const ResourceRequest&) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchWillSendRequest(DocumentLoader*, unsigned long id, |
| ResourceRequest&, const ResourceResponse&) { |
| notImplemented(); |
| } |
| |
| bool FrameLoaderClientAndroid::shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) |
| { |
| notImplemented(); |
| return false; |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, |
| unsigned long id, const AuthenticationChallenge&) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidCancelAuthenticationChallenge(DocumentLoader*, |
| unsigned long id, const AuthenticationChallenge&) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveResponse(DocumentLoader*, |
| unsigned long id, const ResourceResponse&) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveContentLength(DocumentLoader*, |
| unsigned long id, int lengthReceived) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFinishLoading(DocumentLoader*, |
| unsigned long id) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFailLoading(DocumentLoader* docLoader, |
| unsigned long id, const ResourceError&) { |
| notImplemented(); |
| } |
| |
| bool FrameLoaderClientAndroid::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, |
| const ResourceRequest&, const ResourceResponse&, int length) { |
| notImplemented(); |
| return false; |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidHandleOnloadEvents() { |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveServerRedirectForProvisionalLoad() { |
| ASSERT(!m_didReceiveServerRedirect); |
| m_didReceiveServerRedirect = true; |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidCancelClientRedirect() { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchWillPerformClientRedirect(const KURL&, |
| double interval, double fireDate) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidChangeLocationWithinPage() { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidPushStateWithinPage() |
| { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReplaceStateWithinPage() |
| { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidPopStateWithinPage() |
| { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchWillClose() { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveIcon() { |
| ASSERT(m_frame); |
| if (m_frame->tree() && m_frame->tree()->parent()) |
| return; |
| WTF::String url(m_frame->document()->url().string()); |
| // Try to obtain the icon image. |
| // FIXME: This method should not be used from outside WebCore and will be removed. |
| // http://trac.webkit.org/changeset/81484 |
| WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16)); |
| // If the request fails, try the original request url. |
| if (!icon) { |
| DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); |
| KURL originalURL = docLoader->originalRequest().url(); |
| // FIXME: This method should not be used from outside WebCore and will be removed. |
| // http://trac.webkit.org/changeset/81484 |
| icon = WebCore::iconDatabase().synchronousIconForPageURL(originalURL, WebCore::IntSize(16, 16)); |
| } |
| // There is a bug in webkit where cancelling an icon load is treated as a |
| // failure. When this is fixed, we can ASSERT again that we have an icon. |
| if (icon) { |
| ALOGV("Received icon (%p) for %s", icon, |
| url.utf8().data()); |
| m_webFrame->didReceiveIcon(icon); |
| } else { |
| ALOGV("Icon data for %s unavailable, registering for notification...", |
| url.utf8().data()); |
| registerForIconNotification(); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) { |
| ASSERT(m_frame); |
| // Do not report sub frame touch icons |
| if (m_frame->tree() && m_frame->tree()->parent()) |
| return; |
| m_webFrame->didReceiveTouchIconURL(url, precomposed); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidStartProvisionalLoad() { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidReceiveTitle(const StringWithDirection& title) { |
| ASSERT(m_frame); |
| // Used to check for FrameLoadTypeStandard but we only want to send the title for |
| // the top frame and not sub-frames. |
| // FIXME: Use direction of title. |
| if (!m_frame->tree() || !m_frame->tree()->parent()) { |
| m_webFrame->setTitle(title.string()); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidCommitLoad() { |
| #if ENABLE(WEB_AUTOFILL) |
| if (m_frame == m_frame->page()->mainFrame()) { |
| EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(m_frame->page()->editorClient()); |
| WebAutofill* autoFill = editorC->getAutofill(); |
| autoFill->reset(); |
| } |
| #endif |
| verifiedOk(); |
| } |
| |
| static void loadDataIntoFrame(Frame* frame, KURL baseUrl, const String& url, |
| const String& data) { |
| if (baseUrl.isEmpty()) { |
| baseUrl = blankURL(); |
| } |
| ResourceRequest request(baseUrl); |
| CString cstr = data.utf8(); |
| RefPtr<WebCore::SharedBuffer> buf = WebCore::SharedBuffer::create(cstr.data(), cstr.length()); |
| SubstituteData subData(buf, String("text/html"), String("utf-8"), |
| KURL(KURL(), url)); |
| frame->loader()->load(request, subData, false); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) { |
| ASSERT(m_frame); |
| // Ignore ErrorInterrupted since it is due to a policy interruption. This |
| // is caused by a decision to download the main resource rather than |
| // display it. |
| if (error.errorCode() == InternalErrorInterrupted |
| || error.errorCode() == InternalErrorCancelled) { |
| // If we decided to download the main resource or if the user cancelled |
| // it, make sure we report that the load is done. |
| didFinishLoad(); |
| return; |
| } |
| |
| AssetManager* am = globalAssetManager(); |
| |
| // Check to see if the error code was not generated internally |
| WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain; |
| if ((error.errorCode() == ErrorFile || |
| error.errorCode() == ErrorFileNotFound) && |
| (!error.localizedDescription().isEmpty())) { |
| id = WebCore::PlatformBridge::LoadError; |
| } |
| String filename = m_webFrame->getRawResourceFilename(id); |
| if (filename.isEmpty()) |
| return; |
| |
| // Grab the error page from the asset manager |
| Asset* a = am->openNonAsset( |
| filename.utf8().data(), Asset::ACCESS_BUFFER); |
| if (!a) |
| return; |
| |
| // Take the failing url and encode html entities so javascript urls are not |
| // executed. |
| CString failingUrl = error.failingURL().utf8(); |
| WTF::Vector<char> url; |
| int len = failingUrl.length(); |
| const char* data = failingUrl.data(); |
| for (int i = 0; i < len; i++) { |
| char c = data[i]; |
| if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') |
| || (c >= '0' && c <= '9')) |
| url.append(c); |
| else { |
| char buf[16]; |
| int res = sprintf(buf, "&#%d;", c); |
| buf[res] = 0; |
| url.append(buf, res); |
| } |
| } |
| // Vector sets up its data buffer lazilly, so if failingUrl is the empty |
| // string, the data buffer will be null. This will result in sanitizedUrl |
| // being null, and the string substitution below will be a no-op. |
| // FIXME: Ideally we'd always have a non-empty URL, or at least improve the |
| // wording of the error page in this case. See http://b/5293782. |
| String sanitizedUrl = url.data() ? String(url.data(), url.size()) : ""; |
| |
| // Replace all occurances of %s with the failing url. |
| String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength()); |
| s = s.replace("%s", sanitizedUrl); |
| |
| // Replace all occurances of %e with the error text |
| s = s.replace("%e", error.localizedDescription()); |
| |
| // Create the request and the substitute data and tell the FrameLoader to |
| // load with the replacement data. |
| // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for |
| // invalidate URL string. |
| loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s); |
| |
| // Delete the asset. |
| delete a; |
| |
| // Report that the load is finished, since it failed. |
| didFinishLoad(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFailLoad(const ResourceError&) { |
| // called when page is completed with error |
| didFinishLoad(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFinishDocumentLoad() { |
| // called when finishedParsing |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFinishLoad() { |
| didFinishLoad(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFirstLayout() { |
| ASSERT(m_frame); |
| // set EXTRA_LAYOUT_DELAY if the loader is not completed yet |
| if (!m_frame->loader()->isComplete()) |
| m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY); |
| // we need to do this here instead of dispatchDidFirstVisuallyNonEmptyLayout |
| // so that about:blank will update the screen. |
| if (!m_frame->tree()->parent()) { |
| // Only need to notify Java side for the top frame |
| WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view()); |
| if (core) |
| core->didFirstLayout(); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidFirstVisuallyNonEmptyLayout() |
| { |
| notImplemented(); |
| } |
| |
| Frame* FrameLoaderClientAndroid::dispatchCreatePage(const NavigationAction&) { |
| ASSERT(m_frame); |
| #ifdef ANDROID_MULTIPLE_WINDOWS |
| if (m_frame->settings() && m_frame->settings()->supportMultipleWindows()) |
| // Always a user gesture since window.open maps to |
| // ChromeClientAndroid::createWindow |
| return m_webFrame->createWindow(false, true); |
| else |
| #endif |
| // If the client doesn't support multiple windows, just replace the |
| // current frame's contents. |
| return m_frame; |
| } |
| |
| void FrameLoaderClientAndroid::dispatchShow() { |
| ASSERT(m_frame); |
| m_frame->view()->invalidate(); |
| } |
| |
| |
| static bool TreatAsAttachment(const String& content_disposition) { |
| // Some broken sites just send |
| // Content-Disposition: ; filename="file" |
| // screen those out here. |
| if (content_disposition.startsWith(";")) |
| return false; |
| |
| if (content_disposition.startsWith("inline", false)) |
| return false; |
| |
| // Some broken sites just send |
| // Content-Disposition: filename="file" |
| // without a disposition token... screen those out. |
| if (content_disposition.startsWith("filename", false)) |
| return false; |
| |
| // Also in use is Content-Disposition: name="file" |
| if (content_disposition.startsWith("name", false)) |
| return false; |
| |
| // We have a content-disposition of "attachment" or unknown. |
| // RFC 2183, section 2.8 says that an unknown disposition |
| // value should be treated as "attachment" |
| return true; |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDecidePolicyForResponse(FramePolicyFunction func, |
| const ResourceResponse& response, const ResourceRequest& request) { |
| ASSERT(m_frame); |
| ASSERT(func); |
| if (!func) |
| return; |
| |
| PolicyChecker* policy = m_frame->loader()->policyChecker(); |
| |
| if (request.isNull()) { |
| (policy->*func)(PolicyIgnore); |
| return; |
| } |
| // Default to Use (display internally). |
| PolicyAction action = PolicyUse; |
| // Check if we should Download instead. |
| const String& content_disposition = response.httpHeaderField("Content-Disposition"); |
| if (!content_disposition.isEmpty() && |
| TreatAsAttachment(content_disposition)) { |
| // Server wants to override our normal policy. |
| // Check to see if we are a sub frame (main frame has no owner element) |
| if (m_frame->ownerElement() != 0) |
| action = PolicyIgnore; |
| else |
| action = PolicyDownload; |
| (policy->*func)(action); |
| return; |
| } |
| |
| // Ask if it can be handled internally. |
| if (!canShowMIMEType(response.mimeType())) { |
| // Check to see if we are a sub frame (main frame has no owner element) |
| if (m_frame->ownerElement() != 0) |
| action = PolicyIgnore; |
| else |
| action = PolicyDownload; |
| (policy->*func)(action); |
| return; |
| } |
| // A status code of 204 indicates no content change. Ignore the result. |
| WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); |
| if (docLoader->response().httpStatusCode() == 204) |
| action = PolicyIgnore; |
| (policy->*func)(action); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func, |
| const NavigationAction& action, const ResourceRequest& request, |
| PassRefPtr<FormState> formState, const String& frameName) { |
| ASSERT(m_frame); |
| ASSERT(func); |
| if (!func) |
| return; |
| |
| if (request.isNull()) { |
| (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); |
| return; |
| } |
| |
| if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted) |
| m_frame->loader()->resetMultipleFormSubmissionProtection(); |
| |
| // If we get to this point it means that a link has a target that was not |
| // found by the frame tree. Instead of creating a new frame, return the |
| // current frame in dispatchCreatePage. |
| if (canHandleRequest(request)) |
| (m_frame->loader()->policyChecker()->*func)(PolicyUse); |
| else |
| (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); |
| } |
| |
| void FrameLoaderClientAndroid::cancelPolicyCheck() { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchUnableToImplementPolicy(const ResourceError&) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction(FramePolicyFunction func, |
| const NavigationAction& action, const ResourceRequest& request, |
| PassRefPtr<FormState> formState) { |
| ASSERT(m_frame); |
| ASSERT(func); |
| if (!func) |
| return; |
| if (request.isNull()) { |
| (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); |
| return; |
| } |
| |
| // Reset multiple form submission protection. If this is a resubmission, we check with the |
| // user and reset the protection if they choose to resubmit the form (see WebCoreFrameBridge.cpp) |
| if (action.type() == NavigationTypeFormSubmitted) |
| m_frame->loader()->resetMultipleFormSubmissionProtection(); |
| |
| if (action.type() == NavigationTypeFormResubmitted) { |
| m_webFrame->decidePolicyForFormResubmission(func); |
| return; |
| } else |
| (m_frame->loader()->policyChecker()->*func)(PolicyUse); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) { |
| ASSERT(m_frame); |
| ASSERT(func); |
| (m_frame->loader()->policyChecker()->*func)(PolicyUse); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchWillSendSubmitEvent(HTMLFormElement* form) |
| { |
| if (m_webFrame->shouldSaveFormData()) |
| m_webFrame->saveFormData(form); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) { |
| ASSERT(m_frame); |
| if (m_manualLoader) { |
| m_manualLoader->didFail(error); |
| m_manualLoader = NULL; |
| m_hasSentResponseToPlugin = false; |
| } else { |
| if (!error.isNull() && error.errorCode() >= InternalErrorLast && error.errorCode() != ERROR_OK) |
| m_webFrame->reportError(error.errorCode(), |
| error.localizedDescription(), error.failingURL()); |
| } |
| } |
| |
| // This function is called right before the progress is updated. |
| void FrameLoaderClientAndroid::willChangeEstimatedProgress() { |
| verifiedOk(); |
| } |
| |
| // This function is called after the progress has been updated. The bad part |
| // about this is that when a page is completed, this function is called after |
| // the progress has been reset to 0. |
| void FrameLoaderClientAndroid::didChangeEstimatedProgress() { |
| verifiedOk(); |
| } |
| |
| // This will give us the initial estimate when the page first starts to load. |
| void FrameLoaderClientAndroid::postProgressStartedNotification() { |
| ASSERT(m_frame); |
| if (m_frame->page()) |
| m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress()); |
| } |
| |
| // This will give us any updated progress including the final progress. |
| void FrameLoaderClientAndroid::postProgressEstimateChangedNotification() { |
| ASSERT(m_frame); |
| if (m_frame->page()) |
| m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress()); |
| } |
| |
| // This is just a notification that the progress has finished. Don't call |
| // setProgress(1) because postProgressEstimateChangedNotification will do so. |
| void FrameLoaderClientAndroid::postProgressFinishedNotification() { |
| WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view()); |
| if (!m_frame->tree()->parent()) { |
| // only need to notify Java for the top frame |
| core->notifyProgressFinished(); |
| } |
| // notify plugins that the frame has loaded |
| core->notifyPluginsOnFrameLoad(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::setMainFrameDocumentReady(bool) { |
| // this is only interesting once we provide an external API for the DOM |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::startDownload(const ResourceRequest&) { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::willChangeTitle(DocumentLoader*) { |
| verifiedOk(); |
| } |
| |
| void FrameLoaderClientAndroid::didChangeTitle(DocumentLoader* loader) { |
| verifiedOk(); |
| } |
| |
| void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader) { |
| // Telling the frame we received some data and passing 0 as the data is our |
| // way to get work done that is normally done when the first bit of data is |
| // received, even for the case of a document with no data (like about:blank) |
| if (!m_manualLoader) { |
| committedLoad(docLoader, 0, 0); |
| return; |
| } |
| |
| m_manualLoader->didFinishLoading(); |
| m_manualLoader = NULL; |
| m_hasSentResponseToPlugin = false; |
| } |
| |
| void FrameLoaderClientAndroid::updateGlobalHistory() { |
| ASSERT(m_frame); |
| |
| DocumentLoader* docLoader = m_frame->loader()->documentLoader(); |
| ASSERT(docLoader); |
| |
| // Code copied from FrameLoader.cpp:createHistoryItem |
| // Only add this URL to the database if it is a valid page |
| if (docLoader->unreachableURL().isEmpty() |
| && docLoader->response().httpStatusCode() < 400) { |
| m_webFrame->updateVisitedHistory(docLoader->urlForHistory(), false); |
| if (!docLoader->serverRedirectSourceForHistory().isNull()) |
| m_webFrame->updateVisitedHistory(KURL(ParsedURLString, docLoader->serverRedirectDestinationForHistory()), false); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::updateGlobalHistoryRedirectLinks() { |
| // Note, do we need to do anything where there is no HistoryItem? If we call |
| // updateGlobalHistory(), we will add bunch of "data:xxx" urls for gmail.com |
| // which is not what we want. Opt to do nothing now. |
| } |
| |
| bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const { |
| // hmmm, seems like we might do a more thoughtful check |
| ASSERT(m_frame); |
| return item != NULL; |
| } |
| |
| bool FrameLoaderClientAndroid::shouldStopLoadingForHistoryItem(HistoryItem* item) const |
| { |
| return true; |
| } |
| |
| void FrameLoaderClientAndroid::didDisplayInsecureContent() |
| { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::didRunInsecureContent(SecurityOrigin*, const KURL&) |
| { |
| notImplemented(); |
| } |
| |
| void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) { |
| if (!m_manualLoader) |
| loader->commitData(data, length); |
| |
| // commit data may have created a manual plugin loader |
| if (m_manualLoader) { |
| if (!m_hasSentResponseToPlugin) { |
| m_manualLoader->didReceiveResponse(loader->response()); |
| // Failure could cause the main document to have an error causing |
| // the manual loader to be reset. |
| if (!m_manualLoader) |
| return; |
| m_hasSentResponseToPlugin = true; |
| } |
| m_manualLoader->didReceiveData(data, length); |
| } |
| } |
| |
| ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) { |
| return ResourceError(String(), InternalErrorCancelled, request.url(), String()); |
| } |
| |
| ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) { |
| return ResourceError(String(), InternalErrorCannotShowUrl, request.url(), String()); |
| } |
| |
| ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) { |
| return ResourceError(String(), InternalErrorInterrupted, request.url(), String()); |
| } |
| |
| ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) { |
| return ResourceError(String(), InternalErrorCannotShowMimeType, request.url(), String()); |
| } |
| |
| ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) { |
| return ResourceError(String(), InternalErrorFileDoesNotExist, request.url(), String()); |
| } |
| |
| ResourceError FrameLoaderClientAndroid::pluginWillHandleLoadError(const ResourceResponse& request) { |
| return ResourceError(String(), InternalErrorPluginWillHandleLoadError, request.url(), String()); |
| } |
| |
| bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) { |
| notImplemented(); |
| return false; |
| } |
| |
| bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const { |
| // This is called by WebCore to determine if this load can be handled by the |
| // WebView. In general, we delegate to the WebFrame, which may ask the |
| // embedding application whether it wishes to hijack the load. However, we |
| // don't allow this if the load is ... |
| // - An intrapage navigation |
| // - An iframe with a HTTP or HTTPS scheme URL |
| bool canHandle = WebCore::equalIgnoringFragmentIdentifier(request.url(), m_frame->document()->url()) || |
| (request.url().protocol().startsWith("http", false) && m_frame->tree() && m_frame->tree()->parent()) || |
| m_webFrame->canHandleRequest(request); |
| |
| // If this is a server-side redirect and the WebView will handle loading it, |
| // notify the WebFrame, which may notify the embedding application that |
| // we're loading a new URL. |
| if (m_didReceiveServerRedirect && canHandle) |
| m_webFrame->loadStarted(m_frame); |
| m_didReceiveServerRedirect = false; |
| |
| return canHandle; |
| } |
| |
| bool FrameLoaderClientAndroid::canShowMIMEType(const String& mimeType) const { |
| // FIXME: This looks like it has to do with whether or not a type can be |
| // shown "internally" (i.e. inside the browser) regardless of whether |
| // or not the browser is doing the rendering, e.g. a full page plugin. |
| if (MIMETypeRegistry::isSupportedImageResourceMIMEType(mimeType) || |
| MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) || |
| MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || |
| (m_frame && m_frame->settings() |
| && m_frame->settings()->arePluginsEnabled() |
| && PluginDatabase::installedPlugins()->isMIMETypeRegistered( |
| mimeType)) || |
| (DOMImplementation::isTextMIMEType(mimeType) && |
| !mimeType.startsWith("text/vnd")) || |
| DOMImplementation::isXMLMIMEType(mimeType)) |
| return true; |
| return false; |
| } |
| |
| bool FrameLoaderClientAndroid::canShowMIMETypeAsHTML(const String& mimeType) const { |
| return false; |
| } |
| |
| bool FrameLoaderClientAndroid::representationExistsForURLScheme(const String&) const { |
| // don't use representation |
| verifiedOk(); |
| return false; |
| } |
| |
| String FrameLoaderClientAndroid::generatedMIMETypeForURLScheme(const String& URLScheme) const { |
| // FIXME, copy from Apple's port |
| String mimetype("x-apple-web-kit/"); |
| mimetype.append(URLScheme.lower()); |
| return mimetype; |
| } |
| |
| void FrameLoaderClientAndroid::frameLoadCompleted() { |
| // copied from Apple port, without this back with sub-frame will trigger ASSERT |
| ASSERT(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) { |
| ASSERT(m_frame); |
| ASSERT(item); |
| // store the current scale (only) for the top frame |
| if (!m_frame->tree()->parent()) { |
| // We should have added a bridge when the child item was added to its |
| // parent. |
| AndroidWebHistoryBridge* bridge = item->bridge(); |
| ASSERT(bridge); |
| WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); |
| bridge->setScale(webViewCore->scale()); |
| bridge->setTextWrapScale(webViewCore->textWrapScale()); |
| } |
| |
| WebCore::notifyHistoryItemChanged(item); |
| } |
| |
| void FrameLoaderClientAndroid::restoreViewState() { |
| WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); |
| HistoryItem* item = m_frame->loader()->history()->currentItem(); |
| AndroidWebHistoryBridge* bridge = item->bridge(); |
| // restore the scale (only) for the top frame |
| if (!m_frame->tree()->parent()) { |
| webViewCore->restoreScale(bridge->scale(), bridge->textWrapScale()); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidAddBackForwardItem(HistoryItem* item) const { |
| ASSERT(m_frame); |
| m_webFrame->addHistoryItem(item); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidRemoveBackForwardItem(HistoryItem* item) const { |
| ASSERT(m_frame); |
| m_webFrame->removeHistoryItem(0); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidChangeBackForwardIndex() const { |
| ASSERT(m_frame); |
| BackForwardList* list = m_frame->page()->backForwardList(); |
| ASSERT(list); |
| m_webFrame->updateHistoryIndex(list->backListCount()); |
| } |
| |
| void FrameLoaderClientAndroid::provisionalLoadStarted() { |
| ASSERT(m_frame); |
| m_webFrame->loadStarted(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::didFinishLoad() { |
| ASSERT(m_frame); |
| m_frame->document()->setExtraLayoutDelay(0); |
| m_webFrame->didFinishLoad(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::prepareForDataSourceReplacement() { |
| verifiedOk(); |
| } |
| |
| PassRefPtr<DocumentLoader> FrameLoaderClientAndroid::createDocumentLoader( |
| const ResourceRequest& request, const SubstituteData& data) { |
| RefPtr<DocumentLoader> loader = DocumentLoader::create(request, data); |
| return loader.release(); |
| } |
| |
| void FrameLoaderClientAndroid::setTitle(const StringWithDirection& title, const KURL& url) { |
| // Not needed. dispatchDidReceiveTitle is called immediately after this. |
| // url is used to update the Apple port history items. |
| verifiedOk(); |
| } |
| |
| String FrameLoaderClientAndroid::userAgent(const KURL& u) { |
| return m_webFrame->userAgentForURL(&u); |
| } |
| |
| void FrameLoaderClientAndroid::savePlatformDataToCachedFrame(WebCore::CachedFrame* cachedFrame) { |
| CachedFramePlatformDataAndroid* platformData = new CachedFramePlatformDataAndroid(m_frame->settings()); |
| cachedFrame->setCachedFramePlatformData(platformData); |
| } |
| |
| void FrameLoaderClientAndroid::transitionToCommittedFromCachedFrame(WebCore::CachedFrame* cachedFrame) { |
| CachedFramePlatformDataAndroid* platformData = reinterpret_cast<CachedFramePlatformDataAndroid*>(cachedFrame->cachedFramePlatformData()); |
| #ifdef ANDROID_META_SUPPORT |
| platformData->restoreMetadata(m_frame->settings()); |
| #endif |
| |
| #if ENABLE(ANDROID_OVERFLOW_SCROLL) |
| #else |
| WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); |
| |
| webViewCore->clearContent(); |
| #endif |
| |
| m_webFrame->transitionToCommitted(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::transitionToCommittedForNewPage() { |
| ASSERT(m_frame); |
| |
| #ifdef ANDROID_META_SUPPORT |
| // reset metadata settings for the main frame as they are not preserved cross page |
| if (m_frame == m_frame->page()->mainFrame() && m_frame->settings()) |
| m_frame->settings()->resetMetadataSettings(); |
| #endif |
| |
| // Save the old WebViewCore before creating a new FrameView. There is one |
| // WebViewCore per page. Each frame, including the main frame and sub frame, |
| // has a 1:1 FrameView and WebFrameView. |
| WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); |
| Retain(webViewCore); |
| |
| // Save the old WebFrameView's bounds and apply them to the new WebFrameView |
| RefPtr<WebCore::FrameView> oldFrameView = m_frame->view(); |
| WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (oldFrameView->platformWidget()); |
| IntRect bounds; |
| if (oldWebFrameView) |
| bounds = oldWebFrameView->getBounds(); |
| const float oldZoomFactor = oldFrameView->frame()->textZoomFactor(); |
| m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(), |
| oldFrameView->fixedLayoutSize(), oldFrameView->useFixedLayout()); |
| if (oldZoomFactor != 1.0f && oldZoomFactor != m_frame->textZoomFactor()) { |
| m_frame->setTextZoomFactor(oldZoomFactor); |
| } |
| |
| if (oldWebFrameView) { |
| IntRect visBounds = oldWebFrameView->getVisibleBounds(); |
| IntRect windowBounds = oldWebFrameView->getWindowBounds(); |
| // Create a new WebFrameView for the new FrameView |
| WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore); |
| newFrameView->setLocation(bounds.x(), bounds.y()); |
| newFrameView->setSize(bounds.width(), bounds.height()); |
| newFrameView->setVisibleSize(visBounds.width(), visBounds.height()); |
| newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height()); |
| // newFrameView attaches itself to FrameView which Retains the reference, so |
| // call Release for newFrameView |
| Release(newFrameView); |
| } |
| // WebFrameView Retains webViewCore, so call Release for webViewCore |
| Release(webViewCore); |
| |
| m_webFrame->transitionToCommitted(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidBecomeFrameset(bool) |
| { |
| } |
| |
| bool FrameLoaderClientAndroid::canCachePage() const { |
| return true; |
| } |
| |
| void FrameLoaderClientAndroid::download(ResourceHandle* handle, const ResourceRequest&, |
| const ResourceRequest&, const ResourceResponse&) { |
| // Get the C++ side of the load listener and tell it to handle the download |
| handle->getInternal()->m_loader->downloadFile(); |
| } |
| |
| WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL& url, const String& name, |
| HTMLFrameOwnerElement* ownerElement, const String& referrer, |
| bool allowsScrolling, int marginWidth, int marginHeight) |
| { |
| Frame* parent = ownerElement->document()->frame(); |
| FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(m_webFrame); |
| RefPtr<Frame> pFrame = Frame::create(parent->page(), ownerElement, loaderC); |
| Frame* newFrame = pFrame.get(); |
| loaderC->setFrame(newFrame); |
| // Append the subframe to the parent and set the name of the subframe. The name must be set after |
| // appending the child so that the name becomes unique. |
| parent->tree()->appendChild(newFrame); |
| newFrame->tree()->setName(name); |
| // Create a new FrameView and WebFrameView for the child frame to draw into. |
| RefPtr<FrameView> frameView = FrameView::create(newFrame); |
| // Attach the frameView to the newFrame. |
| newFrame->setView(frameView); |
| newFrame->init(); |
| newFrame->selection()->setFocused(true); |
| ALOGV("::WebCore:: createSubFrame returning %p", newFrame); |
| |
| // The creation of the frame may have run arbitrary JavaScript that removed it from the page already. |
| if (!pFrame->page()) |
| return 0; |
| |
| parent->loader()->loadURLIntoChildFrame(url, referrer, pFrame.get()); |
| |
| // onLoad may cuase the frame to be removed from the document. Allow the RefPtr to delete the child frame. |
| if (!pFrame->tree()->parent()) |
| return NULL; |
| |
| return pFrame.release(); |
| } |
| |
| // YouTube flash url path starts with /v/ |
| static const char slash_v_slash[] = { '/', 'v', '/' }; |
| static const char slash_e_slash[] = { '/', 'e', '/' }; |
| |
| static bool isValidYouTubeVideo(const String& path) |
| { |
| if (!charactersAreAllASCII(path.characters(), path.length())) |
| return false; |
| unsigned int len = path.length(); |
| if (len <= sizeof(slash_v_slash)) // check for more than just /v/ |
| return false; |
| CString str = path.lower().utf8(); |
| const char* data = str.data(); |
| // Youtube flash url can start with /v/ or /e/ |
| if (memcmp(data, slash_v_slash, sizeof(slash_v_slash)) != 0) |
| if (memcmp(data, slash_e_slash, sizeof(slash_e_slash)) != 0) |
| return false; |
| // Start after /v/ |
| for (unsigned int i = sizeof(slash_v_slash); i < len; i++) { |
| char c = data[i]; |
| // Check for alpha-numeric characters only. |
| if (WTF::isASCIIAlphanumeric(c) || c == '_' || c == '-') |
| continue; |
| // The url can have more parameters such as &hl=en after the video id. |
| // Once we start seeing extra parameters we can return true. |
| return c == '&' && i > sizeof(slash_v_slash); |
| } |
| return true; |
| } |
| |
| static bool isYouTubeUrl(const KURL& url, const String& mimeType) |
| { |
| String host = url.host(); |
| bool youtube = host.endsWith("youtube.com") |
| || host.endsWith("youtube-nocookie.com"); |
| return youtube && isValidYouTubeVideo(url.path()) |
| && equalIgnoringCase(mimeType, "application/x-shockwave-flash"); |
| } |
| |
| static bool isYouTubeInstalled() { |
| return WebCore::packageNotifier().isPackageInstalled("com.google.android.youtube"); |
| } |
| |
| // Use PluginViewBase rather than an Android specific sub class as we do not require any |
| // Android specific functionality; this just renders a placeholder which will later |
| // activate the real plugin. |
| class PluginToggleWidget : public PluginViewBase { |
| public: |
| PluginToggleWidget(Frame* parent, const IntSize& size, |
| HTMLPlugInElement* elem, const KURL& url, |
| const WTF::Vector<String>& paramNames, |
| const WTF::Vector<String>& paramValues, const String& mimeType, |
| bool loadManually) |
| : PluginViewBase(0) |
| , m_parent(parent) |
| , m_size(size) |
| , m_element(elem) |
| , m_url(url) |
| , m_paramNames(paramNames) |
| , m_paramValues(paramValues) |
| , m_mimeType(mimeType) |
| , m_loadManually(loadManually) |
| { |
| resize(size); |
| } |
| |
| virtual void paint(GraphicsContext* ctx, const IntRect& rect) |
| { |
| // Most of this code is copied from PluginView::paintMissingPluginIcon |
| // with slight modification. |
| |
| static RefPtr<Image> image; |
| if (!image) { |
| image = Image::loadPlatformResource("togglePlugin"); |
| } |
| |
| IntRect imageRect(x(), y(), image->width(), image->height()); |
| |
| int xOffset = (width() - imageRect.width()) >> 1; |
| int yOffset = (height() - imageRect.height()) >> 1; |
| |
| imageRect.move(xOffset, yOffset); |
| |
| if (!rect.intersects(imageRect)) |
| return; |
| |
| // FIXME: We need to clip similarly to paintMissingPluginIcon but it is |
| // way screwed up right now. It has something to do with how we tell |
| // webkit the scroll position and it causes the placeholder to get |
| // clipped very badly. http://b/issue?id=2533303 |
| |
| ctx->save(); |
| ctx->clip(frameRect()); |
| |
| ctx->setFillColor(Color::white, ColorSpaceDeviceRGB); |
| ctx->fillRect(frameRect()); |
| if (frameRect().contains(imageRect)) { |
| // Leave a 2 pixel padding. |
| const int pixelWidth = 2; |
| IntRect innerRect = frameRect(); |
| innerRect.inflate(-pixelWidth); |
| // Draw a 2 pixel light gray border. |
| ctx->setStrokeColor(Color::lightGray, ColorSpaceDeviceRGB); |
| ctx->strokeRect(innerRect, pixelWidth); |
| } |
| |
| // Draw the image in the center |
| ctx->drawImage(image.get(), ColorSpaceDeviceRGB, imageRect.location()); |
| ctx->restore(); |
| } |
| |
| virtual void handleEvent(Event* event) |
| { |
| if (event->type() != eventNames().clickEvent) |
| return; |
| |
| Frame* frame = m_parent->page()->mainFrame(); |
| while (frame) { |
| RenderView* view = frame->contentRenderer(); |
| const HashSet<RenderWidget*> widgets = view->widgets(); |
| HashSet<RenderWidget*>::const_iterator it = widgets.begin(); |
| HashSet<RenderWidget*>::const_iterator end = widgets.end(); |
| for (; it != end; ++it) { |
| Widget* widget = (*it)->widget(); |
| // PluginWidget is used only with PluginToggleWidget |
| if (widget && widget->isPluginViewBase()) { |
| PluginToggleWidget* ptw = |
| static_cast<PluginToggleWidget*>(widget); |
| ptw->swapPlugin(*it); |
| } |
| } |
| frame = frame->tree()->traverseNext(); |
| } |
| } |
| |
| void swapPlugin(RenderWidget* renderer) { |
| typedef FrameLoaderClientAndroid FLCA; |
| FLCA* client = static_cast<FLCA*>(m_parent->loader()->client()); |
| client->enableOnDemandPlugins(); |
| WTF::PassRefPtr<PluginView> prpWidget = |
| PluginView::create(m_parent.get(), |
| m_size, |
| m_element, |
| m_url, |
| m_paramNames, |
| m_paramValues, |
| m_mimeType, |
| m_loadManually); |
| RefPtr<Widget> myProtector(this); |
| prpWidget->focusPluginElement(); |
| renderer->setWidget(prpWidget); |
| } |
| |
| private: |
| void invalidateRect(const IntRect& rect) { } |
| |
| RefPtr<Frame> m_parent; |
| IntSize m_size; |
| HTMLPlugInElement* m_element; |
| KURL m_url; |
| WTF::Vector<String> m_paramNames; |
| WTF::Vector<String> m_paramValues; |
| String m_mimeType; |
| bool m_loadManually; |
| }; |
| |
| WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createPlugin( |
| const IntSize& size, |
| HTMLPlugInElement* element, |
| const KURL& url, |
| const WTF::Vector<String>& names, |
| const WTF::Vector<String>& values, |
| const String& mimeType, |
| bool loadManually) { |
| WTF::PassRefPtr<PluginView> prpWidget = 0; |
| #ifdef ANDROID_PLUGINS |
| // This is copied from PluginView.cpp. We need to determine if a plugin |
| // will be found before doing some of the work in PluginView. |
| String mimeTypeCopy = mimeType; |
| PluginPackage* plugin = |
| PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); |
| if (!plugin && PluginDatabase::installedPlugins()->refresh()) { |
| mimeTypeCopy = mimeType; |
| plugin = PluginDatabase::installedPlugins()->findPlugin(url, |
| mimeTypeCopy); |
| } |
| Settings* settings = m_frame->settings(); |
| // Do the placeholder if plugins are on-demand and there is a plugin for the |
| // given mime type. |
| if (settings && settings->arePluginsOnDemand() && plugin && |
| !m_onDemandPluginsEnabled) { |
| return adoptRef(new PluginToggleWidget(m_frame, size, element, url, |
| names, values, mimeType, loadManually)); |
| } |
| prpWidget = PluginView::create(m_frame, |
| size, |
| element, |
| url, |
| names, |
| values, |
| mimeType, |
| loadManually); |
| // Return the plugin if it was loaded successfully. Otherwise, fallback to |
| // the youtube placeholder if possible. No need to check prpWidget as |
| // PluginView::create will create a PluginView for missing plugins. |
| // Note: this check really only checks if the plugin was found and not if |
| // the plugin was loaded. |
| if (prpWidget->status() == PluginStatusLoadedSuccessfully) |
| return prpWidget; |
| #endif |
| // Create an iframe for youtube urls. |
| if (isYouTubeUrl(url, mimeType) && isYouTubeInstalled()) { |
| WTF::RefPtr<Frame> frame = createFrame(blankURL(), String(), element, |
| String(), false, 0, 0); |
| if (frame) { |
| // grab everything after /v/ |
| String videoId = url.path().substring(sizeof(slash_v_slash)); |
| // Extract just the video id |
| unsigned videoIdEnd = 0; |
| for (; videoIdEnd < videoId.length(); videoIdEnd++) { |
| if (videoId[videoIdEnd] == '&') { |
| videoId = videoId.left(videoIdEnd); |
| break; |
| } |
| } |
| AssetManager* am = globalAssetManager(); |
| Asset* a = am->open("webkit/youtube.html", |
| Asset::ACCESS_BUFFER); |
| if (!a) |
| return NULL; |
| String s = String((const char*)a->getBuffer(false), a->getLength()); |
| s = s.replace("VIDEO_ID", videoId); |
| delete a; |
| loadDataIntoFrame(frame.get(), |
| KURL(ParsedURLString, "file:///android_asset/webkit/"), String(), s); |
| // Transfer ownership to a local refptr. |
| WTF::RefPtr<Widget> widget(frame->view()); |
| return widget.release(); |
| } |
| } |
| return prpWidget; |
| } |
| |
| void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) { |
| // Do not redirect data if the Widget is our plugin placeholder. |
| if (pluginWidget->isPluginView()) { |
| m_manualLoader = static_cast<PluginView*>(pluginWidget); |
| } |
| } |
| |
| WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, HTMLAppletElement*, |
| const KURL& baseURL, const WTF::Vector<String>& paramNames, |
| const WTF::Vector<String>& paramValues) { |
| // don't support widget yet |
| notImplemented(); |
| return 0; |
| } |
| |
| void FrameLoaderClientAndroid::didTransferChildFrameToNewDocument(WebCore::Page*) |
| { |
| ASSERT(m_frame); |
| // m_webFrame points to the WebFrame for the page that our frame previously |
| // belonged to. If the frame now belongs to a new page, we need to update |
| // m_webFrame to point to the WebFrame for the new page. |
| Page* newPage = m_frame->page(); |
| if (newPage != m_webFrame->page()) { |
| ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(newPage->chrome()->client()); |
| Release(m_webFrame); |
| m_webFrame = chromeClient->webFrame(); |
| Retain(m_webFrame); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::transferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*) |
| { |
| notImplemented(); |
| } |
| |
| // This function is used by the <OBJECT> element to determine the type of |
| // the contents and work out if it can render it. |
| ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages) { |
| return FrameLoader::defaultObjectContentType(url, mimeType, shouldPreferPlugInsForImages); |
| } |
| |
| // This function allows the application to set the correct CSS media |
| // style. Android could use it to set the media style 'handheld'. Safari |
| // may use it to set the media style to 'print' when the user wants to print |
| // a particular web page. |
| String FrameLoaderClientAndroid::overrideMediaType() const { |
| notImplemented(); |
| return String(); |
| } |
| |
| // This function is used to re-attach Javascript<->native code classes. |
| void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) |
| { |
| if (world != mainThreadNormalWorld()) |
| return; |
| |
| ASSERT(m_frame); |
| ALOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n", |
| m_frame, m_frame->document()->url().string().ascii().data()); |
| m_webFrame->windowObjectCleared(m_frame); |
| } |
| |
| void FrameLoaderClientAndroid::documentElementAvailable() { |
| } |
| |
| // functions new to Jun-07 tip of tree merge: |
| ResourceError FrameLoaderClientAndroid::blockedError(ResourceRequest const& request) { |
| return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String()); |
| } |
| |
| // functions new to Nov-07 tip of tree merge: |
| void FrameLoaderClientAndroid::didPerformFirstNavigation() const { |
| // This seems to be just a notification that the UI can listen to, to |
| // know if the user has performed first navigation action. |
| // It is called from |
| // void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) |
| // "Navigation" here means a transition from one page to another that |
| // ends up in the back/forward list. |
| } |
| |
| void FrameLoaderClientAndroid::registerForIconNotification(bool listen) { |
| if (listen) |
| WebIconDatabase::RegisterForIconNotification(this); |
| else |
| WebIconDatabase::UnregisterForIconNotification(this); |
| } |
| |
| // This is the WebIconDatabaseClient method for receiving a notification when we |
| // get the icon for the page. |
| void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) { |
| // This call must happen before dispatchDidReceiveIcon since that method |
| // may register for icon notifications again since the icon data may have |
| // to be read from disk. |
| registerForIconNotification(false); |
| KURL u(ParsedURLString, pageUrl); |
| if (equalIgnoringFragmentIdentifier(u, m_frame->document()->url())) { |
| dispatchDidReceiveIcon(); |
| } |
| } |
| |
| void FrameLoaderClientAndroid::dispatchDidChangeIcons() { |
| notImplemented(); |
| } |
| |
| PassRefPtr<FrameNetworkingContext> FrameLoaderClientAndroid::createNetworkingContext() |
| { |
| return FrameNetworkingContextAndroid::create(getFrame()); |
| } |
| |
| } |