// 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 #include #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(interval), static_cast(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 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 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 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(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 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 WebFrameLoaderClient::createDocumentLoader( const ResourceRequest& request, const SubstituteData& data) { RefPtr 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(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 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 &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& param_names, const Vector& 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 updated_param_names = param_names; Vector 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(updated_param_names.size()); } else { argn = ToArray(param_names); argv = ToArray(param_values); argc = static_cast(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(pluginWidget); DCHECK(plugin_widget_ != NULL); } Widget* WebFrameLoaderClient::createJavaAppletWidget( const IntSize& size, HTMLAppletElement* element, const KURL& url, const Vector& param_names, const Vector& 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(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); }