| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * Copyright (C) 2011 Apple 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 "FrameLoaderClientImpl.h" |
| |
| #include "BackForwardListChromium.h" |
| #include "Chrome.h" |
| #include "Document.h" |
| #include "DocumentLoader.h" |
| #include "FormState.h" |
| #include "FrameLoader.h" |
| #include "FrameLoadRequest.h" |
| #include "FrameNetworkingContextImpl.h" |
| #include "FrameView.h" |
| #include "HTTPParsers.h" |
| #include "HistoryItem.h" |
| #include "HitTestResult.h" |
| #include "HTMLAppletElement.h" |
| #include "HTMLFormElement.h" // needed by FormState.h |
| #include "HTMLNames.h" |
| #include "MIMETypeRegistry.h" |
| #include "MouseEvent.h" |
| #include "Page.h" |
| #include "PlatformString.h" |
| #include "PluginData.h" |
| #include "PluginDataChromium.h" |
| #include "ProgressTracker.h" |
| #include "Settings.h" |
| #include "StringExtras.h" |
| #include "WebDataSourceImpl.h" |
| #include "WebDevToolsAgentPrivate.h" |
| #include "WebFormElement.h" |
| #include "WebFrameClient.h" |
| #include "WebFrameImpl.h" |
| #include "WebKit.h" |
| #include "WebKitClient.h" |
| #include "WebMimeRegistry.h" |
| #include "WebNode.h" |
| #include "WebPlugin.h" |
| #include "WebPluginContainerImpl.h" |
| #include "WebPluginLoadObserver.h" |
| #include "WebPluginParams.h" |
| #include "WebSecurityOrigin.h" |
| #include "WebURL.h" |
| #include "WebURLError.h" |
| #include "WebVector.h" |
| #include "WebViewClient.h" |
| #include "WebViewImpl.h" |
| #include "WindowFeatures.h" |
| #include "WrappedResourceRequest.h" |
| #include "WrappedResourceResponse.h" |
| #include <wtf/text/CString.h> |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| // Domain for internal error codes. |
| static const char internalErrorDomain[] = "WebKit"; |
| |
| // An internal error code. Used to note a policy change error resulting from |
| // dispatchDecidePolicyForMIMEType not passing the PolicyUse option. |
| enum { |
| PolicyChangeError = -10000, |
| }; |
| |
| FrameLoaderClientImpl::FrameLoaderClientImpl(WebFrameImpl* frame) |
| : m_webFrame(frame) |
| , m_hasRepresentation(false) |
| , m_sentInitialResponseToPlugin(false) |
| , m_nextNavigationPolicy(WebNavigationPolicyIgnore) |
| { |
| } |
| |
| FrameLoaderClientImpl::~FrameLoaderClientImpl() |
| { |
| } |
| |
| void FrameLoaderClientImpl::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. |
| m_webFrame->closing(); |
| m_webFrame->deref(); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didClearWindowObject(m_webFrame); |
| |
| WebViewImpl* webview = m_webFrame->viewImpl(); |
| if (webview->devToolsAgentPrivate()) |
| webview->devToolsAgentPrivate()->didClearWindowObject(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::documentElementAvailable() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didCreateDocumentElement(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::didCreateScriptContextForFrame() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didCreateScriptContext(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::didDestroyScriptContextForFrame() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didDestroyScriptContext(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::didCreateIsolatedScriptContext() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didCreateIsolatedScriptContext(m_webFrame); |
| } |
| |
| bool FrameLoaderClientImpl::allowScriptExtension(const String& extensionName, |
| int extensionGroup) |
| { |
| if (m_webFrame->client()) |
| return m_webFrame->client()->allowScriptExtension(m_webFrame, extensionName, extensionGroup); |
| return false; |
| } |
| |
| void FrameLoaderClientImpl::didPerformFirstNavigation() const |
| { |
| } |
| |
| void FrameLoaderClientImpl::registerForIconNotification(bool) |
| { |
| } |
| |
| void FrameLoaderClientImpl::didChangeScrollOffset() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didChangeScrollOffset(m_webFrame); |
| } |
| |
| bool FrameLoaderClientImpl::allowJavaScript(bool enabledPerSettings) |
| { |
| if (m_webFrame->client()) |
| return m_webFrame->client()->allowScript(m_webFrame, enabledPerSettings); |
| |
| return enabledPerSettings; |
| } |
| |
| bool FrameLoaderClientImpl::allowPlugins(bool enabledPerSettings) |
| { |
| if (m_webFrame->client()) |
| return m_webFrame->client()->allowPlugins(m_webFrame, enabledPerSettings); |
| |
| return enabledPerSettings; |
| } |
| |
| bool FrameLoaderClientImpl::allowImages(bool enabledPerSettings) |
| { |
| if (m_webFrame->client()) |
| return m_webFrame->client()->allowImages(m_webFrame, enabledPerSettings); |
| |
| return enabledPerSettings; |
| } |
| |
| void FrameLoaderClientImpl::didNotAllowScript() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didNotAllowScript(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::didNotAllowPlugins() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didNotAllowPlugins(m_webFrame); |
| } |
| |
| bool FrameLoaderClientImpl::hasWebView() const |
| { |
| return m_webFrame->viewImpl(); |
| } |
| |
| bool FrameLoaderClientImpl::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 m_webFrame->viewImpl(); |
| } |
| |
| void FrameLoaderClientImpl::makeDocumentView() |
| { |
| m_webFrame->createFrameView(); |
| } |
| |
| void FrameLoaderClientImpl::makeRepresentation(DocumentLoader*) |
| { |
| m_hasRepresentation = true; |
| } |
| |
| void FrameLoaderClientImpl::forceLayout() |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::forceLayoutForNonHTML() |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::setCopiesOnScroll() |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::detachedFromParent2() |
| { |
| // Nothing to do here. |
| } |
| |
| void FrameLoaderClientImpl::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. |
| m_webFrame->frame()->script()->proxy()->clearForClose(); |
| |
| // Alert the client that the frame is being detached. This is the last |
| // chance we have to communicate with the client. |
| if (m_webFrame->client()) |
| m_webFrame->client()->frameDetached(m_webFrame); |
| |
| // Stop communicating with the WebFrameClient at this point since we are no |
| // longer associated with the Page. |
| m_webFrame->setClient(0); |
| } |
| |
| // 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 FrameLoaderClientImpl::assignIdentifierToInitialRequest( |
| unsigned long identifier, DocumentLoader* loader, |
| const ResourceRequest& request) |
| { |
| if (m_webFrame->client()) { |
| WrappedResourceRequest webreq(request); |
| m_webFrame->client()->assignIdentifierToRequest( |
| m_webFrame, identifier, webreq); |
| } |
| } |
| |
| // If the request being loaded by |loader| is a frame, update the ResourceType. |
| // 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 void setTargetTypeFromLoader(ResourceRequest& request, DocumentLoader* loader) |
| { |
| if (loader == loader->frameLoader()->provisionalDocumentLoader()) { |
| ResourceRequest::TargetType type; |
| if (loader->frameLoader()->isLoadingMainFrame()) |
| type = ResourceRequest::TargetIsMainFrame; |
| else |
| type = ResourceRequest::TargetIsSubframe; |
| request.setTargetType(type); |
| } |
| } |
| |
| void FrameLoaderClientImpl::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. |
| setTargetTypeFromLoader(request, loader); |
| |
| // Avoid repeating a form submission when navigating back or forward. |
| if (loader == loader->frameLoader()->provisionalDocumentLoader() |
| && request.httpMethod() == "POST" |
| && isBackForwardLoadType(loader->frameLoader()->loadType())) |
| request.setCachePolicy(ReturnCacheDataDontLoad); |
| } |
| |
| // 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(ParsedURLString, "about:blank")); |
| if (request.firstPartyForCookies().isEmpty()) |
| request.setFirstPartyForCookies(KURL(ParsedURLString, "about:blank")); |
| |
| // Give the WebFrameClient a crack at the request. |
| if (m_webFrame->client()) { |
| WrappedResourceRequest webreq(request); |
| WrappedResourceResponse webresp(redirectResponse); |
| m_webFrame->client()->willSendRequest( |
| m_webFrame, identifier, webreq, webresp); |
| } |
| } |
| |
| bool FrameLoaderClientImpl::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 FrameLoaderClientImpl::dispatchDidReceiveAuthenticationChallenge( |
| DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidCancelAuthenticationChallenge( |
| DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidReceiveResponse(DocumentLoader* loader, |
| unsigned long identifier, |
| const ResourceResponse& response) |
| { |
| if (m_webFrame->client()) { |
| WrappedResourceResponse webresp(response); |
| m_webFrame->client()->didReceiveResponse(m_webFrame, identifier, webresp); |
| } |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidReceiveContentLength( |
| DocumentLoader* loader, |
| unsigned long identifier, |
| int dataLength) |
| { |
| } |
| |
| // Called when a particular resource load completes |
| void FrameLoaderClientImpl::dispatchDidFinishLoading(DocumentLoader* loader, |
| unsigned long identifier) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didFinishResourceLoad(m_webFrame, identifier); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidFailLoading(DocumentLoader* loader, |
| unsigned long identifier, |
| const ResourceError& error) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didFailResourceLoad(m_webFrame, identifier, error); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidFinishDocumentLoad() |
| { |
| // A frame may be reused. This call ensures we don't hold on to our password |
| // listeners and their associated HTMLInputElements. |
| m_webFrame->clearPasswordListeners(); |
| |
| if (m_webFrame->client()) |
| m_webFrame->client()->didFinishDocumentLoad(m_webFrame); |
| } |
| |
| bool FrameLoaderClientImpl::dispatchDidLoadResourceFromMemoryCache( |
| DocumentLoader* loader, |
| const ResourceRequest& request, |
| const ResourceResponse& response, |
| int length) |
| { |
| if (m_webFrame->client()) { |
| WrappedResourceRequest webreq(request); |
| WrappedResourceResponse webresp(response); |
| m_webFrame->client()->didLoadResourceFromMemoryCache( |
| m_webFrame, webreq, webresp); |
| } |
| return false; // Do not suppress remaining notifications |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidHandleOnloadEvents() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didHandleOnloadEvents(m_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 FrameLoaderClientImpl::dispatchDidReceiveServerRedirectForProvisionalLoad() |
| { |
| WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl(); |
| if (!ds) { |
| // Got a server redirect when there is no provisional DS! |
| ASSERT_NOT_REACHED(); |
| return; |
| } |
| |
| // The server redirect may have been blocked. |
| if (ds->request().isNull()) |
| return; |
| |
| // A provisional load should have started already, which should have put an |
| // entry in our redirect chain. |
| ASSERT(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()); |
| |
| if (m_webFrame->client()) |
| m_webFrame->client()->didReceiveServerRedirectForProvisionalLoad(m_webFrame); |
| } |
| |
| // Called on both success and failure of a client redirect. |
| void FrameLoaderClientImpl::dispatchDidCancelClientRedirect() |
| { |
| // No longer expecting a client redirect. |
| if (m_webFrame->client()) { |
| m_expectedClientRedirectSrc = KURL(); |
| m_expectedClientRedirectDest = KURL(); |
| m_webFrame->client()->didCancelClientRedirect(m_webFrame); |
| } |
| |
| // No need to clear the redirect chain, since that data source has already |
| // been deleted by the time this function is called. |
| } |
| |
| void FrameLoaderClientImpl::dispatchWillPerformClientRedirect( |
| const KURL& url, |
| double interval, |
| double fireDate) |
| { |
| // 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. |
| m_expectedClientRedirectSrc = m_webFrame->url(); |
| m_expectedClientRedirectDest = url; |
| |
| // FIXME: 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 (m_expectedClientRedirectDest.isLocalFile() |
| && m_expectedClientRedirectSrc.protocolInHTTPFamily()) { |
| m_expectedClientRedirectSrc = KURL(); |
| m_expectedClientRedirectDest = KURL(); |
| return; |
| } |
| |
| if (m_webFrame->client()) { |
| m_webFrame->client()->willPerformClientRedirect( |
| m_webFrame, |
| m_expectedClientRedirectSrc, |
| m_expectedClientRedirectDest, |
| static_cast<unsigned int>(interval), |
| static_cast<unsigned int>(fireDate)); |
| } |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidNavigateWithinPage() |
| { |
| // Anchor fragment navigations are not normal loads, so we need to synthesize |
| // some events for our delegate. |
| WebViewImpl* webView = m_webFrame->viewImpl(); |
| |
| // Flag of whether frame loader is completed. Generate didStartLoading and |
| // didStopLoading only when loader is completed so that we don't fire |
| // them for fragment redirection that happens in window.onload handler. |
| // See https://bugs.webkit.org/show_bug.cgi?id=31838 |
| bool loaderCompleted = |
| !webView->page()->mainFrame()->loader()->activeDocumentLoader()->isLoadingInAPISense(); |
| |
| // Generate didStartLoading if loader is completed. |
| if (webView->client() && loaderCompleted) |
| webView->client()->didStartLoading(); |
| |
| // We need to classify some hash changes as client redirects. |
| // FIXME: It seems wrong that the currentItem can sometimes be null. |
| HistoryItem* currentItem = m_webFrame->frame()->loader()->history()->currentItem(); |
| bool isHashChange = !currentItem || !currentItem->stateObject(); |
| |
| WebDataSourceImpl* ds = m_webFrame->dataSourceImpl(); |
| ASSERT(ds); // Should not be null when navigating to a reference fragment! |
| if (ds) { |
| KURL url = ds->request().url(); |
| KURL chainEnd; |
| if (ds->hasRedirectChain()) { |
| chainEnd = ds->endOfRedirectChain(); |
| ds->clearRedirectChain(); |
| } |
| |
| if (isHashChange) { |
| // Figure out if this location change is because of a JS-initiated |
| // client redirect (e.g onload/setTimeout document.location.href=). |
| // FIXME: (b/1085325, b/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 wasClientRedirect = |
| (url == m_expectedClientRedirectDest && chainEnd == m_expectedClientRedirectSrc) |
| || !m_webFrame->isProcessingUserGesture(); |
| |
| if (wasClientRedirect) { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didCompleteClientRedirect(m_webFrame, chainEnd); |
| ds->appendRedirect(chainEnd); |
| // Make sure we clear the expected redirect since we just effectively |
| // completed it. |
| m_expectedClientRedirectSrc = KURL(); |
| m_expectedClientRedirectDest = KURL(); |
| } |
| } |
| |
| // 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 isNewNavigation; |
| webView->didCommitLoad(&isNewNavigation); |
| if (m_webFrame->client()) |
| m_webFrame->client()->didNavigateWithinPage(m_webFrame, isNewNavigation); |
| |
| // Generate didStopLoading if loader is completed. |
| if (webView->client() && loaderCompleted) |
| webView->client()->didStopLoading(); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidChangeLocationWithinPage() |
| { |
| if (m_webFrame) |
| m_webFrame->client()->didChangeLocationWithinPage(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidPushStateWithinPage() |
| { |
| dispatchDidNavigateWithinPage(); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidReplaceStateWithinPage() |
| { |
| dispatchDidNavigateWithinPage(); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidPopStateWithinPage() |
| { |
| // Ignored since dispatchDidNavigateWithinPage was already called. |
| } |
| |
| void FrameLoaderClientImpl::dispatchWillClose() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->willClose(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidReceiveIcon() |
| { |
| // The icon database is disabled, so this should never be called. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void FrameLoaderClientImpl::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 = m_webFrame->provisionalDataSourceImpl(); |
| if (!ds) { |
| ASSERT_NOT_REACHED(); |
| return; |
| } |
| KURL url = ds->request().url(); |
| |
| // Since the provisional load just started, we should have not gotten |
| // any redirects yet. |
| ASSERT(!ds->hasRedirectChain()); |
| |
| // 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 completingClientRedirect = false; |
| if (m_expectedClientRedirectSrc.isValid()) { |
| // m_expectedClientRedirectDest could be something like |
| // "javascript:history.go(-1)" thus we need to exclude url starts with |
| // "javascript:". See bug: 1080873 |
| if (m_expectedClientRedirectDest.protocolIs("javascript") |
| || m_expectedClientRedirectDest == url) { |
| ds->appendRedirect(m_expectedClientRedirectSrc); |
| completingClientRedirect = true; |
| } else { |
| // Any pending redirect is no longer in progress. This can happen |
| // if the navigation was canceled with PolicyIgnore, or if the |
| // redirect was scheduled on the wrong frame (e.g., due to a form |
| // submission targeted to _blank, as in http://webkit.org/b/44079). |
| m_expectedClientRedirectSrc = KURL(); |
| m_expectedClientRedirectDest = KURL(); |
| } |
| } |
| ds->appendRedirect(url); |
| |
| if (m_webFrame->client()) { |
| // Whatever information didCompleteClientRedirect contains should only |
| // be considered relevant until the next provisional load has started. |
| // So we first tell the client that the load started, and then tell it |
| // about the client redirect the load is responsible for completing. |
| m_webFrame->client()->didStartProvisionalLoad(m_webFrame); |
| if (completingClientRedirect) { |
| m_webFrame->client()->didCompleteClientRedirect( |
| m_webFrame, m_expectedClientRedirectSrc); |
| } |
| } |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidReceiveTitle(const StringWithDirection& title) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didReceiveTitle(m_webFrame, title.string(), title.direction() == LTR ? WebTextDirectionLeftToRight : WebTextDirectionRightToLeft); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidChangeIcons() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didChangeIcons(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidCommitLoad() |
| { |
| WebViewImpl* webview = m_webFrame->viewImpl(); |
| bool isNewNavigation; |
| webview->didCommitLoad(&isNewNavigation); |
| |
| if (m_webFrame->client()) |
| m_webFrame->client()->didCommitProvisionalLoad(m_webFrame, isNewNavigation); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidFailProvisionalLoad( |
| const ResourceError& error) |
| { |
| |
| // If a policy change occured, then we do not want to inform the plugin |
| // delegate. See http://b/907789 for details. FIXME: This means the |
| // plugin won't receive NPP_URLNotify, which seems like it could result in |
| // a memory leak in the plugin!! |
| if (error.domain() == internalErrorDomain |
| && error.errorCode() == PolicyChangeError) { |
| m_webFrame->didFail(cancelledError(error.failingURL()), true); |
| return; |
| } |
| |
| OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); |
| m_webFrame->didFail(error, true); |
| if (observer) |
| observer->didFailLoading(error); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidFailLoad(const ResourceError& error) |
| { |
| OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); |
| m_webFrame->didFail(error, false); |
| if (observer) |
| observer->didFailLoading(error); |
| |
| // 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 FrameLoaderClientImpl::dispatchDidFinishLoad() |
| { |
| OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); |
| |
| if (m_webFrame->client()) |
| m_webFrame->client()->didFinishLoad(m_webFrame); |
| |
| if (observer) |
| observer->didFinishLoading(); |
| |
| // 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 FrameLoaderClientImpl::dispatchDidFirstLayout() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didFirstLayout(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidFirstVisuallyNonEmptyLayout() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didFirstVisuallyNonEmptyLayout(m_webFrame); |
| } |
| |
| Frame* FrameLoaderClientImpl::dispatchCreatePage(const NavigationAction& action) |
| { |
| struct WindowFeatures features; |
| Page* newPage = m_webFrame->frame()->page()->chrome()->createWindow( |
| m_webFrame->frame(), FrameLoadRequest(m_webFrame->frame()->document()->securityOrigin()), |
| features, action); |
| |
| // Make sure that we have a valid disposition. This should have been set in |
| // the preceeding call to dispatchDecidePolicyForNewWindowAction. |
| ASSERT(m_nextNavigationPolicy != WebNavigationPolicyIgnore); |
| WebNavigationPolicy policy = m_nextNavigationPolicy; |
| m_nextNavigationPolicy = WebNavigationPolicyIgnore; |
| |
| // createWindow can return null (e.g., popup blocker denies the window). |
| if (!newPage) |
| return 0; |
| |
| WebViewImpl::fromPage(newPage)->setInitialNavigationPolicy(policy); |
| return newPage->mainFrame(); |
| } |
| |
| void FrameLoaderClientImpl::dispatchShow() |
| { |
| WebViewImpl* webView = m_webFrame->viewImpl(); |
| if (webView && webView->client()) |
| webView->client()->show(webView->initialNavigationPolicy()); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDecidePolicyForResponse( |
| FramePolicyFunction function, |
| const ResourceResponse& response, |
| const ResourceRequest&) |
| { |
| PolicyAction action; |
| |
| int statusCode = response.httpStatusCode(); |
| if (statusCode == 204 || statusCode == 205) { |
| // The server does not want us to replace the page contents. |
| action = PolicyIgnore; |
| } else if (WebCore::contentDispositionType(response.httpHeaderField("Content-Disposition")) == WebCore::ContentDispositionAttachment) { |
| // 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(response.mimeType())) { |
| // Make sure that we can actually handle this type internally. |
| action = PolicyIgnore; |
| } else { |
| // OK, we will render this page. |
| action = PolicyUse; |
| } |
| |
| // NOTE: PolicyChangeError will be generated when action is not PolicyUse. |
| (m_webFrame->frame()->loader()->policyChecker()->*function)(action); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDecidePolicyForNewWindowAction( |
| FramePolicyFunction function, |
| const NavigationAction& action, |
| const ResourceRequest& request, |
| PassRefPtr<FormState> formState, |
| const String& frameName) |
| { |
| WebNavigationPolicy navigationPolicy; |
| if (!actionSpecifiesNavigationPolicy(action, &navigationPolicy)) |
| navigationPolicy = WebNavigationPolicyNewForegroundTab; |
| |
| PolicyAction policyAction; |
| if (navigationPolicy == WebNavigationPolicyDownload) |
| policyAction = PolicyDownload; |
| else { |
| policyAction = 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. |
| m_nextNavigationPolicy = navigationPolicy; |
| } |
| (m_webFrame->frame()->loader()->policyChecker()->*function)(policyAction); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDecidePolicyForNavigationAction( |
| FramePolicyFunction function, |
| const NavigationAction& action, |
| const ResourceRequest& request, |
| PassRefPtr<FormState> formState) { |
| PolicyAction policyAction = PolicyIgnore; |
| |
| // 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 (m_webFrame->client() && !request.url().isNull()) { |
| WebNavigationPolicy navigationPolicy = WebNavigationPolicyCurrentTab; |
| actionSpecifiesNavigationPolicy(action, &navigationPolicy); |
| |
| // Give the delegate a chance to change the navigation policy. |
| const WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl(); |
| if (ds) { |
| KURL url = ds->request().url(); |
| ASSERT(!url.protocolIs(backForwardNavigationScheme)); |
| |
| bool isRedirect = ds->hasRedirectChain(); |
| |
| WebNavigationType webnavType = |
| WebDataSourceImpl::toWebNavigationType(action.type()); |
| |
| RefPtr<Node> node; |
| for (const Event* event = action.event(); event; event = event->underlyingEvent()) { |
| if (event->isMouseEvent()) { |
| const MouseEvent* mouseEvent = |
| static_cast<const MouseEvent*>(event); |
| node = m_webFrame->frame()->eventHandler()->hitTestResultAtPoint( |
| mouseEvent->absoluteLocation(), false).innerNonSharedNode(); |
| break; |
| } |
| } |
| WebNode originatingNode(node); |
| |
| navigationPolicy = m_webFrame->client()->decidePolicyForNavigation( |
| m_webFrame, ds->request(), webnavType, originatingNode, |
| navigationPolicy, isRedirect); |
| } |
| |
| if (navigationPolicy == WebNavigationPolicyCurrentTab) |
| policyAction = PolicyUse; |
| else if (navigationPolicy == WebNavigationPolicyDownload) |
| policyAction = PolicyDownload; |
| else { |
| if (navigationPolicy != WebNavigationPolicyIgnore) { |
| WrappedResourceRequest webreq(request); |
| m_webFrame->client()->loadURLExternally(m_webFrame, webreq, navigationPolicy); |
| } |
| policyAction = PolicyIgnore; |
| } |
| } |
| |
| (m_webFrame->frame()->loader()->policyChecker()->*function)(policyAction); |
| } |
| |
| void FrameLoaderClientImpl::cancelPolicyCheck() |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::dispatchUnableToImplementPolicy(const ResourceError& error) |
| { |
| m_webFrame->client()->unableToImplementPolicyWithError(m_webFrame, error); |
| } |
| |
| void FrameLoaderClientImpl::dispatchWillSendSubmitEvent(HTMLFormElement* form) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->willSendSubmitEvent(m_webFrame, WebFormElement(form)); |
| } |
| |
| void FrameLoaderClientImpl::dispatchWillSubmitForm(FramePolicyFunction function, |
| PassRefPtr<FormState> formState) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->willSubmitForm(m_webFrame, WebFormElement(formState->form())); |
| (m_webFrame->frame()->loader()->policyChecker()->*function)(PolicyUse); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidLoadMainResource(DocumentLoader*) |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::revertToProvisionalState(DocumentLoader*) |
| { |
| m_hasRepresentation = true; |
| } |
| |
| void FrameLoaderClientImpl::setMainDocumentError(DocumentLoader*, |
| const ResourceError& error) |
| { |
| if (m_pluginWidget.get()) { |
| if (m_sentInitialResponseToPlugin) { |
| m_pluginWidget->didFailLoading(error); |
| m_sentInitialResponseToPlugin = false; |
| } |
| m_pluginWidget = 0; |
| } |
| } |
| |
| void FrameLoaderClientImpl::postProgressStartedNotification() |
| { |
| WebViewImpl* webview = m_webFrame->viewImpl(); |
| if (webview && webview->client()) |
| webview->client()->didStartLoading(); |
| } |
| |
| void FrameLoaderClientImpl::postProgressEstimateChangedNotification() |
| { |
| WebViewImpl* webview = m_webFrame->viewImpl(); |
| if (webview && webview->client()) { |
| webview->client()->didChangeLoadProgress( |
| m_webFrame, m_webFrame->frame()->page()->progress()->estimatedProgress()); |
| } |
| |
| } |
| |
| void FrameLoaderClientImpl::postProgressFinishedNotification() |
| { |
| // FIXME: why might the webview be null? http://b/1234461 |
| WebViewImpl* webview = m_webFrame->viewImpl(); |
| if (webview && webview->client()) |
| webview->client()->didStopLoading(); |
| } |
| |
| void FrameLoaderClientImpl::setMainFrameDocumentReady(bool ready) |
| { |
| // FIXME |
| } |
| |
| // Creates a new connection and begins downloading from that (contrast this |
| // with |download|). |
| void FrameLoaderClientImpl::startDownload(const ResourceRequest& request) |
| { |
| if (m_webFrame->client()) { |
| WrappedResourceRequest webreq(request); |
| m_webFrame->client()->loadURLExternally( |
| m_webFrame, webreq, WebNavigationPolicyDownload); |
| } |
| } |
| |
| void FrameLoaderClientImpl::willChangeTitle(DocumentLoader*) |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::didChangeTitle(DocumentLoader*) |
| { |
| // FIXME |
| } |
| |
| // Called whenever data is received. |
| void FrameLoaderClientImpl::committedLoad(DocumentLoader* loader, const char* data, int length) |
| { |
| if (!m_pluginWidget.get()) { |
| if (m_webFrame->client()) { |
| bool preventDefault = false; |
| m_webFrame->client()->didReceiveDocumentData(m_webFrame, data, length, preventDefault); |
| if (!preventDefault) |
| m_webFrame->commitDocumentData(data, length); |
| } |
| } |
| |
| // If we are sending data to MediaDocument, we should stop here |
| // and cancel the request. |
| if (m_webFrame->frame()->document()->isMediaDocument()) |
| loader->cancelMainResourceLoad(pluginWillHandleLoadError(loader->response())); |
| |
| // The plugin widget could have been created in the m_webFrame->DidReceiveData |
| // function. |
| if (m_pluginWidget.get()) { |
| if (!m_sentInitialResponseToPlugin) { |
| m_sentInitialResponseToPlugin = true; |
| m_pluginWidget->didReceiveResponse( |
| m_webFrame->frame()->loader()->activeDocumentLoader()->response()); |
| } |
| |
| // It's possible that the above call removed the pointer to the plugin, so |
| // check before calling it. |
| if (m_pluginWidget.get()) |
| m_pluginWidget->didReceiveData(data, length); |
| } |
| } |
| |
| void FrameLoaderClientImpl::finishedLoading(DocumentLoader* dl) |
| { |
| if (m_pluginWidget.get()) { |
| m_pluginWidget->didFinishLoading(); |
| m_pluginWidget = 0; |
| m_sentInitialResponseToPlugin = 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 (m_hasRepresentation) |
| dl->writer()->setEncoding("", false); |
| } |
| } |
| |
| void FrameLoaderClientImpl::updateGlobalHistory() |
| { |
| } |
| |
| void FrameLoaderClientImpl::updateGlobalHistoryRedirectLinks() |
| { |
| } |
| |
| bool FrameLoaderClientImpl::shouldGoToHistoryItem(HistoryItem* item) const |
| { |
| const KURL& url = item->url(); |
| if (!url.protocolIs(backForwardNavigationScheme)) |
| return true; |
| |
| // Else, we'll punt this history navigation to the embedder. It is |
| // necessary that we intercept this here, well before the FrameLoader |
| // has made any state changes for this history traversal. |
| |
| bool ok; |
| int offset = url.lastPathComponent().toIntStrict(&ok); |
| if (!ok) { |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| |
| WebViewImpl* webview = m_webFrame->viewImpl(); |
| if (webview->client()) |
| webview->client()->navigateBackForwardSoon(offset); |
| |
| return false; |
| } |
| |
| bool FrameLoaderClientImpl::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) const |
| { |
| // Don't stop loading for pseudo-back-forward URLs, since they will get |
| // translated and then pass through again. |
| const KURL& url = targetItem->url(); |
| return !url.protocolIs(backForwardNavigationScheme); |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidAddBackForwardItem(HistoryItem*) const |
| { |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidRemoveBackForwardItem(HistoryItem*) const |
| { |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidChangeBackForwardIndex() const |
| { |
| } |
| |
| void FrameLoaderClientImpl::didDisplayInsecureContent() |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didDisplayInsecureContent(m_webFrame); |
| } |
| |
| void FrameLoaderClientImpl::didRunInsecureContent(SecurityOrigin* origin, const KURL& insecureURL) |
| { |
| if (m_webFrame->client()) |
| m_webFrame->client()->didRunInsecureContent(m_webFrame, WebSecurityOrigin(origin), insecureURL); |
| } |
| |
| ResourceError FrameLoaderClientImpl::blockedError(const ResourceRequest&) |
| { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| ResourceError FrameLoaderClientImpl::cancelledError(const ResourceRequest& request) |
| { |
| if (!m_webFrame->client()) |
| return ResourceError(); |
| |
| return m_webFrame->client()->cancelledError( |
| m_webFrame, WrappedResourceRequest(request)); |
| } |
| |
| ResourceError FrameLoaderClientImpl::cannotShowURLError(const ResourceRequest& request) |
| { |
| if (!m_webFrame->client()) |
| return ResourceError(); |
| |
| return m_webFrame->client()->cannotHandleRequestError( |
| m_webFrame, WrappedResourceRequest(request)); |
| } |
| |
| ResourceError FrameLoaderClientImpl::interruptForPolicyChangeError( |
| const ResourceRequest& request) |
| { |
| return ResourceError(internalErrorDomain, PolicyChangeError, |
| request.url().string(), String()); |
| } |
| |
| ResourceError FrameLoaderClientImpl::cannotShowMIMETypeError(const ResourceResponse&) |
| { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| ResourceError FrameLoaderClientImpl::fileDoesNotExistError(const ResourceResponse&) |
| { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| ResourceError FrameLoaderClientImpl::pluginWillHandleLoadError(const ResourceResponse&) |
| { |
| // FIXME |
| return ResourceError(); |
| } |
| |
| bool FrameLoaderClientImpl::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" |
| ResourceError c = cancelledError(ResourceRequest()); |
| return error.errorCode() != c.errorCode() || error.domain() != c.domain(); |
| } |
| |
| bool FrameLoaderClientImpl::canHandleRequest(const ResourceRequest& request) const |
| { |
| return m_webFrame->client()->canHandleRequest( |
| m_webFrame, WrappedResourceRequest(request)); |
| } |
| |
| bool FrameLoaderClientImpl::canShowMIMETypeAsHTML(const String& MIMEType) const |
| { |
| notImplemented(); |
| return false; |
| } |
| |
| bool FrameLoaderClientImpl::canShowMIMEType(const String& mimeType) 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. |
| |
| // mimeType 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 (webKitClient()->mimeRegistry()->supportsMIMEType(mimeType) == WebMimeRegistry::IsSupported) |
| return true; |
| |
| // If Chrome is started with the --disable-plugins switch, pluginData is null. |
| PluginData* pluginData = m_webFrame->frame()->page()->pluginData(); |
| |
| // See if the type is handled by an installed plugin, if so, we can show it. |
| // FIXME: (http://b/1085524) This is the place to stick a preference to |
| // disable full page plugins (optionally for certain types!) |
| return !mimeType.isEmpty() && pluginData && pluginData->supportsMimeType(mimeType); |
| } |
| |
| bool FrameLoaderClientImpl::representationExistsForURLScheme(const String&) const |
| { |
| // FIXME |
| return false; |
| } |
| |
| String FrameLoaderClientImpl::generatedMIMETypeForURLScheme(const String& scheme) 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(scheme.lower()); |
| return mimeType; |
| } |
| |
| void FrameLoaderClientImpl::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 |
| // m_webFrame->frame()->loader()->setPreviousHistoryItem(0); |
| } |
| |
| void FrameLoaderClientImpl::saveViewStateToItem(HistoryItem*) |
| { |
| // FIXME |
| } |
| |
| void FrameLoaderClientImpl::restoreViewState() |
| { |
| // FIXME: probably scrolls to last position when you go back or forward |
| } |
| |
| void FrameLoaderClientImpl::provisionalLoadStarted() |
| { |
| // FIXME: On mac, this does various caching stuff |
| } |
| |
| void FrameLoaderClientImpl::didFinishLoad() |
| { |
| OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); |
| if (observer) |
| observer->didFinishLoading(); |
| } |
| |
| void FrameLoaderClientImpl::prepareForDataSourceReplacement() |
| { |
| // FIXME |
| } |
| |
| PassRefPtr<DocumentLoader> FrameLoaderClientImpl::createDocumentLoader( |
| const ResourceRequest& request, |
| const SubstituteData& data) |
| { |
| RefPtr<WebDataSourceImpl> ds = WebDataSourceImpl::create(request, data); |
| if (m_webFrame->client()) |
| m_webFrame->client()->didCreateDataSource(m_webFrame, ds.get()); |
| return ds.release(); |
| } |
| |
| void FrameLoaderClientImpl::setTitle(const StringWithDirection& title, const KURL& url) |
| { |
| // FIXME: inform consumer of changes to the title. |
| } |
| |
| String FrameLoaderClientImpl::userAgent(const KURL& url) |
| { |
| return webKitClient()->userAgent(url); |
| } |
| |
| void FrameLoaderClientImpl::savePlatformDataToCachedFrame(CachedFrame*) |
| { |
| // The page cache should be disabled. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void FrameLoaderClientImpl::transitionToCommittedFromCachedFrame(CachedFrame*) |
| { |
| ASSERT_NOT_REACHED(); |
| } |
| |
| // Called when the FrameLoader goes into a state in which a new page load |
| // will occur. |
| void FrameLoaderClientImpl::transitionToCommittedForNewPage() |
| { |
| makeDocumentView(); |
| } |
| |
| void FrameLoaderClientImpl::didSaveToPageCache() |
| { |
| } |
| |
| void FrameLoaderClientImpl::didRestoreFromPageCache() |
| { |
| } |
| |
| void FrameLoaderClientImpl::dispatchDidBecomeFrameset(bool) |
| { |
| } |
| |
| bool FrameLoaderClientImpl::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 FrameLoaderClientImpl::download(ResourceHandle* handle, |
| const ResourceRequest& request, |
| const ResourceRequest& initialRequest, |
| const ResourceResponse& response) |
| { |
| ASSERT_NOT_REACHED(); |
| } |
| |
| PassRefPtr<Frame> FrameLoaderClientImpl::createFrame( |
| const KURL& url, |
| const String& name, |
| HTMLFrameOwnerElement* ownerElement, |
| const String& referrer, |
| bool allowsScrolling, |
| int marginWidth, |
| int marginHeight) |
| { |
| FrameLoadRequest frameRequest(m_webFrame->frame()->document()->securityOrigin(), |
| ResourceRequest(url, referrer), name); |
| return m_webFrame->createChildFrame(frameRequest, ownerElement); |
| } |
| |
| void FrameLoaderClientImpl::didTransferChildFrameToNewDocument(Page*) |
| { |
| ASSERT(m_webFrame->frame()->ownerElement()); |
| |
| WebFrameImpl* newParent = static_cast<WebFrameImpl*>(m_webFrame->parent()); |
| if (!newParent || !newParent->client()) |
| return; |
| |
| // Replace the client since the old client may be destroyed when the |
| // previous page is closed. |
| m_webFrame->setClient(newParent->client()); |
| } |
| |
| void FrameLoaderClientImpl::transferLoadingResourceFromPage(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request, Page* oldPage) |
| { |
| assignIdentifierToInitialRequest(identifier, loader, request); |
| |
| WebFrameImpl* oldWebFrame = WebFrameImpl::fromFrame(oldPage->mainFrame()); |
| if (oldWebFrame && oldWebFrame->client()) |
| oldWebFrame->client()->removeIdentifierForRequest(identifier); |
| } |
| |
| PassRefPtr<Widget> FrameLoaderClientImpl::createPlugin( |
| const IntSize& size, // FIXME: how do we use this? |
| HTMLPlugInElement* element, |
| const KURL& url, |
| const Vector<String>& paramNames, |
| const Vector<String>& paramValues, |
| const String& mimeType, |
| bool loadManually) |
| { |
| if (!m_webFrame->client()) |
| return 0; |
| |
| WebPluginParams params; |
| params.url = url; |
| params.mimeType = mimeType; |
| params.attributeNames = paramNames; |
| params.attributeValues = paramValues; |
| params.loadManually = loadManually; |
| |
| WebPlugin* webPlugin = m_webFrame->client()->createPlugin(m_webFrame, params); |
| if (!webPlugin) |
| return 0; |
| |
| // The container takes ownership of the WebPlugin. |
| RefPtr<WebPluginContainerImpl> container = |
| WebPluginContainerImpl::create(element, webPlugin); |
| |
| if (!webPlugin->initialize(container.get())) |
| return 0; |
| |
| // The element might have been removed during plugin initialization! |
| if (!element->renderer()) |
| return 0; |
| |
| return container; |
| } |
| |
| // This method gets called when a plugin is put in place of html content |
| // (e.g., acrobat reader). |
| void FrameLoaderClientImpl::redirectDataToPlugin(Widget* pluginWidget) |
| { |
| if (pluginWidget->isPluginContainer()) |
| m_pluginWidget = static_cast<WebPluginContainerImpl*>(pluginWidget); |
| ASSERT(m_pluginWidget.get()); |
| } |
| |
| PassRefPtr<Widget> FrameLoaderClientImpl::createJavaAppletWidget( |
| const IntSize& size, |
| HTMLAppletElement* element, |
| const KURL& /* baseURL */, |
| const Vector<String>& paramNames, |
| const Vector<String>& paramValues) |
| { |
| return createPlugin(size, element, KURL(), paramNames, paramValues, |
| "application/x-java-applet", false); |
| } |
| |
| ObjectContentType FrameLoaderClientImpl::objectContentType( |
| const KURL& url, |
| const String& explicitMimeType, |
| bool shouldPreferPlugInsForImages) |
| { |
| // This code is based on Apple's implementation from |
| // WebCoreSupport/WebFrameBridge.mm. |
| |
| String mimeType = explicitMimeType; |
| if (mimeType.isEmpty()) { |
| // Try to guess the MIME type based off the extension. |
| String filename = url.lastPathComponent(); |
| int extensionPos = filename.reverseFind('.'); |
| if (extensionPos >= 0) { |
| String extension = filename.substring(extensionPos + 1); |
| mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension); |
| if (mimeType.isEmpty()) { |
| // If there's no mimetype registered for the extension, check to see |
| // if a plugin can handle the extension. |
| mimeType = getPluginMimeTypeFromExtension(extension); |
| } |
| } |
| |
| if (mimeType.isEmpty()) |
| return ObjectContentFrame; |
| } |
| |
| // If Chrome is started with the --disable-plugins switch, pluginData is 0. |
| PluginData* pluginData = m_webFrame->frame()->page()->pluginData(); |
| bool plugInSupportsMIMEType = pluginData && pluginData->supportsMimeType(mimeType); |
| |
| if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) |
| return shouldPreferPlugInsForImages && plugInSupportsMIMEType ? ObjectContentNetscapePlugin : ObjectContentImage; |
| |
| if (plugInSupportsMIMEType) |
| return ObjectContentNetscapePlugin; |
| |
| if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType)) |
| return ObjectContentFrame; |
| |
| return ObjectContentNone; |
| } |
| |
| String FrameLoaderClientImpl::overrideMediaType() const |
| { |
| // FIXME |
| return String(); |
| } |
| |
| bool FrameLoaderClientImpl::actionSpecifiesNavigationPolicy( |
| const NavigationAction& action, |
| WebNavigationPolicy* policy) |
| { |
| const MouseEvent* event = 0; |
| if (action.type() == NavigationTypeLinkClicked |
| && action.event()->isMouseEvent()) |
| event = static_cast<const MouseEvent*>(action.event()); |
| else if (action.type() == NavigationTypeFormSubmitted |
| && action.event() |
| && action.event()->underlyingEvent() |
| && action.event()->underlyingEvent()->isMouseEvent()) |
| event = static_cast<const MouseEvent*>(action.event()->underlyingEvent()); |
| |
| if (!event) |
| return false; |
| |
| return WebViewImpl::navigationPolicyFromMouseEvent( |
| event->button(), event->ctrlKey(), event->shiftKey(), event->altKey(), |
| event->metaKey(), policy); |
| } |
| |
| PassOwnPtr<WebPluginLoadObserver> FrameLoaderClientImpl::pluginLoadObserver() |
| { |
| WebDataSourceImpl* ds = WebDataSourceImpl::fromDocumentLoader( |
| m_webFrame->frame()->loader()->activeDocumentLoader()); |
| if (!ds) { |
| // We can arrive here if a popstate event handler detaches this frame. |
| // FIXME: Remove this code once http://webkit.org/b/36202 is fixed. |
| ASSERT(!m_webFrame->frame()->page()); |
| return 0; |
| } |
| return ds->releasePluginLoadObserver(); |
| } |
| |
| PassRefPtr<FrameNetworkingContext> FrameLoaderClientImpl::createNetworkingContext() |
| { |
| return FrameNetworkingContextImpl::create(m_webFrame->frame()); |
| } |
| |
| } // namespace WebKit |