| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "config.h" |
| #include <string> |
| #include <vector> |
| |
| #include "base/compiler_specific.h" |
| |
| MSVC_PUSH_WARNING_LEVEL(0); |
| #include "Chrome.h" |
| #include "CString.h" |
| #include "Document.h" |
| #include "DocumentLoader.h" |
| #include "HistoryItem.h" |
| #include "HTMLAppletElement.h" |
| #include "HTMLCollection.h" |
| #include "HTMLFormElement.h" // needed by FormState.h |
| #include "HTMLFormControlElement.h" |
| #include "HTMLInputElement.h" |
| #include "HTMLNames.h" |
| #include "FormState.h" |
| #include "FrameLoader.h" |
| #include "FrameLoadRequest.h" |
| #include "FrameView.h" |
| #include "MIMETypeRegistry.h" |
| #include "MouseEvent.h" |
| #include "Page.h" |
| #include "PlatformString.h" |
| #include "PluginData.h" |
| #include "RefPtr.h" |
| #include "StringExtras.h" |
| #include "WindowFeatures.h" |
| MSVC_POP_WARNING(); |
| |
| #undef LOG |
| #include "base/basictypes.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| #include "net/base/mime_util.h" |
| #include "net/base/net_errors.h" |
| #if defined(OS_WIN) |
| #include "webkit/activex_shim/activex_shared.h" |
| #endif |
| #include "webkit/api/public/WebForm.h" |
| #include "webkit/api/public/WebURL.h" |
| #include "webkit/api/public/WebURLError.h" |
| #include "webkit/api/public/WebVector.h" |
| #include "webkit/api/src/WrappedResourceRequest.h" |
| #include "webkit/api/src/WrappedResourceResponse.h" |
| #include "webkit/glue/autofill_form.h" |
| #include "webkit/glue/alt_404_page_resource_fetcher.h" |
| #include "webkit/glue/glue_util.h" |
| #include "webkit/glue/password_form_dom_manager.h" |
| #include "webkit/glue/plugins/plugin_list.h" |
| #include "webkit/glue/searchable_form_data.h" |
| #include "webkit/glue/webappcachecontext.h" |
| #include "webkit/glue/webdatasource_impl.h" |
| #include "webkit/glue/webdevtoolsagent_impl.h" |
| #include "webkit/glue/webframeloaderclient_impl.h" |
| #include "webkit/glue/webkit_glue.h" |
| #include "webkit/glue/webplugin_delegate.h" |
| #include "webkit/glue/webplugin_impl.h" |
| #include "webkit/glue/webview_delegate.h" |
| #include "webkit/glue/webview_impl.h" |
| |
| using namespace WebCore; |
| using base::Time; |
| using base::TimeDelta; |
| using WebKit::WebNavigationType; |
| using WebKit::WebURL; |
| using WebKit::WebVector; |
| using WebKit::WrappedResourceRequest; |
| using WebKit::WrappedResourceResponse; |
| |
| // Domain for internal error codes. |
| static const char kInternalErrorDomain[] = "webkit_glue"; |
| |
| // An internal error code. Used to note a policy change error resulting from |
| // dispatchDecidePolicyForMIMEType not passing the PolicyUse option. |
| enum { |
| ERR_POLICY_CHANGE = -10000, |
| }; |
| |
| WebFrameLoaderClient::WebFrameLoaderClient(WebFrameImpl* frame) : |
| webframe_(frame), |
| postpone_loading_data_(false), |
| has_representation_(false), |
| plugin_widget_(NULL), |
| sent_initial_response_to_plugin_(false), |
| next_window_open_disposition_(IGNORE_ACTION) { |
| } |
| |
| WebFrameLoaderClient::~WebFrameLoaderClient() { |
| } |
| |
| void WebFrameLoaderClient::frameLoaderDestroyed() { |
| // When the WebFrame was created, it had an extra reference given to it on |
| // behalf of the Frame. Since the WebFrame owns us, this extra ref also |
| // serves to keep us alive until the FrameLoader is done with us. The |
| // FrameLoader calls this method when it's going away. Therefore, we balance |
| // out that extra reference, which may cause 'this' to be deleted. |
| webframe_->Closing(); |
| webframe_->Release(); |
| } |
| |
| void WebFrameLoaderClient::windowObjectCleared() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->WindowObjectCleared(webframe_); |
| |
| WebDevToolsAgentImpl* tools_agent = webview->GetWebDevToolsAgentImpl(); |
| if (tools_agent) { |
| tools_agent->WindowObjectCleared(webframe_); |
| } |
| } |
| |
| void WebFrameLoaderClient::documentElementAvailable() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DocumentElementAvailable(webframe_); |
| } |
| |
| void WebFrameLoaderClient::didPerformFirstNavigation() const { |
| } |
| |
| void WebFrameLoaderClient::registerForIconNotification(bool listen){ |
| } |
| |
| bool WebFrameLoaderClient::hasWebView() const { |
| return webframe_->GetWebViewImpl() != NULL; |
| } |
| |
| bool WebFrameLoaderClient::hasFrameView() const { |
| // The Mac port has this notion of a WebFrameView, which seems to be |
| // some wrapper around an NSView. Since our equivalent is HWND, I guess |
| // we have a "frameview" whenever we have the toplevel HWND. |
| return webframe_->GetWebViewImpl() != NULL; |
| } |
| |
| void WebFrameLoaderClient::makeDocumentView() { |
| webframe_->CreateFrameView(); |
| } |
| |
| void WebFrameLoaderClient::makeRepresentation(DocumentLoader*) { |
| has_representation_ = true; |
| } |
| |
| void WebFrameLoaderClient::forceLayout() { |
| // FIXME |
| } |
| void WebFrameLoaderClient::forceLayoutForNonHTML() { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::setCopiesOnScroll() { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::detachedFromParent2() { |
| // Nothing to do here. |
| } |
| |
| void WebFrameLoaderClient::detachedFromParent3() { |
| // Close down the proxy. The purpose of this change is to make the |
| // call to ScriptController::clearWindowShell a no-op when called from |
| // Frame::pageDestroyed. Without this change, this call to clearWindowShell |
| // will cause a crash. If you remove/modify this, just ensure that you can |
| // go to a page and then navigate to a new page without getting any asserts |
| // or crashes. |
| webframe_->frame()->script()->proxy()->clearForClose(); |
| } |
| |
| // This function is responsible for associating the |identifier| with a given |
| // subresource load. The following functions that accept an |identifier| are |
| // called for each subresource, so they should not be dispatched to the |
| // WebFrame. |
| void WebFrameLoaderClient::assignIdentifierToInitialRequest( |
| unsigned long identifier, DocumentLoader* loader, |
| const ResourceRequest& request) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) { |
| WrappedResourceRequest webreq(request); |
| d->AssignIdentifierToRequest(webview, identifier, webreq); |
| } |
| } |
| |
| // Determines whether the request being loaded by |loader| is a frame or a |
| // subresource. A subresource in this context is anything other than a frame -- |
| // this includes images and xmlhttp requests. It is important to note that a |
| // subresource is NOT limited to stuff loaded through the frame's subresource |
| // loader. Synchronous xmlhttp requests for example, do not go through the |
| // subresource loader, but we still label them as TargetIsSubResource. |
| // |
| // The important edge cases to consider when modifying this function are |
| // how synchronous resource loads are treated during load/unload threshold. |
| static ResourceRequest::TargetType DetermineTargetTypeFromLoader( |
| DocumentLoader* loader) { |
| if (loader == loader->frameLoader()->provisionalDocumentLoader()) { |
| if (loader->frameLoader()->isLoadingMainFrame()) { |
| return ResourceRequest::TargetIsMainFrame; |
| } else { |
| return ResourceRequest::TargetIsSubFrame; |
| } |
| } |
| return ResourceRequest::TargetIsSubResource; |
| } |
| |
| void WebFrameLoaderClient::dispatchWillSendRequest( |
| DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, |
| const ResourceResponse& redirectResponse) { |
| |
| if (loader) { |
| // We want to distinguish between a request for a document to be loaded into |
| // the main frame, a sub-frame, or the sub-objects in that document. |
| request.setTargetType(DetermineTargetTypeFromLoader(loader)); |
| } |
| |
| // FrameLoader::loadEmptyDocumentSynchronously() creates an empty document |
| // with no URL. We don't like that, so we'll rename it to about:blank. |
| if (request.url().isEmpty()) |
| request.setURL(KURL("about:blank")); |
| if (request.firstPartyForCookies().isEmpty()) |
| request.setFirstPartyForCookies(KURL("about:blank")); |
| |
| // Give the delegate a crack at the request. |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) { |
| WrappedResourceRequest webreq(request); |
| d->WillSendRequest(webview, identifier, &webreq); |
| } |
| |
| request.setAppCacheContextID( |
| webframe_->GetAppCacheContext()->GetContextID()); |
| } |
| |
| bool WebFrameLoaderClient::shouldUseCredentialStorage(DocumentLoader*, |
| unsigned long identifier) { |
| // FIXME |
| // Intended to pass through to a method on the resource load delegate. |
| // If implemented, that method controls whether the browser should ask the |
| // networking layer for a stored default credential for the page (say from |
| // the Mac OS keychain). If the method returns false, the user should be |
| // presented with an authentication challenge whether or not the networking |
| // layer has a credential stored. |
| // This returns true for backward compatibility: the ability to override the |
| // system credential store is new. (Actually, not yet fully implemented in |
| // WebKit, as of this writing.) |
| return true; |
| } |
| |
| void WebFrameLoaderClient::dispatchDidReceiveAuthenticationChallenge( |
| DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::dispatchDidCancelAuthenticationChallenge( |
| DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::dispatchDidReceiveResponse(DocumentLoader* loader, |
| unsigned long identifier, |
| const ResourceResponse& response) { |
| |
| |
| /* TODO(evanm): reenable this once we properly sniff XHTML from text/xml documents. |
| // True if the request was for the page's main frame, or a subframe. |
| bool is_frame = ResourceType::IsFrame(DetermineTargetTypeFromLoader(loader)); |
| if (is_frame && |
| response.httpStatusCode() == 200 && |
| mime_util::IsViewSourceMimeType( |
| webkit_glue::CStringToStdString(response.mimeType().latin1()).c_str())) { |
| loader->frame()->setInViewSourceMode(); |
| }*/ |
| |
| // When the frame request first 404's, chrome may replace it with the alternate |
| // 404 page's contents. It does this using substitute data in the document |
| // loader, so the original response and url of the request can be preserved. |
| // We need to avoid replacing the current page, if it has already been |
| // replaced (otherwise could loop on setting alt-404 page!) |
| bool is_substitute_data = loader->substituteData().isValid(); |
| |
| // If it's a 404 page, we wait until we get 512 bytes of data before trying |
| // to load the document. This allows us to put up an alternate 404 page if |
| // there's short text. |
| ResourceRequest::TargetType target_type = |
| DetermineTargetTypeFromLoader(loader); |
| postpone_loading_data_ = |
| ResourceRequest::TargetIsMainFrame == target_type && |
| !is_substitute_data && |
| response.httpStatusCode() == 404 && |
| GetAlt404PageUrl(loader).is_valid(); |
| if (postpone_loading_data_) |
| postponed_data_.clear(); |
| |
| // Cancel any pending loads. |
| alt_404_page_fetcher_.reset(NULL); |
| } |
| |
| void WebFrameLoaderClient::dispatchDidReceiveContentLength( |
| DocumentLoader* loader, |
| unsigned long identifier, |
| int length_received) { |
| } |
| |
| // Called when a particular resource load completes |
| void WebFrameLoaderClient::dispatchDidFinishLoading(DocumentLoader* loader, |
| unsigned long identifier) { |
| if (postpone_loading_data_) { |
| // The server returned a 404 and the content was < 512 bytes (which we |
| // suppressed). Go ahead and fetch the alternate page content. |
| const GURL& url = GetAlt404PageUrl(loader); |
| DCHECK(url.is_valid()) << |
| "URL changed? It was valid in dispatchDidReceiveResponse."; |
| alt_404_page_fetcher_.reset(new Alt404PageResourceFetcher(this, |
| webframe_->frame(), loader, url)); |
| } |
| |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidFinishLoading(webview, identifier); |
| } |
| |
| GURL WebFrameLoaderClient::GetAlt404PageUrl(DocumentLoader* loader) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (!d) |
| return GURL(); |
| |
| const GURL& failedURL = webkit_glue::KURLToGURL(loader->url()); |
| |
| // If trying to view source on a 404 page, just show the original page |
| // content. |
| if (webframe_->frame()->inViewSourceMode()) |
| return GURL(); |
| |
| // Construct the URL to fetch from the alt error page server. "html404" |
| // is understood by the link doctor server. |
| return d->GetAlternateErrorPageURL(failedURL, WebViewDelegate::HTTP_404); |
| } |
| |
| void WebFrameLoaderClient::Alt404PageFinished(DocumentLoader* loader, |
| const std::string& html) { |
| if (html.length() > 0) { |
| // TODO(tc): Handle backoff on so we don't hammer the alt error page |
| // servers. |
| webframe_->LoadHTMLString(html, webkit_glue::KURLToGURL(loader->url())); |
| } else { |
| // Fall back on original text |
| webframe_->LoadHTMLString(postponed_data_, |
| webkit_glue::KURLToGURL(loader->url())); |
| } |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFailLoading(DocumentLoader* loader, |
| unsigned long identifier, |
| const ResourceError& error) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| if (webview && webview->delegate()) { |
| webview->delegate()->DidFailLoadingWithError( |
| webview, identifier, webkit_glue::ResourceErrorToWebURLError(error)); |
| } |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFinishDocumentLoad() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| DocumentLoader* documentLoader = |
| webframe_->frame()->loader()->activeDocumentLoader(); |
| WebDataSourceImpl* data_source = |
| WebDataSourceImpl::FromLoader(documentLoader); |
| |
| // A frame may be reused. This call ensures we don't hold on to our password |
| // listeners and their associated HTMLInputElements. |
| webframe_->ClearPasswordListeners(); |
| |
| if (d) |
| d->DidFinishDocumentLoadForFrame(webview, webframe_); |
| } |
| |
| bool WebFrameLoaderClient::dispatchDidLoadResourceFromMemoryCache( |
| DocumentLoader* loader, |
| const ResourceRequest& request, |
| const ResourceResponse& response, |
| int length) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| |
| bool result = false; |
| if (d) { |
| WrappedResourceRequest webreq(request); |
| WrappedResourceResponse webresp(response); |
| result = d->DidLoadResourceFromMemoryCache(webview, webreq, webresp, |
| webframe_); |
| } |
| return result; |
| } |
| |
| void WebFrameLoaderClient::dispatchDidLoadResourceByXMLHttpRequest( |
| unsigned long identifier, |
| const ScriptString& source) { |
| } |
| |
| void WebFrameLoaderClient::dispatchDidHandleOnloadEvents() { |
| // During the onload event of a subframe, the subframe can be removed. In |
| // that case, it has no page. This is covered by |
| // LayoutTests/fast/dom/replaceChild.html |
| if (!webframe_->frame()->page()) |
| return; |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidHandleOnloadEventsForFrame(webview, webframe_); |
| } |
| |
| // Redirect Tracking |
| // ================= |
| // We want to keep track of the chain of redirects that occur during page |
| // loading. There are two types of redirects, server redirects which are HTTP |
| // response codes, and client redirects which are document.location= and meta |
| // refreshes. |
| // |
| // This outlines the callbacks that we get in different redirect situations, |
| // and how each call modifies the redirect chain. |
| // |
| // Normal page load |
| // ---------------- |
| // dispatchDidStartProvisionalLoad() -> adds URL to the redirect list |
| // dispatchDidCommitLoad() -> DISPATCHES & clears list |
| // |
| // Server redirect (success) |
| // ------------------------- |
| // dispatchDidStartProvisionalLoad() -> adds source URL |
| // dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL |
| // dispatchDidCommitLoad() -> DISPATCHES |
| // |
| // Client redirect (success) |
| // ------------------------- |
| // (on page) |
| // dispatchWillPerformClientRedirect() -> saves expected redirect |
| // dispatchDidStartProvisionalLoad() -> appends redirect source (since |
| // it matches the expected redirect) |
| // and the current page as the dest) |
| // dispatchDidCancelClientRedirect() -> clears expected redirect |
| // dispatchDidCommitLoad() -> DISPATCHES |
| // |
| // Client redirect (cancelled) |
| // (e.g meta-refresh trumped by manual doc.location change, or just cancelled |
| // because a link was clicked that requires the meta refresh to be rescheduled |
| // (the SOURCE URL may have changed). |
| // --------------------------- |
| // dispatchDidCancelClientRedirect() -> clears expected redirect |
| // dispatchDidStartProvisionalLoad() -> adds only URL to redirect list |
| // dispatchDidCommitLoad() -> DISPATCHES & clears list |
| // rescheduled ? dispatchWillPerformClientRedirect() -> saves expected redirect |
| // : nothing |
| |
| // Client redirect (failure) |
| // ------------------------- |
| // (on page) |
| // dispatchWillPerformClientRedirect() -> saves expected redirect |
| // dispatchDidStartProvisionalLoad() -> appends redirect source (since |
| // it matches the expected redirect) |
| // and the current page as the dest) |
| // dispatchDidCancelClientRedirect() |
| // dispatchDidFailProvisionalLoad() |
| // |
| // Load 1 -> Server redirect to 2 -> client redirect to 3 -> server redirect to 4 |
| // ------------------------------------------------------------------------------ |
| // dispatchDidStartProvisionalLoad() -> adds source URL 1 |
| // dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL 2 |
| // dispatchDidCommitLoad() -> DISPATCHES 1+2 |
| // -- begin client redirect and NEW DATA SOURCE |
| // dispatchWillPerformClientRedirect() -> saves expected redirect |
| // dispatchDidStartProvisionalLoad() -> appends URL 2 and URL 3 |
| // dispatchDidReceiveServerRedirectForProvisionalLoad() -> appends destination URL 4 |
| // dispatchDidCancelClientRedirect() -> clears expected redirect |
| // dispatchDidCommitLoad() -> DISPATCHES |
| // |
| // Interesting case with multiple location changes involving anchors. |
| // Load page 1 containing future client-redirect (back to 1, e.g meta refresh) > Click |
| // on a link back to the same page (i.e an anchor href) > |
| // client-redirect finally fires (with new source, set to 1#anchor) |
| // ----------------------------------------------------------------------------- |
| // dispatchWillPerformClientRedirect(non-zero 'interval' param) -> saves expected redirect |
| // -- click on anchor href |
| // dispatchDidCancelClientRedirect() -> clears expected redirect |
| // dispatchDidStartProvisionalLoad() -> adds 1#anchor source |
| // dispatchDidCommitLoad() -> DISPATCHES 1#anchor |
| // dispatchWillPerformClientRedirect() -> saves exp. source (1#anchor) |
| // -- redirect timer fires |
| // dispatchDidStartProvisionalLoad() -> appends 1#anchor (src) and 1 (dest) |
| // dispatchDidCancelClientRedirect() -> clears expected redirect |
| // dispatchDidCommitLoad() -> DISPATCHES 1#anchor + 1 |
| // |
| void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad() { |
| WebDataSourceImpl* ds = webframe_->GetProvisionalDataSourceImpl(); |
| if (!ds) { |
| NOTREACHED() << "Got a server redirect when there is no provisional DS"; |
| return; |
| } |
| |
| // A provisional load should have started already, which should have put an |
| // entry in our redirect chain. |
| DCHECK(ds->HasRedirectChain()); |
| |
| // The URL of the destination is on the provisional data source. We also need |
| // to update the redirect chain to account for this addition (we do this |
| // before the callback so the callback can look at the redirect chain to see |
| // what happened). |
| ds->AppendRedirect(ds->request().url()); |
| |
| // Dispatch callback |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidReceiveProvisionalLoadServerRedirect(webview, webframe_); |
| } |
| |
| // Called on both success and failure of a client redirect. |
| void WebFrameLoaderClient::dispatchDidCancelClientRedirect() { |
| // No longer expecting a client redirect. |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview ? webview->delegate() : NULL; |
| if (d) { |
| expected_client_redirect_src_ = GURL(); |
| expected_client_redirect_dest_ = GURL(); |
| |
| d->DidCancelClientRedirect(webview, webframe_); |
| } |
| |
| // No need to clear the redirect chain, since that data source has already |
| // been deleted by the time this function is called. |
| } |
| |
| void WebFrameLoaderClient::dispatchWillPerformClientRedirect(const KURL& url, |
| double interval, |
| double fire_date) { |
| // Tells dispatchDidStartProvisionalLoad that if it sees this item it is a |
| // redirect and the source item should be added as the start of the chain. |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview ? webview->delegate() : NULL; |
| if (d) { |
| expected_client_redirect_src_ = webframe_->GetURL(); |
| expected_client_redirect_dest_ = webkit_glue::KURLToGURL(url); |
| |
| // TODO(timsteele): bug 1135512. Webkit does not properly notify us of |
| // cancelling http > file client redirects. Since the FrameLoader's policy |
| // is to never carry out such a navigation anyway, the best thing we can do |
| // for now to not get confused is ignore this notification. |
| if (expected_client_redirect_dest_.SchemeIsFile() && |
| (expected_client_redirect_src_.SchemeIs("http") || |
| expected_client_redirect_src_.SchemeIsSecure())) { |
| expected_client_redirect_src_ = GURL(); |
| expected_client_redirect_dest_ = GURL(); |
| return; |
| } |
| |
| d->WillPerformClientRedirect(webview, |
| webframe_, |
| expected_client_redirect_src_, |
| expected_client_redirect_dest_, |
| static_cast<unsigned int>(interval), |
| static_cast<unsigned int>(fire_date)); |
| } |
| } |
| |
| void WebFrameLoaderClient::dispatchDidChangeLocationWithinPage() { |
| // Anchor fragment navigations are not normal loads, so we need to synthesize |
| // some events for our delegate. |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidStartLoading(webview); |
| |
| WebDataSourceImpl* ds = webframe_->GetDataSourceImpl(); |
| DCHECK(ds) << "DataSource NULL when navigating to reference fragment"; |
| if (ds) { |
| GURL url = ds->request().url(); |
| GURL chain_end = ds->GetEndOfRedirectChain(); |
| ds->ClearRedirectChain(); |
| |
| // Figure out if this location change is because of a JS-initiated client |
| // redirect (e.g onload/setTimeout document.location.href=). |
| // TODO(timsteele): (bugs 1085325, 1046841) We don't get proper redirect |
| // performed/cancelled notifications across anchor navigations, so the |
| // other redirect-tracking code in this class (see dispatch*ClientRedirect() |
| // and dispatchDidStartProvisionalLoad) is insufficient to catch and |
| // properly flag these transitions. Once a proper fix for this bug is |
| // identified and applied the following block may no longer be required. |
| bool was_client_redirect = |
| ((url == expected_client_redirect_dest_) && |
| (chain_end == expected_client_redirect_src_)) || |
| (NavigationGestureForLastLoad() == NavigationGestureAuto); |
| |
| if (was_client_redirect) { |
| if (d) |
| d->DidCompleteClientRedirect(webview, webframe_, chain_end); |
| ds->AppendRedirect(chain_end); |
| // Make sure we clear the expected redirect since we just effectively |
| // completed it. |
| expected_client_redirect_src_ = GURL(); |
| expected_client_redirect_dest_ = GURL(); |
| } |
| |
| // Regardless of how we got here, we are navigating to a URL so we need to |
| // add it to the redirect chain. |
| ds->AppendRedirect(url); |
| } |
| |
| bool is_new_navigation; |
| webview->DidCommitLoad(&is_new_navigation); |
| if (d) { |
| d->DidChangeLocationWithinPageForFrame(webview, webframe_, |
| is_new_navigation); |
| } |
| |
| if (d) |
| d->DidStopLoading(webview); |
| } |
| |
| void WebFrameLoaderClient::dispatchWillClose() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| // Make sure WebViewImpl releases the references it uses to restore focus. |
| // If we didn't do this, WebViewImpl might try to restore focus to an invalid |
| // element. |
| webview->ReleaseFocusReferences(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->WillCloseFrame(webview, webframe_); |
| } |
| |
| void WebFrameLoaderClient::dispatchDidReceiveIcon() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidReceiveIconForFrame(webview, webframe_); |
| } |
| |
| void WebFrameLoaderClient::dispatchDidStartProvisionalLoad() { |
| // In case a redirect occurs, we need this to be set so that the redirect |
| // handling code can tell where the redirect came from. Server redirects |
| // will occur on the provisional load, so we need to keep track of the most |
| // recent provisional load URL. |
| // See dispatchDidReceiveServerRedirectForProvisionalLoad. |
| WebDataSourceImpl* ds = webframe_->GetProvisionalDataSourceImpl(); |
| if (!ds) { |
| NOTREACHED() << "Attempting to provisional load but there isn't one"; |
| return; |
| } |
| GURL url = ds->request().url(); |
| |
| // Since the provisional load just started, we should have not gotten |
| // any redirects yet. |
| DCHECK(!ds->HasRedirectChain()); |
| |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| // If this load is what we expected from a client redirect, treat it as a |
| // redirect from that original page. The expected redirect urls will be |
| // cleared by DidCancelClientRedirect. |
| bool completing_client_redirect = false; |
| if (expected_client_redirect_src_.is_valid()) { |
| // expected_client_redirect_dest_ could be something like |
| // "javascript:history.go(-1)" thus we need to exclude url starts with |
| // "javascript:". See bug: 1080873 |
| DCHECK(expected_client_redirect_dest_.SchemeIs("javascript") || |
| expected_client_redirect_dest_ == url); |
| ds->AppendRedirect(expected_client_redirect_src_); |
| completing_client_redirect = true; |
| } |
| ds->AppendRedirect(url); |
| |
| if (d) { |
| // As the comment for DidCompleteClientRedirect in webview_delegate.h |
| // points out, whatever information its invocation contains should only |
| // be considered relevant until the next provisional load has started. |
| // So we first tell the delegate that the load started, and then tell it |
| // about the client redirect the load is responsible for completing. |
| d->DidStartProvisionalLoadForFrame(webview, webframe_, |
| NavigationGestureForLastLoad()); |
| if (completing_client_redirect) |
| d->DidCompleteClientRedirect(webview, webframe_, |
| expected_client_redirect_src_); |
| } |
| |
| // Cancel any pending loads. |
| if (alt_404_page_fetcher_.get()) |
| alt_404_page_fetcher_->Cancel(); |
| } |
| |
| void WebFrameLoaderClient::dispatchDidReceiveTitle(const String& title) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) { |
| d->DidReceiveTitle(webview, webkit_glue::StringToStdWString(title), |
| webframe_); |
| } |
| } |
| |
| void WebFrameLoaderClient::dispatchDidCommitLoad() { |
| webframe_->SelectAppCacheWithoutManifest(); |
| |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| bool is_new_navigation; |
| webview->DidCommitLoad(&is_new_navigation); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidCommitLoadForFrame(webview, webframe_, is_new_navigation); |
| |
| WebDevToolsAgentImpl* tools_agent = webview->GetWebDevToolsAgentImpl(); |
| if (tools_agent) { |
| tools_agent->DidCommitLoadForFrame(webview, webframe_, is_new_navigation); |
| } |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFailProvisionalLoad( |
| const ResourceError& error) { |
| // If a policy change occured, then we do not want to inform the plugin |
| // delegate. See bug 907789 for details. |
| if (error.domain() == kInternalErrorDomain && |
| error.errorCode() == ERR_POLICY_CHANGE) { |
| webframe_->DidFail(cancelledError(error.failingURL()), true); |
| } else { |
| webframe_->DidFail(error, true); |
| WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); |
| if (plg_delegate) |
| plg_delegate->DidFinishLoadWithReason(NPRES_NETWORK_ERR); |
| } |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFailLoad(const ResourceError& error) { |
| webframe_->DidFail(error, false); |
| |
| WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); |
| if (plg_delegate) |
| plg_delegate->DidFinishLoadWithReason(NPRES_NETWORK_ERR); |
| |
| // Don't clear the redirect chain, this will happen in the middle of client |
| // redirects, and we need the context. The chain will be cleared when the |
| // provisional load succeeds or fails, not the "real" one. |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFinishLoad() { |
| DocumentLoader* documentLoader = |
| webframe_->frame()->loader()->activeDocumentLoader(); |
| WebDataSourceImpl* dataSource = |
| WebDataSourceImpl::FromLoader(documentLoader); |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidFinishLoadForFrame(webview, webframe_); |
| WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); |
| if (plg_delegate) |
| plg_delegate->DidFinishLoadWithReason(NPRES_DONE); |
| |
| // Don't clear the redirect chain, this will happen in the middle of client |
| // redirects, and we need the context. The chain will be cleared when the |
| // provisional load succeeds or fails, not the "real" one. |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFirstLayout() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->DidFirstLayout(webview, webframe_); |
| } |
| |
| void WebFrameLoaderClient::dispatchDidFirstVisuallyNonEmptyLayout() { |
| // FIXME: called when webkit finished layout of a page that was visually |
| // non-empty. |
| // All resources have not necessarily finished loading. |
| } |
| |
| Frame* WebFrameLoaderClient::dispatchCreatePage() { |
| struct WebCore::WindowFeatures features; |
| Page* new_page = webframe_->frame()->page()->chrome()->createWindow( |
| webframe_->frame(), FrameLoadRequest(), features); |
| |
| // Make sure that we have a valid disposition. This should have been set in |
| // the preceeding call to dispatchDecidePolicyForNewWindowAction. |
| DCHECK(next_window_open_disposition_ != IGNORE_ACTION); |
| WindowOpenDisposition disp = next_window_open_disposition_; |
| next_window_open_disposition_ = IGNORE_ACTION; |
| |
| // createWindow can return NULL (e.g., popup blocker denies the window). |
| if (!new_page) |
| return NULL; |
| |
| WebViewImpl::FromPage(new_page)->set_window_open_disposition(disp); |
| return new_page->mainFrame(); |
| } |
| |
| void WebFrameLoaderClient::dispatchShow() { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) |
| d->Show(webview, webview->window_open_disposition()); |
| } |
| |
| static bool TreatAsAttachment(const ResourceResponse& response) { |
| const String& content_disposition = |
| response.httpHeaderField("Content-Disposition"); |
| if (content_disposition.isEmpty()) |
| return false; |
| |
| // 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 WebFrameLoaderClient::dispatchDecidePolicyForMIMEType( |
| FramePolicyFunction function, |
| const String& mime_type, |
| const ResourceRequest&) { |
| const ResourceResponse& response = |
| webframe_->frame()->loader()->activeDocumentLoader()->response(); |
| |
| PolicyAction action; |
| |
| int status_code = response.httpStatusCode(); |
| if (status_code == 204 || status_code == 205) { |
| // The server does not want us to replace the page contents. |
| action = PolicyIgnore; |
| } else if (TreatAsAttachment(response)) { |
| // The server wants us to download instead of replacing the page contents. |
| // Downloading is handled by the embedder, but we still get the initial |
| // response so that we can ignore it and clean up properly. |
| action = PolicyIgnore; |
| } else if (!canShowMIMEType(mime_type)) { |
| // Make sure that we can actually handle this type internally. |
| action = PolicyIgnore; |
| } else { |
| // OK, we will render this page. |
| action = PolicyUse; |
| } |
| |
| // NOTE: ERR_POLICY_CHANGE will be generated when action is not PolicyUse. |
| (webframe_->frame()->loader()->*function)(action); |
| } |
| |
| void WebFrameLoaderClient::dispatchDecidePolicyForNewWindowAction( |
| WebCore::FramePolicyFunction function, |
| const WebCore::NavigationAction& action, |
| const WebCore::ResourceRequest& request, |
| PassRefPtr<WebCore::FormState> form_state, |
| const WebCore::String& frame_name) { |
| WindowOpenDisposition disposition; |
| if (!ActionSpecifiesDisposition(action, &disposition)) |
| disposition = NEW_FOREGROUND_TAB; |
| |
| PolicyAction policy_action; |
| if (disposition == SAVE_TO_DISK) { |
| policy_action = PolicyDownload; |
| } else { |
| policy_action = PolicyUse; |
| |
| // Remember the disposition for when dispatchCreatePage is called. It is |
| // unfortunate that WebCore does not provide us with any context when |
| // creating or showing the new window that would allow us to avoid having |
| // to keep this state. |
| next_window_open_disposition_ = disposition; |
| } |
| (webframe_->frame()->loader()->*function)(policy_action); |
| } |
| |
| void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction( |
| WebCore::FramePolicyFunction function, |
| const WebCore::NavigationAction& action, |
| const WebCore::ResourceRequest& request, |
| PassRefPtr<WebCore::FormState> form_state) { |
| PolicyAction policy_action = PolicyUse; |
| |
| WebViewImpl* wv = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = wv->delegate(); |
| // It is valid for this function to be invoked in code paths where the |
| // the webview is closed. |
| // The NULL check here is to fix a crash that seems strange |
| // (see - https://bugs.webkit.org/show_bug.cgi?id=23554). |
| if (d && !request.url().isNull()) { |
| WindowOpenDisposition disposition = CURRENT_TAB; |
| ActionSpecifiesDisposition(action, &disposition); |
| |
| // Give the delegate a chance to change the disposition. When we do not |
| // have a provisional data source here, it means that we are scrolling to |
| // an anchor in the page. We don't need to ask the WebViewDelegate about |
| // such navigations. |
| const WebDataSourceImpl* ds = webframe_->GetProvisionalDataSourceImpl(); |
| if (ds) { |
| GURL url = ds->request().url(); |
| if (url.SchemeIs(webkit_glue::kBackForwardNavigationScheme)) { |
| HandleBackForwardNavigation(url); |
| disposition = IGNORE_ACTION; |
| } else { |
| bool is_redirect = ds->HasRedirectChain(); |
| |
| WebNavigationType webnav_type = |
| WebDataSourceImpl::NavigationTypeToWebNavigationType(action.type()); |
| |
| disposition = d->DispositionForNavigationAction( |
| wv, webframe_, ds->request(), webnav_type, disposition, is_redirect); |
| } |
| |
| if (disposition != IGNORE_ACTION) { |
| if (disposition == CURRENT_TAB) { |
| policy_action = PolicyUse; |
| } else if (disposition == SAVE_TO_DISK) { |
| policy_action = PolicyDownload; |
| } else { |
| GURL referrer = webkit_glue::StringToGURL( |
| request.httpHeaderField("Referer")); |
| |
| d->OpenURL(webframe_->GetWebViewImpl(), |
| webkit_glue::KURLToGURL(request.url()), |
| referrer, |
| disposition); |
| policy_action = PolicyIgnore; |
| } |
| } else { |
| policy_action = PolicyIgnore; |
| } |
| } |
| } else { |
| policy_action = PolicyIgnore; |
| } |
| |
| (webframe_->frame()->loader()->*function)(policy_action); |
| } |
| |
| void WebFrameLoaderClient::cancelPolicyCheck() { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::dispatchUnableToImplementPolicy(const ResourceError&) { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::dispatchWillSubmitForm(FramePolicyFunction function, |
| PassRefPtr<FormState> form_ref) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (d) { |
| d->WillSubmitForm(webview, webframe_, |
| webkit_glue::HTMLFormElementToWebForm(form_ref->form())); |
| } |
| (webframe_->frame()->loader()->*function)(PolicyUse); |
| } |
| |
| void WebFrameLoaderClient::dispatchDidLoadMainResource(DocumentLoader*) { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::revertToProvisionalState(DocumentLoader*) { |
| has_representation_ = true; |
| } |
| |
| void WebFrameLoaderClient::setMainDocumentError(DocumentLoader*, |
| const ResourceError& error) { |
| if (plugin_widget_) { |
| if (sent_initial_response_to_plugin_) { |
| plugin_widget_->didFail(error); |
| sent_initial_response_to_plugin_ = false; |
| } |
| plugin_widget_ = NULL; |
| } |
| } |
| |
| void WebFrameLoaderClient::postProgressStartedNotification() { |
| if (hasWebView()) { |
| WebViewImpl* web_view = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = web_view->delegate(); |
| if (d) |
| d->DidStartLoading(web_view); |
| } |
| } |
| |
| void WebFrameLoaderClient::postProgressEstimateChangedNotification() { |
| // FIXME |
| } |
| |
| void WebFrameLoaderClient::postProgressFinishedNotification() { |
| // TODO(ericroman): why might webframe_->webview_impl be null? |
| // http://b/1234461 |
| if (hasWebView()) { |
| WebViewImpl* web_view = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = web_view->delegate(); |
| if (d) |
| d->DidStopLoading(web_view); |
| } |
| } |
| |
| void WebFrameLoaderClient::setMainFrameDocumentReady(bool ready) { |
| WebViewImpl* web_view = webframe_->GetWebViewImpl(); |
| if (!web_view) |
| return; |
| WebDevToolsAgentImpl* tools_agent = web_view->GetWebDevToolsAgentImpl(); |
| if (!tools_agent) |
| return; |
| |
| if (webframe_ == web_view->GetMainFrame()) { |
| tools_agent->SetMainFrameDocumentReady(ready); |
| } |
| } |
| |
| // Creates a new connection and begins downloading from that (contrast this |
| // with |download|). |
| void WebFrameLoaderClient::startDownload(const ResourceRequest& request) { |
| WebViewDelegate* d = webframe_->GetWebViewImpl()->delegate(); |
| if (d) { |
| const GURL url(webkit_glue::KURLToGURL(request.url())); |
| const GURL referrer(webkit_glue::StringToStdString(request.httpReferrer())); |
| d->DownloadUrl(url, referrer); |
| } |
| } |
| |
| void WebFrameLoaderClient::willChangeTitle(DocumentLoader*) { |
| // FIXME |
| } |
| void WebFrameLoaderClient::didChangeTitle(DocumentLoader*) { |
| // FIXME |
| } |
| |
| // Called whenever data is received. |
| void WebFrameLoaderClient::committedLoad(DocumentLoader* loader, const char* data, int length) { |
| if (!plugin_widget_) { |
| if (postpone_loading_data_) { |
| postponed_data_.append(data, length); |
| if (postponed_data_.length() >= 512) { |
| postpone_loading_data_ = false; |
| webframe_->DidReceiveData(loader, postponed_data_.c_str(), |
| static_cast<int>(postponed_data_.length())); |
| } |
| return; |
| } |
| webframe_->DidReceiveData(loader, data, length); |
| } |
| |
| // The plugin widget could have been created in the webframe_->DidReceiveData |
| // function. |
| if (plugin_widget_) { |
| if (!sent_initial_response_to_plugin_) { |
| sent_initial_response_to_plugin_ = true; |
| plugin_widget_->didReceiveResponse( |
| webframe_->frame()->loader()->activeDocumentLoader()->response()); |
| } |
| plugin_widget_->didReceiveData(data, length); |
| } |
| } |
| |
| void WebFrameLoaderClient::finishedLoading(DocumentLoader* dl) { |
| if (plugin_widget_) { |
| plugin_widget_->didFinishLoading(); |
| plugin_widget_ = NULL; |
| sent_initial_response_to_plugin_ = false; |
| } else { |
| // This is necessary to create an empty document. See bug 634004. |
| // However, we only want to do this if makeRepresentation has been called, to |
| // match the behavior on the Mac. |
| if (has_representation_) |
| dl->frameLoader()->setEncoding("", false); |
| } |
| } |
| |
| void WebFrameLoaderClient::updateGlobalHistory() { |
| } |
| |
| void WebFrameLoaderClient::updateGlobalHistoryRedirectLinks() { |
| } |
| |
| bool WebFrameLoaderClient::shouldGoToHistoryItem(HistoryItem*) const { |
| // FIXME |
| return true; |
| } |
| |
| ResourceError WebFrameLoaderClient::blockedError(const WebCore::ResourceRequest&) { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| ResourceError WebFrameLoaderClient::cancelledError( |
| const ResourceRequest& request) { |
| return ResourceError(net::kErrorDomain, net::ERR_ABORTED, |
| request.url().string(), String()); |
| } |
| ResourceError WebFrameLoaderClient::cannotShowURLError(const ResourceRequest&) { |
| // FIXME |
| return ResourceError(); |
| } |
| ResourceError WebFrameLoaderClient::interruptForPolicyChangeError( |
| const ResourceRequest& request) { |
| return ResourceError(kInternalErrorDomain, ERR_POLICY_CHANGE, |
| request.url().string(), String()); |
| } |
| |
| ResourceError WebFrameLoaderClient::cannotShowMIMETypeError(const ResourceResponse&) { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| ResourceError WebFrameLoaderClient::fileDoesNotExistError(const ResourceResponse&) { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| ResourceError WebFrameLoaderClient::pluginWillHandleLoadError(const WebCore::ResourceResponse&) { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| bool WebFrameLoaderClient::shouldFallBack(const ResourceError& error) { |
| // This method is called when we fail to load the URL for an <object> tag |
| // that has fallback content (child elements) and is being loaded as a frame. |
| // The error parameter indicates the reason for the load failure. |
| // We should let the fallback content load only if this wasn't a cancelled |
| // request. |
| // Note: The mac version also has a case for "WebKitErrorPluginWillHandleLoad" |
| return error.errorCode() != net::ERR_ABORTED; |
| } |
| |
| bool WebFrameLoaderClient::canHandleRequest(const ResourceRequest&) const { |
| // FIXME: this appears to be used only by the context menu code to determine |
| // if "open" should be displayed in the menu when clicking on a link. |
| return true; |
| } |
| |
| bool WebFrameLoaderClient::canShowMIMEType(const String& mime_type) const { |
| // This method is called to determine if the media type can be shown |
| // "internally" (i.e. inside the browser) regardless of whether or not the |
| // browser or a plugin is doing the rendering. |
| |
| // mime_type strings are supposed to be ASCII, but if they are not for some |
| // reason, then it just means that the mime type will fail all of these "is |
| // supported" checks and go down the path of an unhandled mime type. |
| if (net::IsSupportedMimeType( |
| webkit_glue::CStringToStdString(mime_type.latin1()))) |
| return true; |
| |
| // If Chrome is started with the --disable-plugins switch, pluginData is null. |
| WebCore::PluginData* plugin_data = webframe_->frame()->page()->pluginData(); |
| |
| // See if the type is handled by an installed plugin, if so, we can show it. |
| // TODO(beng): (http://b/1085524) This is the place to stick a preference to |
| // disable full page plugins (optionally for certain types!) |
| return !mime_type.isEmpty() && plugin_data && plugin_data->supportsMimeType(mime_type); |
| } |
| |
| bool WebFrameLoaderClient::representationExistsForURLScheme(const String& URLScheme) const { |
| // FIXME |
| return false; |
| } |
| |
| String WebFrameLoaderClient::generatedMIMETypeForURLScheme(const String& URLScheme) const { |
| // This appears to generate MIME types for protocol handlers that are handled |
| // internally. The only place I can find in the WebKit code that uses this |
| // function is WebView::registerViewClass, where it is used as part of the |
| // process by which custom view classes for certain document representations |
| // are registered. |
| String mimetype("x-apple-web-kit/"); |
| mimetype.append(URLScheme.lower()); |
| return mimetype; |
| } |
| |
| void WebFrameLoaderClient::frameLoadCompleted() { |
| // FIXME: the mac port also conditionally calls setDrawsBackground:YES on |
| // it's ScrollView here. |
| |
| // This comment from the Mac port: |
| // Note: Can be called multiple times. |
| // Even if already complete, we might have set a previous item on a frame that |
| // didn't do any data loading on the past transaction. Make sure to clear these out. |
| |
| // FIXME: setPreviousHistoryItem() no longer exists. http://crbug.com/8566 |
| // webframe_->frame()->loader()->setPreviousHistoryItem(0); |
| } |
| |
| void WebFrameLoaderClient::saveViewStateToItem(HistoryItem*) { |
| // FIXME |
| } |
| |
| |
| void WebFrameLoaderClient::restoreViewState() { |
| // FIXME: probably scrolls to last position when you go back or forward |
| } |
| |
| void WebFrameLoaderClient::provisionalLoadStarted() { |
| // FIXME: On mac, this does various caching stuff |
| } |
| |
| void WebFrameLoaderClient::didFinishLoad() { |
| WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); |
| if (plg_delegate) |
| plg_delegate->DidFinishLoadWithReason(NPRES_DONE); |
| } |
| void WebFrameLoaderClient::prepareForDataSourceReplacement() { |
| // FIXME |
| } |
| |
| PassRefPtr<DocumentLoader> WebFrameLoaderClient::createDocumentLoader( |
| const ResourceRequest& request, |
| const SubstituteData& data) { |
| RefPtr<WebDataSourceImpl> ds = WebDataSourceImpl::Create(request, data); |
| WebViewDelegate* d = webframe_->GetWebViewImpl()->delegate(); |
| if (d) |
| d->DidCreateDataSource(webframe_, ds.get()); |
| return ds.release(); |
| } |
| |
| void WebFrameLoaderClient::setTitle(const String& title, const KURL& url) { |
| // FIXME: monitor for changes in WebFrameLoaderClient.mm |
| // FIXME: Set the title of the current history item. HistoryItemImpl's setter |
| // will notify its clients (e.g. the history database) that the title |
| // has changed. |
| // |
| // e.g.: |
| // WebHistoryItem* item = |
| // webframe_->GetWebViewImpl()->GetBackForwardList()->GetCurrentItem(); |
| // WebHistoryItemImpl* item_impl = static_cast<WebHistoryItemImpl*>(item); |
| // |
| // item_impl->SetTitle(webkit_glue::StringToStdWString(title)); |
| } |
| |
| String WebFrameLoaderClient::userAgent(const KURL& url) { |
| return webkit_glue::StdStringToString( |
| webkit_glue::GetUserAgent(webkit_glue::KURLToGURL(url))); |
| } |
| |
| void WebFrameLoaderClient::savePlatformDataToCachedFrame(WebCore::CachedFrame*) { |
| NOTREACHED() << "Page cache should be disabled"; |
| } |
| |
| void WebFrameLoaderClient::transitionToCommittedFromCachedFrame(WebCore::CachedFrame*) { |
| ASSERT_NOT_REACHED(); |
| } |
| |
| // Called when the FrameLoader goes into a state in which a new page load |
| // will occur. |
| void WebFrameLoaderClient::transitionToCommittedForNewPage() { |
| makeDocumentView(); |
| } |
| |
| bool WebFrameLoaderClient::canCachePage() const { |
| // Since we manage the cache, always report this page as non-cacheable to |
| // FrameLoader. |
| return false; |
| } |
| |
| // Downloading is handled in the browser process, not WebKit. If we get to this |
| // point, our download detection code in the ResourceDispatcherHost is broken! |
| void WebFrameLoaderClient::download(ResourceHandle* handle, |
| const ResourceRequest& request, |
| const ResourceRequest& initialRequest, |
| const ResourceResponse& response) { |
| NOTREACHED(); |
| } |
| |
| PassRefPtr<Frame> WebFrameLoaderClient::createFrame( |
| const KURL& url, |
| const String& name, |
| HTMLFrameOwnerElement* owner_element, |
| const String& referrer, |
| bool allows_scrolling, |
| int margin_width, |
| int margin_height) { |
| FrameLoadRequest frame_request(ResourceRequest(url, referrer), name); |
| return webframe_->CreateChildFrame(frame_request, owner_element); |
| } |
| |
| // Utility function to convert a vector to an array of char*'s. |
| // Caller is responsible to free memory with DeleteToArray(). |
| static char** ToArray(const Vector<WebCore::String> &vector) { |
| char **rv = new char *[vector.size()+1]; |
| unsigned int index = 0; |
| for (index = 0; index < vector.size(); ++index) { |
| WebCore::CString src = vector[index].utf8(); |
| rv[index] = new char[src.length() + 1]; |
| base::strlcpy(rv[index], src.data(), src.length() + 1); |
| rv[index][src.length()] = '\0'; |
| } |
| rv[index] = 0; |
| return rv; |
| } |
| |
| static void DeleteToArray(char** arr) { |
| char **ptr = arr; |
| while (*ptr != 0) { |
| delete [] *ptr; |
| ++ptr; |
| } |
| delete [] arr; |
| } |
| |
| Widget* WebFrameLoaderClient::createPlugin(const IntSize& size, // TODO(erikkay): how do we use this? |
| HTMLPlugInElement* element, |
| const KURL&url, |
| const Vector<String>& param_names, |
| const Vector<String>& param_values, |
| const String& mime_type, |
| bool load_manually) { |
| WebViewImpl* webview = webframe_->GetWebViewImpl(); |
| WebViewDelegate* d = webview->delegate(); |
| if (!d) |
| return NULL; |
| |
| GURL gurl = webkit_glue::KURLToGURL(url); |
| std::string my_mime_type = |
| webkit_glue::CStringToStdString(mime_type.latin1()); |
| StringToLowerASCII(&my_mime_type); |
| |
| // Get the classid and version from attributes of the object. |
| std::string combined_clsid; |
| #if defined(OS_WIN) |
| std::string clsid, version; |
| if (activex_shim::IsMimeTypeActiveX(my_mime_type)) { |
| GURL url = webframe_->GetURL(); |
| for (unsigned int i = 0; i < param_names.size(); i++) { |
| String lowercase_param_name = param_names[i].lower(); |
| if (lowercase_param_name == "classid") { |
| activex_shim::GetClsidFromClassidAttribute( |
| webkit_glue::CStringToStdString(param_values[i].latin1()), &clsid); |
| } else if (lowercase_param_name == "codebase") { |
| version = activex_shim::GetVersionFromCodebaseAttribute( |
| webkit_glue::CStringToStdString(param_values[i].latin1())); |
| } |
| } |
| |
| // Attempt to map this clsid to a known NPAPI mime type if possible, failing |
| // which we attempt to load the activex shim for the clsid. |
| if (!activex_shim::GetMimeTypeForClsid(clsid, &my_mime_type)) { |
| // We need to pass the combined clsid + version to PluginsList, so that it |
| // would detect if the requested version is installed. If not, it needs |
| // to use the default plugin to update the control. |
| if (!version.empty()) |
| combined_clsid = clsid + "#" + version; |
| else |
| combined_clsid = clsid; |
| } |
| } |
| #endif |
| |
| #if defined(OS_POSIX) |
| // WebCore asks us to make a plugin even if we don't have a |
| // registered handler, with a comment saying it's so we can display |
| // the broken plugin icon. In Chromium, we normally register a |
| // fallback plugin handler that allows you to install a missing |
| // plugin. Since we don't yet have a default plugin handler, we |
| // need to return NULL here rather than going through all the |
| // plugin-creation IPCs only to discover we don't have a plugin |
| // registered, which causes a crash. |
| // TODO(evanm): remove me once we have a default plugin. |
| if (objectContentType(url, mime_type) != ObjectContentNetscapePlugin) |
| return NULL; |
| #endif |
| |
| std::string actual_mime_type; |
| WebPluginDelegate* plugin_delegate = |
| d->CreatePluginDelegate(webframe_->GetWebViewImpl(), gurl, my_mime_type, |
| combined_clsid, &actual_mime_type); |
| if (!plugin_delegate) |
| return NULL; |
| |
| if (!actual_mime_type.empty()) |
| my_mime_type = actual_mime_type; |
| |
| DCHECK(param_names.size() == param_values.size()); |
| |
| char **argn = NULL; |
| char **argv = NULL; |
| int argc = 0; |
| // There is a bug in Webkit which occurs when a plugin instance is defined |
| // with an OBJECT tag containing the "DATA" attribute". Please refer to the |
| // webkit issue http://bugs.webkit.org/show_bug.cgi?id=15457 for more info. |
| // The code below is a patch which should be taken out when a fix is |
| // available in webkit. The logic is to add the "src" attribute to the list |
| // of params if the "data" attribute exists. |
| // TODO(iyengar) : remove this when a fix is available in webkit. |
| int data_attr_index = -1; |
| int src_attr_index = -1; |
| for (unsigned int i = 0; i < param_names.size(); i++) { |
| String param_name = param_names[i].lower(); |
| if (param_name == "data") |
| data_attr_index = i; |
| else if (param_name == "src") |
| src_attr_index = i; |
| } |
| if ((data_attr_index != -1) && (src_attr_index == -1)) { |
| Vector<String> updated_param_names = param_names; |
| Vector<String> updated_param_values = param_values; |
| updated_param_names.append("src"); |
| updated_param_values.append(param_values[data_attr_index]); |
| |
| argn = ToArray(updated_param_names); |
| argv = ToArray(updated_param_values); |
| argc = static_cast<int>(updated_param_names.size()); |
| } else { |
| argn = ToArray(param_names); |
| argv = ToArray(param_values); |
| argc = static_cast<int>(param_names.size()); |
| } |
| |
| Widget* result = WebPluginImpl::Create(gurl, argn, argv, argc, element, |
| webframe_, plugin_delegate, |
| load_manually, my_mime_type); |
| |
| DeleteToArray(argn); |
| DeleteToArray(argv); |
| |
| return result; |
| } |
| |
| // This method gets called when a plugin is put in place of html content |
| // (e.g., acrobat reader). |
| void WebFrameLoaderClient::redirectDataToPlugin(Widget* pluginWidget) { |
| plugin_widget_ = static_cast<WebPluginContainer*>(pluginWidget); |
| DCHECK(plugin_widget_ != NULL); |
| } |
| |
| Widget* WebFrameLoaderClient::createJavaAppletWidget( |
| const IntSize& size, |
| HTMLAppletElement* element, |
| const KURL& url, |
| const Vector<String>& param_names, |
| const Vector<String>& param_values) { |
| return createPlugin(size, element, url, param_names, param_values, |
| "application/x-java-applet", false); |
| } |
| |
| ObjectContentType WebFrameLoaderClient::objectContentType( |
| const KURL& url, |
| const String& explicit_mime_type) { |
| // This code is based on Apple's implementation from |
| // WebCoreSupport/WebFrameBridge.mm. |
| |
| String mime_type = explicit_mime_type; |
| if (mime_type.isEmpty()) { |
| // Try to guess the MIME type based off the extension. |
| String filename = url.lastPathComponent(); |
| int extension_pos = filename.reverseFind('.'); |
| if (extension_pos >= 0) |
| mime_type = MIMETypeRegistry::getMIMETypeForPath(url.path()); |
| |
| if (mime_type.isEmpty()) |
| return ObjectContentFrame; |
| } |
| |
| if (MIMETypeRegistry::isSupportedImageMIMEType(mime_type)) |
| return ObjectContentImage; |
| |
| // If Chrome is started with the --disable-plugins switch, pluginData is null. |
| PluginData* plugin_data = webframe_->frame()->page()->pluginData(); |
| if (plugin_data && plugin_data->supportsMimeType(mime_type)) |
| return ObjectContentNetscapePlugin; |
| |
| if (MIMETypeRegistry::isSupportedNonImageMIMEType(mime_type)) |
| return ObjectContentFrame; |
| |
| return ObjectContentNone; |
| } |
| |
| String WebFrameLoaderClient::overrideMediaType() const { |
| // FIXME |
| String rv; |
| return rv; |
| } |
| |
| bool WebFrameLoaderClient::ActionSpecifiesDisposition( |
| const WebCore::NavigationAction& action, |
| WindowOpenDisposition* disposition) { |
| if ((action.type() != NavigationTypeLinkClicked) || |
| !action.event()->isMouseEvent()) |
| return false; |
| |
| const MouseEvent* event = static_cast<const MouseEvent*>(action.event()); |
| #if defined(OS_WIN) || defined(OS_LINUX) |
| const bool new_tab_modifier = (event->button() == 1) || event->ctrlKey(); |
| #elif defined(OS_MACOSX) |
| const bool new_tab_modifier = (event->button() == 1) || event->metaKey(); |
| #endif |
| const bool shift = event->shiftKey(); |
| const bool alt = event->altKey(); |
| if (!new_tab_modifier && !shift && !alt) |
| return false; |
| |
| DCHECK(disposition); |
| if (new_tab_modifier) |
| *disposition = shift ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; |
| else |
| *disposition = shift ? NEW_WINDOW : SAVE_TO_DISK; |
| return true; |
| } |
| |
| NavigationGesture WebFrameLoaderClient::NavigationGestureForLastLoad() { |
| // TODO(timsteele): userGestureHint returns too many false positives |
| // (see bug 1051891) to trust it and assign NavigationGestureUser, so |
| // for now we assign Unknown in those cases and Auto otherwise. |
| // (Issue 874811 known false negative as well). |
| return webframe_->frame()->loader()->userGestureHint() ? |
| NavigationGestureUnknown : |
| NavigationGestureAuto; |
| } |
| |
| void WebFrameLoaderClient::HandleBackForwardNavigation(const GURL& url) { |
| DCHECK(url.SchemeIs(webkit_glue::kBackForwardNavigationScheme)); |
| |
| std::string offset_str = url.ExtractFileName(); |
| int offset; |
| if (!StringToInt(offset_str, &offset)) |
| return; |
| |
| WebViewDelegate* d = webframe_->GetWebViewImpl()->delegate(); |
| if (d) |
| d->NavigateBackForwardSoon(offset); |
| } |