diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
commit | f5b16fed647e941aa66933178da85db2860d639b (patch) | |
tree | f00e9856c04aad3b558a140955e7674add33f051 /webkit/glue/webframeloaderclient_impl.cc | |
parent | 920c091ac3ee15079194c82ae8a7a18215f3f23c (diff) | |
download | chromium_src-f5b16fed647e941aa66933178da85db2860d639b.zip chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.gz chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.bz2 |
Add webkit to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue/webframeloaderclient_impl.cc')
-rw-r--r-- | webkit/glue/webframeloaderclient_impl.cc | 1502 |
1 files changed, 1502 insertions, 0 deletions
diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc new file mode 100644 index 0000000..b1056d8 --- /dev/null +++ b/webkit/glue/webframeloaderclient_impl.cc @@ -0,0 +1,1502 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "config.h" +#include <string> +#include <vector> + +#pragma warning(push, 0) +#include "Chrome.h" +#include "CString.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "HistoryItem.h" +#include "HTMLFormElement.h" // needed by FormState.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 "PluginInfoStore.h" +#include "RefPtr.h" +#include "WindowFeatures.h" +#pragma warning(pop) + +#undef LOG +#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" +#include "webkit/activex_shim/activex_shared.h" +#include "webkit/glue/webframeloaderclient_impl.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/webdatasource_impl.h" +#include "webkit/glue/webdocumentloader_impl.h" +#include "webkit/glue/weberror_impl.h" +#include "webkit/glue/webhistoryitem_impl.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webplugin_impl.h" +#include "webkit/glue/webresponse_impl.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview_impl.h" +#include "webkit/glue/weburlrequest.h" + +using namespace WebCore; + +// 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 ref() given to it on behalf + // of the FrameWin, which accesses it via the FrameWinClient interface. + // 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 ref. + // + // May delete 'this' + webframe_->Release(); +} + +void WebFrameLoaderClient::windowObjectCleared() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->WindowObjectCleared(webframe_); +} + +void WebFrameLoaderClient::didPerformFirstNavigation() const { +} + +void WebFrameLoaderClient::registerForIconNotification(bool listen){ +} + +void WebFrameLoaderClient::unloadListenerChanged() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->OnUnloadListenerChanged(webview, webframe_); +} + +bool WebFrameLoaderClient::hasWebView() const { + return webframe_->webview_impl() != 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_->webview_impl() != NULL; +} + +bool WebFrameLoaderClient::privateBrowsingEnabled() const { + // FIXME + return false; +} + +void WebFrameLoaderClient::makeDocumentView() { + webframe_->CreateFrameView(); +} + +void WebFrameLoaderClient::makeRepresentation(DocumentLoader*) { + has_representation_ = true; +} + +void WebFrameLoaderClient::setDocumentViewFromCachedPage(CachedPage*) { + // FIXME +} +void WebFrameLoaderClient::forceLayout() { + // FIXME +} +void WebFrameLoaderClient::forceLayoutForNonHTML() { + // FIXME +} + +void WebFrameLoaderClient::setCopiesOnScroll() { + // FIXME +} + +void WebFrameLoaderClient::detachedFromParent2() { + // FIXME +} +void WebFrameLoaderClient::detachedFromParent3() { + // FIXME +} + +void WebFrameLoaderClient::detachedFromParent4() { + // Called during the last part of frame detaching, to indicate that we should + // destroy various objects (including the FrameWin). + webframe_->Closing(); +} + +void WebFrameLoaderClient::loadedFromCachedPage() { + // FIXME +} + +// 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_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + WebRequestImpl 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 SUB_RESOURCE. +// +// The important edge cases to consider when modifying this function are +// how synchronous resource loads are treated during load/unload threshold. +static ResourceType::Type DetermineResourceTypeFromLoader(DocumentLoader* loader) { + if (loader == loader->frameLoader()->provisionalDocumentLoader()) { + if (loader->frameLoader()->isLoadingMainFrame()) { + return ResourceType::MAIN_FRAME; + } else { + return ResourceType::SUB_FRAME; + } + } + return ResourceType::SUB_RESOURCE; +} + +void WebFrameLoaderClient::dispatchWillSendRequest( + DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, + const ResourceResponse& redirectResponse) { + // We set the Frame on the ResourceRequest to provide load context to the + // ResourceHandle implementation. + request.setFrame(webframe_->frame()); + + // 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.setResourceType(DetermineResourceTypeFromLoader(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.mainDocumentURL().isEmpty()) + request.setMainDocumentURL(KURL("about:blank")); + + // Give the delegate a crack at the request. + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + WebRequestImpl webreq(request); + d->WillSendRequest(webview, identifier, &webreq); + request = webreq.frame_load_request().resourceRequest(); + } +} + +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) { + + // True if the request was for the page's main frame, or a subframe. + bool is_frame = ResourceType::IsFrame(DetermineResourceTypeFromLoader(loader)); + + /* TODO(evanm): reenable this once we properly sniff XHTML from text/xml documents. + 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. + postpone_loading_data_ = is_frame && + !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 lengthReceived) { + // FIXME +} + +// 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_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidFinishLoading(webview, identifier); +} + +GURL WebFrameLoaderClient::GetAlt404PageUrl(DocumentLoader* loader) { + WebViewImpl* webview = webframe_->webview_impl(); + 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_->webview_impl(); + if (webview && webview->delegate()) { + webview->delegate()->DidFailLoadingWithError(webview, identifier, + WebErrorImpl(error)); + } +} + +void WebFrameLoaderClient::dispatchDidFinishDocumentLoad() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + // The document has now been fully loaded. + // Scan for password forms to be sent to the browser + PassRefPtr<WebCore::HTMLCollection> forms = + webframe_->frame()->document()->forms(); + + std::vector<PasswordForm> actions; + unsigned int form_count = forms->length(); + for (unsigned int i = 0; i < form_count; ++i) { + // Strange but true, sometimes item can be NULL. + WebCore::Node* item = forms->item(i); + if (item) { + WebCore::HTMLFormElement* form = + static_cast<WebCore::HTMLFormElement*>(item); + + // Honour autocomplete=off. + if (!form->autoComplete()) + continue; + + scoped_ptr<PasswordForm> data( + PasswordFormDomManager::CreatePasswordForm(form)); + if (data.get()) + actions.push_back(*data); + } + } + if (d && (actions.size() > 0)) + d->OnPasswordFormsSeen(webview, actions); + if (d) + d->DidFinishDocumentLoadForFrame(webview, webframe_); +} + +bool WebFrameLoaderClient::dispatchDidLoadResourceFromMemoryCache( + DocumentLoader* loader, + const ResourceRequest& request, + const ResourceResponse& response, + int length) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + WebRequestImpl webreq(request); + WebResponseImpl webresp(response); + return d->DidLoadResourceFromMemoryCache(webview, webreq, webresp, + webframe_); + } + + return false; +} + +void WebFrameLoaderClient::dispatchDidHandleOnloadEvents() { + WebViewImpl* webview = webframe_->webview_impl(); + 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->GetRedirectChain().empty()); + + // 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->GetRequest().GetURL()); + + // Dispatch callback + WebViewImpl* webview = webframe_->webview_impl(); + 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_->webview_impl(); + 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_->webview_impl(); + 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_->webview_impl(); + 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->GetRequest().GetURL(); + GURL chain_end = ds->GetRedirectChain().back(); + 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); + + // WebKit will re-use requests for in-page navigations, but we want to + // think of it as a new request that has a page ID in session history. + // This will set the proper page ID, etc. on the request so that the + // browser will treat it properly. + webframe_->CacheCurrentRequestInfo(ds); + } + + 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_->webview_impl(); + // 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_->webview_impl(); + 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; + } + const GURL& url = ds->GetRequest().GetURL(); + + // Since the provisional load just started, we should have not gotten + // any redirects yet. + DCHECK(ds->GetRedirectChain().empty()); + + WebViewImpl* webview = webframe_->webview_impl(); + 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. + 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_); + if (d) + d->DidCompleteClientRedirect(webview, webframe_, + expected_client_redirect_src_); + } + ds->AppendRedirect(url); + + if (d) + d->DidStartProvisionalLoadForFrame(webview, webframe_, + NavigationGestureForLastLoad()); + + // Cancel any pending loads. + if (alt_404_page_fetcher_.get()) + alt_404_page_fetcher_->Cancel(); +} + +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::dispatchDidReceiveTitle(const String& title) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + d->DidReceiveTitle(webview, webkit_glue::StringToStdWString(title), + webframe_); + } +} + +void WebFrameLoaderClient::dispatchDidCommitLoad() { + WebViewImpl* webview = webframe_->webview_impl(); + + bool is_new_navigation; + webview->DidCommitLoad(&is_new_navigation); + WebViewDelegate* d = webview->delegate(); + if (d) + d->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() { + WebViewImpl* webview = webframe_->webview_impl(); + 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() { + // FIXME: called when webkit finished layout of page. + // 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_->webview_impl(); + 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, + 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); +} + +// Conversion. +static WebNavigationType NavigationTypeToWebNavigationType( + WebCore::NavigationType t) { + switch (t) { + case WebCore::NavigationTypeLinkClicked: + return WebNavigationTypeLinkClicked; + case WebCore::NavigationTypeFormSubmitted: + return WebNavigationTypeFormSubmitted; + case WebCore::NavigationTypeBackForward: + return WebNavigationTypeBackForward; + case WebCore::NavigationTypeReload: + return WebNavigationTypeReload; + case WebCore::NavigationTypeFormResubmitted: + return WebNavigationTypeFormResubmitted; + default: + case WebCore::NavigationTypeOther: + return WebNavigationTypeOther; + } +} + +void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction( + WebCore::FramePolicyFunction function, + const WebCore::NavigationAction& action, + const WebCore::ResourceRequest& request) { + PolicyAction policy_action = PolicyUse; + + WebViewImpl* wv = webframe_->webview_impl(); + WebViewDelegate* d = wv->delegate(); + // It is valid for this function to be invoked in code paths where the + // the webview is closed. + if (d) { + 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) { + bool is_redirect = !ds->GetRedirectChain().empty(); + + WebNavigationType webnav_type = + NavigationTypeToWebNavigationType(action.type()); + + disposition = d->DispositionForNavigationAction( + wv, webframe_, &ds->GetRequest(), 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 { + d->OpenURL(webframe_->webview_impl(), + webkit_glue::KURLToGURL(request.url()), + 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) { + SearchableFormData* form_data = SearchableFormData::Create(form_ref->form()); + WebDocumentLoaderImpl* loader = static_cast<WebDocumentLoaderImpl*>( + webframe_->frame()->loader()->provisionalDocumentLoader()); + // Don't free the SearchableFormData, the loader will do that. + loader->set_searchable_form_data(form_data); + + PasswordForm* pass_data = + PasswordFormDomManager::CreatePasswordForm(form_ref->form()); + // Don't free the PasswordFormData, the loader will do that. + loader->set_password_form_data(pass_data); + + loader->set_form_submit(true); + + (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::clearUnarchivingState(DocumentLoader*) { + // FIXME +} + +void WebFrameLoaderClient::postProgressStartedNotification() { + if (hasWebView()) { + WebViewDelegate* d = webframe_->webview_impl()->delegate(); + if (d) + d->DidStartLoading(webframe_->webview_impl()); + } +} + +void WebFrameLoaderClient::postProgressEstimateChangedNotification() { + // FIXME +} + +void WebFrameLoaderClient::postProgressFinishedNotification() { + // TODO(ericroman): why might webframe_->webview_impl be null? + // http://b/1234461 + if (hasWebView()) { + WebViewDelegate* d = webframe_->webview_impl()->delegate(); + + if (d) + d->DidStopLoading(webframe_->webview_impl()); + } +} + +void WebFrameLoaderClient::setMainFrameDocumentReady(bool ready) { + // FIXME +} + +// Creates a new connection and begins downloading from that (contrast this +// with |download|). +void WebFrameLoaderClient::startDownload(const ResourceRequest& request) { + WebViewDelegate* d = webframe_->webview_impl()->delegate(); + if (d) { + const GURL url(webkit_glue::KURLToGURL(request.url())); + const GURL referrer(webkit_glue::StringToStdWString(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::finalSetupForReplace(DocumentLoader*) { + // FIXME +} + +void WebFrameLoaderClient::updateGlobalHistoryForStandardLoad(const KURL& kurl) { +} + +void WebFrameLoaderClient::updateGlobalHistoryForReload(const KURL&) { + // FIXME: this is for updating the visit time. +} + +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(); +} + +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; +} + +void WebFrameLoaderClient::setDefersLoading(bool) { + // FIXME +} + +bool WebFrameLoaderClient::willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL& originalURL) const { + // FIXME + return false; +} +bool WebFrameLoaderClient::isArchiveLoadPending(ResourceLoader*) const { + // FIXME + return false; +} +void WebFrameLoaderClient::cancelPendingArchiveLoad(ResourceLoader*) { + // FIXME +} +void WebFrameLoaderClient::clearArchivedResources() { + // FIXME +} + +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. + + if (mime_util::IsSupportedMimeType( + WideToASCII(webkit_glue::StringToStdWString(mime_type)))) + return true; + + // 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 PluginInfoStore::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(L"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. + 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) { + WebDocumentLoaderImpl* loader = new WebDocumentLoaderImpl(request, data); + + // Attach a datasource to the loader as a way of accessing requests. + WebDataSourceImpl* datasource = + WebDataSourceImpl::CreateInstance(webframe_, loader); + loader->SetDataSource(datasource); + + webframe_->CacheCurrentRequestInfo(datasource); + + return loader; +} + +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_->webview_impl()->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( + webframe_->webview_impl()->GetPreferences().user_agent); +} + +void WebFrameLoaderClient::savePlatformDataToCachedPage(WebCore::CachedPage*) { + NOTREACHED() << "Page cache should be disabled"; +} + +void WebFrameLoaderClient::transitionToCommittedFromCachedPage(WebCore::CachedPage*) { + ASSERT_NOT_REACHED(); +} + +void WebFrameLoaderClient::transitionToCommittedForNewPage() { + makeDocumentView(); +} + +bool WebFrameLoaderClient::canCachePage() const { + NOTREACHED() << "Page cache should be disabled"; + 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); + + Frame* new_frame = NULL; + if (webframe_) + webframe_->CreateChildFrame(frame_request, owner_element, allows_scrolling, + margin_width, margin_height, new_frame); + return new_frame; +} + +// 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]; + strncpy_s(rv[index], src.length() + 1, src.data(), _TRUNCATE); + 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? + Element *element, const KURL &url, + const Vector<String> ¶m_names, + const Vector<String> ¶m_values, + const String &mime_type, + bool load_manually) { + WebViewImpl* webview = webframe_->webview_impl(); + 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 clsid, version, combined_clsid; + 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())); + } + } + // We only allowed specific ActiveX controls to run from certain websites. + if (!activex_shim::IsActiveXAllowed(clsid, url)) + return NULL; + // 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; + } + + std::string actual_mime_type; + WebPluginDelegate* plugin_delegate = + d->CreatePluginDelegate(webframe_->webview_impl(), 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); + + 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, + Element *element, const KURL &url, + const Vector<String> ¶m_names, + const Vector<String> ¶m_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 (PluginInfoStore::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()); + const bool middle_or_ctrl = (event->button() == 1) || event->ctrlKey(); + const bool shift = event->shiftKey(); + const bool alt = event->altKey(); + if (!middle_or_ctrl && !shift && !alt) + return false; + + DCHECK(disposition); + if (middle_or_ctrl) + *disposition = shift ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + else + *disposition = shift ? NEW_WINDOW : SAVE_TO_DISK; + return true; +} |