summaryrefslogtreecommitdiff
path: root/Source/WebCore/loader/DocumentLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/loader/DocumentLoader.cpp')
-rw-r--r--Source/WebCore/loader/DocumentLoader.cpp1121
1 files changed, 638 insertions, 483 deletions
diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp
index 8acee0a2a..0eb8170c1 100644
--- a/Source/WebCore/loader/DocumentLoader.cpp
+++ b/Source/WebCore/loader/DocumentLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
* 2. 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.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,33 +31,44 @@
#include "DocumentLoader.h"
#include "ApplicationCacheHost.h"
+#include "Archive.h"
#include "ArchiveResourceCollection.h"
#include "CachedPage.h"
#include "CachedRawResource.h"
#include "CachedResourceLoader.h"
+#include "ContentExtensionError.h"
+#include "ContentSecurityPolicy.h"
#include "DOMWindow.h"
#include "Document.h"
#include "DocumentParser.h"
#include "DocumentWriter.h"
+#include "ElementChildIterator.h"
#include "Event.h"
+#include "EventNames.h"
+#include "ExtensionStyleSheets.h"
#include "FormState.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameTree.h"
#include "HTMLFormElement.h"
#include "HTMLFrameOwnerElement.h"
+#include "HTTPHeaderNames.h"
#include "HistoryItem.h"
#include "IconController.h"
+#include "IconLoader.h"
#include "InspectorInstrumentation.h"
+#include "LinkIconCollector.h"
+#include "LinkIconType.h"
#include "Logging.h"
#include "MainFrame.h"
#include "MemoryCache.h"
#include "Page.h"
#include "PolicyChecker.h"
#include "ProgressTracker.h"
-#include "ResourceBuffer.h"
#include "ResourceHandle.h"
+#include "ResourceLoadObserver.h"
#include "SchemeRegistry.h"
+#include "ScriptableDocumentParser.h"
#include "SecurityPolicy.h"
#include "Settings.h"
#include "SubresourceLoader.h"
@@ -67,148 +78,116 @@
#include <wtf/Ref.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
-#include <wtf/unicode/Unicode.h>
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
#include "ArchiveFactory.h"
#endif
-#if USE(CONTENT_FILTERING)
+#if ENABLE(CONTENT_FILTERING)
#include "ContentFilter.h"
#endif
+#if USE(QUICK_LOOK)
+#include "PreviewConverter.h"
+#include "QuickLook.h"
+#endif
+
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - DocumentLoader::" fmt, this, ##__VA_ARGS__)
+
namespace WebCore {
-static void cancelAll(const ResourceLoaderSet& loaders)
+static void cancelAll(const ResourceLoaderMap& loaders)
{
Vector<RefPtr<ResourceLoader>> loadersCopy;
- copyToVector(loaders, loadersCopy);
- size_t size = loadersCopy.size();
- for (size_t i = 0; i < size; ++i)
- loadersCopy[i]->cancel();
+ copyValuesToVector(loaders, loadersCopy);
+ for (auto& loader : loadersCopy)
+ loader->cancel();
}
-static void setAllDefersLoading(const ResourceLoaderSet& loaders, bool defers)
+static void setAllDefersLoading(const ResourceLoaderMap& loaders, bool defers)
{
Vector<RefPtr<ResourceLoader>> loadersCopy;
- copyToVector(loaders, loadersCopy);
- size_t size = loadersCopy.size();
- for (size_t i = 0; i < size; ++i)
- loadersCopy[i]->setDefersLoading(defers);
+ copyValuesToVector(loaders, loadersCopy);
+ for (auto& loader : loadersCopy)
+ loader->setDefersLoading(defers);
}
-static bool areAllLoadersPageCacheAcceptable(const ResourceLoaderSet& loaders)
+static bool areAllLoadersPageCacheAcceptable(const ResourceLoaderMap& loaders)
{
Vector<RefPtr<ResourceLoader>> loadersCopy;
- copyToVector(loaders, loadersCopy);
+ copyValuesToVector(loaders, loadersCopy);
for (auto& loader : loadersCopy) {
- ResourceHandle* handle = loader->handle();
- if (!handle)
+ if (!loader->frameLoader() || !loader->frameLoader()->frame().page())
return false;
- CachedResource* cachedResource = memoryCache()->resourceForURL(handle->firstRequest().url());
+ CachedResource* cachedResource = MemoryCache::singleton().resourceForRequest(loader->request(), loader->frameLoader()->frame().page()->sessionID());
if (!cachedResource)
return false;
+ // Only image and XHR loads do prevent the page from entering the PageCache.
// All non-image loads will prevent the page from entering the PageCache.
- if (!cachedResource->isImage())
+ if (!cachedResource->isImage() && !cachedResource->areAllClientsXMLHttpRequests())
return false;
}
return true;
}
-DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& substituteData)
- : m_deferMainResourceDataLoad(true)
- , m_frame(0)
- , m_cachedResourceLoader(CachedResourceLoader::create(this))
+DocumentLoader::DocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData)
+ : m_cachedResourceLoader(CachedResourceLoader::create(this))
, m_writer(m_frame)
- , m_originalRequest(req)
+ , m_originalRequest(request)
, m_substituteData(substituteData)
- , m_originalRequestCopy(req)
- , m_request(req)
+ , m_originalRequestCopy(request)
+ , m_request(request)
, m_originalSubstituteDataWasValid(substituteData.isValid())
- , m_committed(false)
- , m_isStopping(false)
- , m_gotFirstByte(false)
- , m_isClientRedirect(false)
- , m_isLoadingMultipartContent(false)
- , m_wasOnloadHandled(false)
- , m_stopRecordingResponses(false)
- , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired)
- , m_didCreateGlobalHistoryEntry(false)
- , m_loadingMainResource(false)
- , m_timeOfLastDataReceived(0.0)
- , m_identifierForLoadWithoutResourceLoader(0)
- , m_dataLoadTimer(this, &DocumentLoader::handleSubstituteDataLoadNow)
- , m_waitingForContentPolicy(false)
- , m_subresourceLoadersArePageCacheAcceptable(false)
- , m_applicationCacheHost(adoptPtr(new ApplicationCacheHost(this)))
+ , m_substituteResourceDeliveryTimer(*this, &DocumentLoader::substituteResourceDeliveryTimerFired)
+ , m_dataLoadTimer(*this, &DocumentLoader::handleSubstituteDataLoadNow)
+ , m_applicationCacheHost(std::make_unique<ApplicationCacheHost>(*this))
{
}
FrameLoader* DocumentLoader::frameLoader() const
{
if (!m_frame)
- return 0;
+ return nullptr;
return &m_frame->loader();
}
-ResourceLoader* DocumentLoader::mainResourceLoader() const
+SubresourceLoader* DocumentLoader::mainResourceLoader() const
{
- return m_mainResource ? m_mainResource->loader() : 0;
+ if (!m_mainResource)
+ return nullptr;
+ return m_mainResource->loader();
}
DocumentLoader::~DocumentLoader()
{
ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !isLoading());
+ ASSERT_WITH_MESSAGE(!m_waitingForContentPolicy, "The content policy callback should never outlive its DocumentLoader.");
+ ASSERT_WITH_MESSAGE(!m_waitingForNavigationPolicy, "The navigation policy callback should never outlive its DocumentLoader.");
if (m_iconLoadDecisionCallback)
m_iconLoadDecisionCallback->invalidate();
if (m_iconDataCallback)
m_iconDataCallback->invalidate();
m_cachedResourceLoader->clearDocumentLoader();
-
+
clearMainResource();
}
-PassRefPtr<ResourceBuffer> DocumentLoader::mainResourceData() const
+RefPtr<SharedBuffer> DocumentLoader::mainResourceData() const
{
if (m_substituteData.isValid())
- return ResourceBuffer::create(m_substituteData.content()->data(), m_substituteData.content()->size());
+ return m_substituteData.content()->copy();
if (m_mainResource)
return m_mainResource->resourceBuffer();
- return 0;
+ return nullptr;
}
Document* DocumentLoader::document() const
{
if (m_frame && m_frame->loader().documentLoader() == this)
return m_frame->document();
- return 0;
-}
-
-const ResourceRequest& DocumentLoader::originalRequest() const
-{
- return m_originalRequest;
-}
-
-const ResourceRequest& DocumentLoader::originalRequestCopy() const
-{
- return m_originalRequestCopy;
-}
-
-const ResourceRequest& DocumentLoader::request() const
-{
- return m_request;
-}
-
-ResourceRequest& DocumentLoader::request()
-{
- return m_request;
-}
-
-const URL& DocumentLoader::url() const
-{
- return request().url();
+ return nullptr;
}
void DocumentLoader::replaceRequestURLForSameDocumentNavigation(const URL& url)
@@ -225,8 +204,11 @@ void DocumentLoader::setRequest(const ResourceRequest& req)
handlingUnreachableURL = m_substituteData.isValid() && !m_substituteData.failingURL().isEmpty();
+ bool shouldNotifyAboutProvisionalURLChange = false;
if (handlingUnreachableURL)
m_committed = false;
+ else if (isLoadingMainResource() && req.url() != m_request.url())
+ shouldNotifyAboutProvisionalURLChange = true;
// We should never be getting a redirect callback after the data
// source is committed, except in the unreachable URL case. It
@@ -234,6 +216,8 @@ void DocumentLoader::setRequest(const ResourceRequest& req)
ASSERT(!m_committed);
m_request = req;
+ if (shouldNotifyAboutProvisionalURLChange)
+ frameLoader()->client().dispatchDidChangeProvisionalURL();
}
void DocumentLoader::setMainDocumentError(const ResourceError& error)
@@ -246,6 +230,9 @@ void DocumentLoader::mainReceivedError(const ResourceError& error)
{
ASSERT(!error.isNull());
+ if (!frameLoader())
+ return;
+
if (m_identifierForLoadWithoutResourceLoader) {
ASSERT(!mainResourceLoader());
frameLoader()->client().dispatchDidFailLoading(this, m_identifierForLoadWithoutResourceLoader, error);
@@ -259,8 +246,6 @@ void DocumentLoader::mainReceivedError(const ResourceError& error)
m_applicationCacheHost->failedLoadingMainResource();
- if (!frameLoader())
- return;
setMainDocumentError(error);
clearMainResourceLoader();
frameLoader()->receivedMainResourceError(error);
@@ -272,8 +257,8 @@ void DocumentLoader::mainReceivedError(const ResourceError& error)
// but not loads initiated by child frames' data sources -- that's the WebFrame's job.
void DocumentLoader::stopLoading()
{
- RefPtr<Frame> protectFrame(m_frame);
- Ref<DocumentLoader> protectLoader(*this);
+ RefPtr<Frame> protectedFrame(m_frame);
+ Ref<DocumentLoader> protectedThis(*this);
// In some rare cases, calling FrameLoader::stopLoading could cause isLoading() to return false.
// (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it
@@ -300,7 +285,7 @@ void DocumentLoader::stopLoading()
cancelAll(m_multipartSubresourceLoaders);
// Appcache uses ResourceHandle directly, DocumentLoader doesn't count these loads.
- m_applicationCacheHost->stopLoadingInFrame(m_frame);
+ m_applicationCacheHost->stopLoadingInFrame(*m_frame);
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
clearArchiveResources();
@@ -321,19 +306,21 @@ void DocumentLoader::stopLoading()
m_isStopping = true;
- FrameLoader* frameLoader = DocumentLoader::frameLoader();
-
- if (isLoadingMainResource()) {
- // Stop the main resource loader and let it send the cancelled message.
- cancelMainResourceLoad(frameLoader->cancelledError(m_request));
- } else if (!m_subresourceLoaders.isEmpty())
- // The main resource loader already finished loading. Set the cancelled error on the
- // document and let the subresourceLoaders send individual cancelled messages below.
- setMainDocumentError(frameLoader->cancelledError(m_request));
- else
- // If there are no resource loaders, we need to manufacture a cancelled message.
- // (A back/forward navigation has no resource loaders because its resources are cached.)
- mainReceivedError(frameLoader->cancelledError(m_request));
+ // The frame may have been detached from this document by the onunload handler
+ if (auto* frameLoader = DocumentLoader::frameLoader()) {
+ if (isLoadingMainResource()) {
+ // Stop the main resource loader and let it send the cancelled message.
+ cancelMainResourceLoad(frameLoader->cancelledError(m_request));
+ } else if (!m_subresourceLoaders.isEmpty() || !m_plugInStreamLoaders.isEmpty()) {
+ // The main resource loader already finished loading. Set the cancelled error on the
+ // document and let the subresourceLoaders and pluginLoaders send individual cancelled messages below.
+ setMainDocumentError(frameLoader->cancelledError(m_request));
+ } else {
+ // If there are no resource loaders, we need to manufacture a cancelled message.
+ // (A back/forward navigation has no resource loaders because its resources are cached.)
+ mainReceivedError(frameLoader->cancelledError(m_request));
+ }
+ }
// We always need to explicitly cancel the Document's parser when stopping the load.
// Otherwise cancelling the parser while starting the next page load might result
@@ -366,9 +353,14 @@ bool DocumentLoader::isLoading() const
return isLoadingMainResource() || !m_subresourceLoaders.isEmpty() || !m_plugInStreamLoaders.isEmpty();
}
-void DocumentLoader::notifyFinished(CachedResource* resource)
+void DocumentLoader::notifyFinished(CachedResource& resource)
{
- ASSERT_UNUSED(resource, m_mainResource == resource);
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter && !m_contentFilter->continueAfterNotifyFinished(resource))
+ return;
+#endif
+
+ ASSERT_UNUSED(resource, m_mainResource == &resource);
ASSERT(m_mainResource);
if (!m_mainResource->errorOccurred() && !m_mainResource->wasCanceled()) {
finishedLoading(m_mainResource->loadFinishTime());
@@ -388,10 +380,10 @@ void DocumentLoader::finishedLoading(double finishTime)
// There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
// See <rdar://problem/6304600> for more details.
#if !USE(CF)
- ASSERT(!m_frame->page()->defersLoading() || InspectorInstrumentation::isDebuggerPaused(m_frame));
+ ASSERT(!m_frame->page()->defersLoading() || frameLoader()->stateMachine().creatingInitialEmptyDocument() || InspectorInstrumentation::isDebuggerPaused(m_frame));
#endif
- Ref<DocumentLoader> protect(*this);
+ Ref<DocumentLoader> protectedThis(*this);
if (m_identifierForLoadWithoutResourceLoader) {
// A didFinishLoading delegate might try to cancel the load (despite it
@@ -403,27 +395,14 @@ void DocumentLoader::finishedLoading(double finishTime)
frameLoader()->notifier().dispatchDidFinishLoading(this, identifier, finishTime);
}
-#if USE(CONTENT_FILTERING)
- if (m_contentFilter && m_contentFilter->needsMoreData()) {
- m_contentFilter->finishedAddingData();
- int length;
- const char* data = m_contentFilter->getReplacementData(length);
- if (data)
- dataReceived(m_mainResource.get(), data, length);
-
- if (m_contentFilter->didBlockData())
- setContentFilterForBlockedLoad(m_contentFilter);
- }
-#endif
-
maybeFinishLoadingMultipartContent();
- double responseEndTime = finishTime;
+ MonotonicTime responseEndTime = MonotonicTime::fromRawSeconds(finishTime);
if (!responseEndTime)
responseEndTime = m_timeOfLastDataReceived;
if (!responseEndTime)
- responseEndTime = monotonicallyIncreasingTime();
- timing()->setResponseEnd(responseEndTime);
+ responseEndTime = MonotonicTime::now();
+ timing().setResponseEnd(responseEndTime);
commitIfReady();
if (!frameLoader())
@@ -441,14 +420,14 @@ void DocumentLoader::finishedLoading(double finishTime)
if (!m_mainDocumentError.isNull())
return;
clearMainResourceLoader();
- if (!frameLoader()->stateMachine()->creatingInitialEmptyDocument())
+ if (!frameLoader()->stateMachine().creatingInitialEmptyDocument())
frameLoader()->checkLoadComplete();
// If the document specified an application cache manifest, it violates the author's intent if we store it in the memory cache
// and deny the appcache the chance to intercept it in the future, so remove from the memory cache.
if (m_frame) {
if (m_mainResource && m_frame->document()->hasManifest())
- memoryCache()->remove(m_mainResource.get());
+ MemoryCache::singleton().remove(*m_mainResource);
}
m_applicationCacheHost->finishedLoadingMainResource();
}
@@ -466,13 +445,13 @@ bool DocumentLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest
return false;
}
-void DocumentLoader::handleSubstituteDataLoadNow(DocumentLoaderTimer*)
+void DocumentLoader::handleSubstituteDataLoadNow()
{
- URL url = m_substituteData.responseURL();
- if (url.isEmpty())
- url = m_request.url();
- ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), "");
- responseReceived(0, response);
+ ResourceResponse response = m_substituteData.response();
+ if (response.url().isEmpty())
+ response = ResourceResponse(m_request.url(), m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding());
+
+ responseReceived(response);
}
void DocumentLoader::startDataLoadTimer()
@@ -488,14 +467,14 @@ void DocumentLoader::startDataLoadTimer()
void DocumentLoader::handleSubstituteDataLoadSoon()
{
if (!m_deferMainResourceDataLoad || frameLoader()->loadsSynchronously())
- handleSubstituteDataLoadNow(0);
+ handleSubstituteDataLoadNow();
else
startDataLoadTimer();
}
-void DocumentLoader::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
+void DocumentLoader::redirectReceived(CachedResource& resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
{
- ASSERT_UNUSED(resource, resource == m_mainResource);
+ ASSERT_UNUSED(resource, &resource == m_mainResource);
willSendRequest(request, redirectResponse);
}
@@ -507,27 +486,42 @@ void DocumentLoader::willSendRequest(ResourceRequest& newRequest, const Resource
// callbacks is meant to prevent.
ASSERT(!newRequest.isNull());
- if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url())) {
+ bool didReceiveRedirectResponse = !redirectResponse.isNull();
+ if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url(), didReceiveRedirectResponse)) {
cancelMainResourceLoad(frameLoader()->cancelledError(newRequest));
return;
}
- ASSERT(timing()->fetchStart());
- if (!redirectResponse.isNull()) {
+ ASSERT(timing().fetchStart());
+ if (didReceiveRedirectResponse) {
// If the redirecting url is not allowed to display content from the target origin,
// then block the redirect.
- RefPtr<SecurityOrigin> redirectingOrigin = SecurityOrigin::create(redirectResponse.url());
- if (!redirectingOrigin->canDisplay(newRequest.url())) {
+ Ref<SecurityOrigin> redirectingOrigin(SecurityOrigin::create(redirectResponse.url()));
+ if (!redirectingOrigin.get().canDisplay(newRequest.url())) {
FrameLoader::reportLocalLoadFailed(m_frame, newRequest.url().string());
cancelMainResourceLoad(frameLoader()->cancelledError(newRequest));
return;
}
- timing()->addRedirect(redirectResponse.url(), newRequest.url());
+ if (!portAllowed(newRequest.url())) {
+ FrameLoader::reportBlockedPortFailed(m_frame, newRequest.url().string());
+ cancelMainResourceLoad(frameLoader()->blockedError(newRequest));
+ return;
+ }
+ timing().addRedirect(redirectResponse.url(), newRequest.url());
}
+ ASSERT(m_frame);
+
+ Frame& topFrame = m_frame->tree().top();
+
+ ASSERT(m_frame->document());
+ ASSERT(topFrame.document());
+
+ ResourceLoadObserver::sharedObserver().logFrameNavigation(*m_frame, topFrame, newRequest, redirectResponse);
+
// Update cookie policy base URL as URL changes, except for subframes, which use the
// URL of the main frame which doesn't change when we redirect.
- if (frameLoader()->frame().isMainFrame())
+ if (m_frame->isMainFrame())
newRequest.setFirstPartyForCookies(newRequest.url());
// If we're fielding a redirect in response to a POST, force a load from origin, since
@@ -537,43 +531,58 @@ void DocumentLoader::willSendRequest(ResourceRequest& newRequest, const Resource
if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
newRequest.setCachePolicy(ReloadIgnoringCacheData);
- Frame& topFrame = m_frame->tree().top();
if (&topFrame != m_frame) {
- if (!frameLoader()->mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), newRequest.url())) {
+ if (!m_frame->loader().mixedContentChecker().canDisplayInsecureContent(m_frame->document()->securityOrigin(), MixedContentChecker::ContentType::Active, newRequest.url(), MixedContentChecker::AlwaysDisplayInNonStrictMode::Yes)) {
+ cancelMainResourceLoad(frameLoader()->cancelledError(newRequest));
+ return;
+ }
+ if (!frameLoader()->mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), MixedContentChecker::ContentType::Active, newRequest.url())) {
cancelMainResourceLoad(frameLoader()->cancelledError(newRequest));
return;
}
}
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter && !m_contentFilter->continueAfterWillSendRequest(newRequest, redirectResponse))
+ return;
+#endif
+
setRequest(newRequest);
- if (!redirectResponse.isNull()) {
+ if (didReceiveRedirectResponse) {
// We checked application cache for initial URL, now we need to check it for redirected one.
ASSERT(!m_substituteData.isValid());
m_applicationCacheHost->maybeLoadMainResourceForRedirect(newRequest, m_substituteData);
- if (m_substituteData.isValid())
- m_identifierForLoadWithoutResourceLoader = mainResourceLoader()->identifier();
+ if (m_substituteData.isValid()) {
+ RELEASE_ASSERT(m_mainResource);
+ ResourceLoader* loader = m_mainResource->loader();
+ m_identifierForLoadWithoutResourceLoader = loader ? loader->identifier() : m_mainResource->identifierForLoadWithoutResourceLoader();
+ }
}
// FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate
// listener. But there's no way to do that in practice. So instead we cancel later if the
// listener tells us to. In practice that means the navigation policy needs to be decided
// synchronously for these redirect cases.
- if (redirectResponse.isNull())
+ if (!didReceiveRedirectResponse)
return;
- frameLoader()->policyChecker().checkNavigationPolicy(newRequest, [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) {
+ ASSERT(!m_waitingForNavigationPolicy);
+ m_waitingForNavigationPolicy = true;
+ frameLoader()->policyChecker().checkNavigationPolicy(newRequest, didReceiveRedirectResponse, [this] (const ResourceRequest& request, FormState*, bool shouldContinue) {
continueAfterNavigationPolicy(request, shouldContinue);
});
}
void DocumentLoader::continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue)
{
+ ASSERT(m_waitingForNavigationPolicy);
+ m_waitingForNavigationPolicy = false;
if (!shouldContinue)
stopLoadingForPolicyChange();
else if (m_substituteData.isValid()) {
// A redirect resulted in loading substitute data.
- ASSERT(timing()->redirectCount());
+ ASSERT(timing().redirectCount());
// We need to remove our reference to the CachedResource in favor of a SubstituteData load.
// This will probably trigger the cancellation of the CachedResource's underlying ResourceLoader, though there is a
@@ -582,46 +591,76 @@ void DocumentLoader::continueAfterNavigationPolicy(const ResourceRequest&, bool
// However, from an API perspective, this isn't a cancellation. Therefore, sever our relationship with the network load,
// but prevent the ResourceLoader from sending ResourceLoadNotifier callbacks.
RefPtr<ResourceLoader> resourceLoader = mainResourceLoader();
- ASSERT(resourceLoader->shouldSendResourceLoadCallbacks());
- resourceLoader->setSendCallbackPolicy(DoNotSendCallbacks);
+ if (resourceLoader) {
+ ASSERT(resourceLoader->shouldSendResourceLoadCallbacks());
+ resourceLoader->setSendCallbackPolicy(DoNotSendCallbacks);
+ }
+
clearMainResource();
- resourceLoader->setSendCallbackPolicy(SendCallbacks);
+
+ if (resourceLoader)
+ resourceLoader->setSendCallbackPolicy(SendCallbacks);
handleSubstituteDataLoadSoon();
}
}
-void DocumentLoader::responseReceived(CachedResource* resource, const ResourceResponse& response)
+void DocumentLoader::stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied(unsigned long identifier, const ResourceResponse& response)
+{
+ InspectorInstrumentation::continueAfterXFrameOptionsDenied(*m_frame, identifier, *this, response);
+ m_frame->document()->enforceSandboxFlags(SandboxOrigin);
+ if (HTMLFrameOwnerElement* ownerElement = m_frame->ownerElement())
+ ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false));
+
+ // The load event might have detached this frame. In that case, the load will already have been cancelled during detach.
+ if (FrameLoader* frameLoader = this->frameLoader())
+ cancelMainResourceLoad(frameLoader->cancelledError(m_request));
+}
+
+void DocumentLoader::responseReceived(CachedResource& resource, const ResourceResponse& response)
{
- ASSERT_UNUSED(resource, m_mainResource == resource);
- Ref<DocumentLoader> protect(*this);
+ ASSERT_UNUSED(resource, m_mainResource == &resource);
+ responseReceived(response);
+}
+
+void DocumentLoader::responseReceived(const ResourceResponse& response)
+{
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter && !m_contentFilter->continueAfterResponseReceived(response))
+ return;
+#endif
+
+ Ref<DocumentLoader> protectedThis(*this);
bool willLoadFallback = m_applicationCacheHost->maybeLoadFallbackForMainResponse(request(), response);
// The memory cache doesn't understand the application cache or its caching rules. So if a main resource is served
// from the application cache, ensure we don't save the result for future use.
if (willLoadFallback)
- memoryCache()->remove(m_mainResource.get());
+ MemoryCache::singleton().remove(*m_mainResource);
if (willLoadFallback)
return;
- DEFINE_STATIC_LOCAL(AtomicString, xFrameOptionHeader, ("x-frame-options", AtomicString::ConstructFromLiteral));
- HTTPHeaderMap::const_iterator it = response.httpHeaderFields().find(xFrameOptionHeader);
- if (it != response.httpHeaderFields().end()) {
+ ASSERT(m_identifierForLoadWithoutResourceLoader || m_mainResource);
+ unsigned long identifier = m_identifierForLoadWithoutResourceLoader ? m_identifierForLoadWithoutResourceLoader : m_mainResource->identifier();
+ ASSERT(identifier);
+
+ auto url = response.url();
+
+ ContentSecurityPolicy contentSecurityPolicy(SecurityOrigin::create(url), m_frame);
+ contentSecurityPolicy.didReceiveHeaders(ContentSecurityPolicyResponseHeaders(response));
+ if (!contentSecurityPolicy.allowFrameAncestors(*m_frame, url)) {
+ stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied(identifier, response);
+ return;
+ }
+
+ const auto& commonHeaders = response.httpHeaderFields().commonHeaders();
+ auto it = commonHeaders.find(HTTPHeaderName::XFrameOptions);
+ if (it != commonHeaders.end()) {
String content = it->value;
- ASSERT(m_mainResource);
- unsigned long identifier = m_identifierForLoadWithoutResourceLoader ? m_identifierForLoadWithoutResourceLoader : m_mainResource->identifier();
- ASSERT(identifier);
- if (frameLoader()->shouldInterruptLoadForXFrameOptions(content, response.url(), identifier)) {
- InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame, this, identifier, response);
- String message = "Refused to display '" + response.url().stringCenterEllipsizedToLength() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
- frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, identifier);
- frame()->document()->enforceSandboxFlags(SandboxOrigin);
- if (HTMLFrameOwnerElement* ownerElement = frame()->ownerElement())
- ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false));
-
- // The load event might have detached this frame. In that case, the load will already have been cancelled during detach.
- if (frameLoader())
- cancelMainResourceLoad(frameLoader()->cancelledError(m_request));
+ if (frameLoader()->shouldInterruptLoadForXFrameOptions(content, url, identifier)) {
+ String message = "Refused to display '" + url.stringCenterEllipsizedToLength() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
+ m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, identifier);
+ stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied(identifier, response);
return;
}
}
@@ -635,19 +674,22 @@ void DocumentLoader::responseReceived(CachedResource* resource, const ResourceRe
if (m_isLoadingMultipartContent) {
setupForReplace();
m_mainResource->clear();
- } else if (response.isMultipart()) {
- FeatureObserver::observe(m_frame->document(), FeatureObserver::MultipartMainResource);
+ } else if (response.isMultipart())
m_isLoadingMultipartContent = true;
- }
m_response = response;
if (m_identifierForLoadWithoutResourceLoader) {
+ if (m_mainResource && m_mainResource->wasRedirected()) {
+ ASSERT(m_mainResource->status() == CachedResource::Status::Cached);
+ frameLoader()->client().dispatchDidReceiveServerRedirectForProvisionalLoad();
+ }
addResponse(m_response);
frameLoader()->notifier().dispatchDidReceiveResponse(this, m_identifierForLoadWithoutResourceLoader, m_response, 0);
}
ASSERT(!m_waitingForContentPolicy);
+ ASSERT(frameLoader());
m_waitingForContentPolicy = true;
// Always show content with valid substitute data.
@@ -664,16 +706,38 @@ void DocumentLoader::responseReceived(CachedResource* resource, const ResourceRe
}
#endif
-#if USE(CONTENT_FILTERING)
- if (response.url().protocolIsInHTTPFamily() && ContentFilter::isEnabled())
- m_contentFilter = ContentFilter::create(response);
-#endif
-
frameLoader()->policyChecker().checkContentPolicy(m_response, [this](PolicyAction policy) {
continueAfterContentPolicy(policy);
});
}
+static bool isRemoteWebArchive(const DocumentLoader& documentLoader)
+{
+ using MIMETypeHashSet = HashSet<String, ASCIICaseInsensitiveHash>;
+ static NeverDestroyed<MIMETypeHashSet> webArchiveMIMETypes {
+ MIMETypeHashSet {
+ ASCIILiteral("application/x-webarchive"),
+ ASCIILiteral("application/x-mimearchive"),
+ ASCIILiteral("multipart/related"),
+#if PLATFORM(GTK)
+ ASCIILiteral("message/rfc822"),
+#endif
+ }
+ };
+
+ const ResourceResponse& response = documentLoader.response();
+ String mimeType = response.mimeType();
+ if (mimeType.isNull() || !webArchiveMIMETypes.get().contains(mimeType))
+ return false;
+
+#if USE(QUICK_LOOK)
+ if (response.url().protocolIs(QLPreviewProtocol()))
+ return false;
+#endif
+
+ return !documentLoader.substituteData().isValid() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(documentLoader.request().url().protocol().toStringWithoutCopying());
+}
+
void DocumentLoader::continueAfterContentPolicy(PolicyAction policy)
{
ASSERT(m_waitingForContentPolicy);
@@ -681,20 +745,10 @@ void DocumentLoader::continueAfterContentPolicy(PolicyAction policy)
if (isStopping())
return;
- URL url = m_request.url();
- const String& mimeType = m_response.mimeType();
-
switch (policy) {
case PolicyUse: {
// Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
- bool isRemoteWebArchive = (equalIgnoringCase("application/x-webarchive", mimeType)
- || equalIgnoringCase("application/x-mimearchive", mimeType)
-#if PLATFORM(GTK)
- || equalIgnoringCase("message/rfc822", mimeType)
-#endif
- || equalIgnoringCase("multipart/related", mimeType))
- && !m_substituteData.isValid() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol());
- if (!frameLoader()->client().canShowMIMEType(mimeType) || isRemoteWebArchive) {
+ if (!frameLoader()->client().canShowMIMEType(m_response.mimeType()) || isRemoteWebArchive(*this)) {
frameLoader()->policyChecker().cannotShowMIMEType(m_response);
// Check reachedTerminalState since the load may have already been canceled inside of _handleUnimplementablePolicyWithErrorCode::.
stopLoadingForPolicyChange();
@@ -711,31 +765,37 @@ void DocumentLoader::continueAfterContentPolicy(PolicyAction policy)
}
if (ResourceLoader* mainResourceLoader = this->mainResourceLoader())
- InspectorInstrumentation::continueWithPolicyDownload(m_frame, this, mainResourceLoader->identifier(), m_response);
+ InspectorInstrumentation::continueWithPolicyDownload(*m_frame, mainResourceLoader->identifier(), *this, m_response);
// When starting the request, we didn't know that it would result in download and not navigation. Now we know that main document URL didn't change.
// Download may use this knowledge for purposes unrelated to cookies, notably for setting file quarantine data.
frameLoader()->setOriginalURLForDownloadRequest(m_request);
- frameLoader()->client().convertMainResourceLoadToDownload(this, m_request, m_response);
+
+ SessionID sessionID = SessionID::defaultSessionID();
+ if (frame() && frame()->page())
+ sessionID = frame()->page()->sessionID();
+
+ if (m_request.url().protocolIsData()) {
+ // We decode data URL internally, there is no resource load to convert.
+ frameLoader()->client().startDownload(m_request);
+ } else
+ frameLoader()->client().convertMainResourceLoadToDownload(this, sessionID, m_request, m_response);
// It might have gone missing
if (mainResourceLoader())
- mainResourceLoader()->didFail(interruptedForPolicyChangeError());
+ static_cast<ResourceLoader*>(mainResourceLoader())->didFail(interruptedForPolicyChangeError());
return;
}
case PolicyIgnore:
if (ResourceLoader* mainResourceLoader = this->mainResourceLoader())
- InspectorInstrumentation::continueWithPolicyIgnore(m_frame, this, mainResourceLoader->identifier(), m_response);
+ InspectorInstrumentation::continueWithPolicyIgnore(*m_frame, mainResourceLoader->identifier(), *this, m_response);
stopLoadingForPolicyChange();
return;
-
- default:
- ASSERT_NOT_REACHED();
}
if (m_response.isHTTP()) {
- int status = m_response.httpStatusCode();
- if (status < 200 || status >= 300) {
+ int status = m_response.httpStatusCode(); // Status may be zero when loading substitute data, in particular from a WebArchive.
+ if (status && (status < 200 || status >= 300)) {
bool hostedByObject = frameLoader()->isHostedByObjectElement();
frameLoader()->handleFallbackContent();
@@ -747,9 +807,10 @@ void DocumentLoader::continueAfterContentPolicy(PolicyAction policy)
}
}
- if (!isStopping() && m_substituteData.isValid()) {
- if (m_substituteData.content()->size())
- dataReceived(0, m_substituteData.content()->data(), m_substituteData.content()->size());
+ if (!isStopping() && m_substituteData.isValid() && isLoadingMainResource()) {
+ auto content = m_substituteData.content();
+ if (content && content->size())
+ dataReceived(content->data(), content->size());
if (isLoadingMainResource())
finishedLoading(0);
}
@@ -759,8 +820,8 @@ void DocumentLoader::commitLoad(const char* data, int length)
{
// Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
// by starting a new load, so retain temporarily.
- RefPtr<Frame> protectFrame(m_frame);
- Ref<DocumentLoader> protectLoader(*this);
+ RefPtr<Frame> protectedFrame(m_frame);
+ Ref<DocumentLoader> protectedThis(*this);
commitIfReady();
FrameLoader* frameLoader = DocumentLoader::frameLoader();
@@ -771,6 +832,9 @@ void DocumentLoader::commitLoad(const char* data, int length)
return;
#endif
frameLoader->client().committedLoad(this, data, length);
+
+ if (isMultipartReplacingLoad())
+ frameLoader->client().didReplaceMultipartContent();
}
ResourceError DocumentLoader::interruptedForPolicyChangeError() const
@@ -781,7 +845,7 @@ ResourceError DocumentLoader::interruptedForPolicyChangeError() const
void DocumentLoader::stopLoadingForPolicyChange()
{
ResourceError error = interruptedForPolicyChangeError();
- error.setIsCancellation(true);
+ error.setType(ResourceError::Type::Cancellation);
cancelMainResourceLoad(error);
}
@@ -797,16 +861,14 @@ void DocumentLoader::commitData(const char* bytes, size_t length)
// load local resources. See https://bugs.webkit.org/show_bug.cgi?id=16756
// and https://bugs.webkit.org/show_bug.cgi?id=19760 for further
// discussion.
- m_frame->document()->securityOrigin()->grantLoadLocalResources();
+ m_frame->document()->securityOrigin().grantLoadLocalResources();
}
- if (frameLoader()->stateMachine()->creatingInitialEmptyDocument())
+ if (frameLoader()->stateMachine().creatingInitialEmptyDocument())
return;
-
-#if ENABLE(MHTML)
- // The origin is the MHTML file, we need to set the base URL to the document encoded in the MHTML so
- // relative URLs are resolved properly.
- if (m_archive && m_archive->type() == Archive::MHTML)
+
+#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
+ if (m_archive && m_archive->shouldOverrideBaseURL())
m_frame->document()->setBaseURLOverride(m_archive->mainResource()->url());
#endif
@@ -815,21 +877,17 @@ void DocumentLoader::commitData(const char* bytes, size_t length)
if (!isMultipartReplacingLoad())
frameLoader()->receivedFirstData();
+ // The load could be canceled under receivedFirstData(), which makes delegate calls and even sometimes dispatches DOM events.
+ if (!isLoading())
+ return;
+
bool userChosen;
String encoding;
-#if USE(CONTENT_FILTERING)
- // The content filter's replacement data has a known encoding that might
- // differ from the response's encoding.
- if (m_contentFilter && m_contentFilter->didBlockData()) {
- ASSERT(!m_contentFilter->needsMoreData());
- userChosen = false;
- } else
-#endif
if (overrideEncoding().isNull()) {
userChosen = false;
encoding = response().textEncodingName();
-#if ENABLE(WEB_ARCHIVE)
- if (m_archive && m_archive->type() == Archive::WebArchive)
+#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
+ if (m_archive && m_archive->shouldUseMainResourceEncoding())
encoding = m_archive->mainResource()->textEncoding();
#endif
} else {
@@ -839,68 +897,64 @@ void DocumentLoader::commitData(const char* bytes, size_t length)
m_writer.setEncoding(encoding, userChosen);
}
+
+#if ENABLE(CONTENT_EXTENSIONS)
+ auto& extensionStyleSheets = m_frame->document()->extensionStyleSheets();
+
+ for (auto& pendingStyleSheet : m_pendingNamedContentExtensionStyleSheets)
+ extensionStyleSheets.maybeAddContentExtensionSheet(pendingStyleSheet.key, *pendingStyleSheet.value);
+ for (auto& pendingSelectorEntry : m_pendingContentExtensionDisplayNoneSelectors) {
+ for (const auto& pendingSelector : pendingSelectorEntry.value)
+ extensionStyleSheets.addDisplayNoneSelector(pendingSelectorEntry.key, pendingSelector.first, pendingSelector.second);
+ }
+
+ m_pendingNamedContentExtensionStyleSheets.clear();
+ m_pendingContentExtensionDisplayNoneSelectors.clear();
+#endif
+
ASSERT(m_frame->document()->parsing());
m_writer.addData(bytes, length);
}
-void DocumentLoader::dataReceived(CachedResource* resource, const char* data, int length)
+void DocumentLoader::dataReceived(CachedResource& resource, const char* data, int length)
+{
+ ASSERT_UNUSED(resource, &resource == m_mainResource);
+ dataReceived(data, length);
+}
+
+void DocumentLoader::dataReceived(const char* data, int length)
{
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter && !m_contentFilter->continueAfterDataReceived(data, length))
+ return;
+#endif
+
ASSERT(data);
ASSERT(length);
- ASSERT_UNUSED(resource, resource == m_mainResource);
ASSERT(!m_response.isNull());
-#if USE(CFNETWORK) || PLATFORM(MAC)
- // Workaround for <rdar://problem/6060782>
- if (m_response.isNull())
- m_response = ResourceResponse(URL(), "text/html", 0, String(), String());
-#endif
-
// There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
// See <rdar://problem/6304600> for more details.
#if !USE(CF)
ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading());
#endif
-#if USE(CONTENT_FILTERING)
- bool loadWasBlockedBeforeFinishing = false;
- if (m_contentFilter && m_contentFilter->needsMoreData()) {
- m_contentFilter->addData(data, length);
-
- if (m_contentFilter->needsMoreData()) {
- // Since the filter still needs more data to make a decision,
- // avoid committing this data to prevent partial rendering of
- // content that might later be blocked.
- return;
- }
-
- data = m_contentFilter->getReplacementData(length);
- loadWasBlockedBeforeFinishing = m_contentFilter->didBlockData();
-
- if (loadWasBlockedBeforeFinishing)
- setContentFilterForBlockedLoad(m_contentFilter);
- }
-#endif
-
if (m_identifierForLoadWithoutResourceLoader)
frameLoader()->notifier().dispatchDidReceiveData(this, m_identifierForLoadWithoutResourceLoader, data, length, -1);
m_applicationCacheHost->mainResourceDataReceived(data, length, -1, false);
- m_timeOfLastDataReceived = monotonicallyIncreasingTime();
+ m_timeOfLastDataReceived = MonotonicTime::now();
if (!isMultipartReplacingLoad())
commitLoad(data, length);
-
-#if USE(CONTENT_FILTERING)
- if (loadWasBlockedBeforeFinishing)
- cancelMainResourceLoad(frameLoader()->cancelledError(m_request));
-#endif
}
void DocumentLoader::setupForReplace()
{
if (!mainResourceData())
return;
+
+ frameLoader()->client().willReplaceMultipartContent();
maybeFinishLoadingMultipartContent();
maybeCreateArchive();
@@ -924,14 +978,19 @@ void DocumentLoader::checkLoadComplete()
m_frame->document()->domWindow()->finishedLoading();
}
-void DocumentLoader::setFrame(Frame* frame)
+void DocumentLoader::attachToFrame(Frame& frame)
{
- if (m_frame == frame)
+ if (m_frame == &frame)
return;
- ASSERT(frame && !m_frame);
- m_frame = frame;
- m_writer.setFrame(frame);
+
+ ASSERT(!m_frame);
+ m_frame = &frame;
+ m_writer.setFrame(&frame);
attachToFrame();
+
+#ifndef NDEBUG
+ m_hasEverBeenAttached = true;
+#endif
}
void DocumentLoader::attachToFrame()
@@ -941,30 +1000,43 @@ void DocumentLoader::attachToFrame()
void DocumentLoader::detachFromFrame()
{
- ASSERT(m_frame);
- RefPtr<Frame> protectFrame(m_frame);
- Ref<DocumentLoader> protectLoader(*this);
+#ifndef NDEBUG
+ if (m_hasEverBeenAttached)
+ ASSERT_WITH_MESSAGE(m_frame, "detachFromFrame() is being called on a DocumentLoader twice without an attachToFrame() inbetween");
+ else
+ ASSERT_WITH_MESSAGE(m_frame, "detachFromFrame() is being called on a DocumentLoader that has never attached to any Frame");
+#endif
+ RefPtr<Frame> protectedFrame(m_frame);
+ Ref<DocumentLoader> protectedThis(*this);
// It never makes sense to have a document loader that is detached from its
- // frame have any loads active, so go ahead and kill all the loads.
+ // frame have any loads active, so kill all the loads.
stopLoading();
- if (m_mainResource && m_mainResource->hasClient(this))
- m_mainResource->removeClient(this);
+ if (m_mainResource && m_mainResource->hasClient(*this))
+ m_mainResource->removeClient(*this);
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter)
+ m_contentFilter->stopFilteringMainResource();
+#endif
- m_applicationCacheHost->setDOMApplicationCache(0);
- InspectorInstrumentation::loaderDetachedFromFrame(m_frame, this);
- m_frame = 0;
+ m_applicationCacheHost->setDOMApplicationCache(nullptr);
+
+ cancelPolicyCheckIfNeeded();
+
+ // Even though we ASSERT at the top of this method that we have an m_frame, we're seeing crashes where m_frame is null.
+ // This means either that a DocumentLoader is detaching twice, or is detaching before ever having attached.
+ // Until we figure out how that is happening, null check m_frame before dereferencing it here.
+ // <rdar://problem/21293082> and https://bugs.webkit.org/show_bug.cgi?id=146786
+ if (m_frame)
+ InspectorInstrumentation::loaderDetachedFromFrame(*m_frame, *this);
+
+ m_frame = nullptr;
}
void DocumentLoader::clearMainResourceLoader()
{
m_loadingMainResource = false;
-#if PLATFORM(IOS)
- // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to ResourceRequest.h.
- m_request.setMainResourceRequest(false);
-#endif
-
if (this == frameLoader()->activeDocumentLoader())
checkLoadComplete();
}
@@ -976,15 +1048,19 @@ bool DocumentLoader::isLoadingInAPISense() const
if (frameLoader()->state() != FrameStateComplete) {
if (m_frame->settings().needsIsLoadingInAPISenseQuirk() && !m_subresourceLoaders.isEmpty())
return true;
-
- Document* doc = m_frame->document();
- if ((isLoadingMainResource() || !m_frame->document()->loadEventFinished()) && isLoading())
+
+ ASSERT(m_frame->document());
+ auto& document = *m_frame->document();
+ if ((isLoadingMainResource() || !document.loadEventFinished()) && isLoading())
return true;
if (m_cachedResourceLoader->requestCount())
return true;
- if (doc->processingLoadEvent())
+ if (document.processingLoadEvent())
+ return true;
+ if (document.hasActiveParser())
return true;
- if (doc->hasActiveParser())
+ auto* scriptableParser = document.scriptableDocumentParser();
+ if (scriptableParser && scriptableParser->hasScriptsWaitingForStylesheets())
return true;
}
return frameLoader()->subframeIsLoading();
@@ -995,65 +1071,55 @@ bool DocumentLoader::maybeCreateArchive()
#if !ENABLE(WEB_ARCHIVE) && !ENABLE(MHTML)
return false;
#else
-
// Give the archive machinery a crack at this document. If the MIME type is not an archive type, it will return 0.
- RefPtr<ResourceBuffer> mainResourceBuffer = mainResourceData();
- m_archive = ArchiveFactory::create(m_response.url(), mainResourceBuffer ? mainResourceBuffer->sharedBuffer() : 0, m_response.mimeType());
+ m_archive = ArchiveFactory::create(m_response.url(), mainResourceData().get(), m_response.mimeType());
if (!m_archive)
return false;
- addAllArchiveResources(m_archive.get());
- ArchiveResource* mainResource = m_archive->mainResource();
- m_parsedArchiveData = mainResource->data();
- m_writer.setMIMEType(mainResource->mimeType());
-
+ addAllArchiveResources(*m_archive);
+ ASSERT(m_archive->mainResource());
+ auto& mainResource = *m_archive->mainResource();
+ m_parsedArchiveData = &mainResource.data();
+ m_writer.setMIMEType(mainResource.mimeType());
+
ASSERT(m_frame->document());
- commitData(mainResource->data()->data(), mainResource->data()->size());
+ commitData(mainResource.data().data(), mainResource.data().size());
return true;
-#endif // !ENABLE(WEB_ARCHIVE) && !ENABLE(MHTML)
+#endif
}
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
-void DocumentLoader::setArchive(PassRefPtr<Archive> archive)
+
+void DocumentLoader::setArchive(Ref<Archive>&& archive)
{
- m_archive = archive;
- addAllArchiveResources(m_archive.get());
+ m_archive = WTFMove(archive);
+ addAllArchiveResources(*m_archive);
}
-void DocumentLoader::addAllArchiveResources(Archive* archive)
+void DocumentLoader::addAllArchiveResources(Archive& archive)
{
if (!m_archiveResourceCollection)
- m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection);
-
- ASSERT(archive);
- if (!archive)
- return;
-
+ m_archiveResourceCollection = std::make_unique<ArchiveResourceCollection>();
m_archiveResourceCollection->addAllResources(archive);
}
// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on.
// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps?
-void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource)
+void DocumentLoader::addArchiveResource(Ref<ArchiveResource>&& resource)
{
if (!m_archiveResourceCollection)
- m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection);
-
- ASSERT(resource);
- if (!resource)
- return;
-
- m_archiveResourceCollection->addResource(resource);
+ m_archiveResourceCollection = std::make_unique<ArchiveResourceCollection>();
+ m_archiveResourceCollection->addResource(WTFMove(resource));
}
-PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName, const URL& url)
+RefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName, const URL& url)
{
- return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName, url) : PassRefPtr<Archive>(0);
+ return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName, url) : nullptr;
}
void DocumentLoader::clearArchiveResources()
{
- m_archiveResourceCollection.clear();
+ m_archiveResourceCollection = nullptr;
m_substituteResourceDeliveryTimer.stop();
}
@@ -1061,116 +1127,88 @@ SharedBuffer* DocumentLoader::parsedArchiveData() const
{
return m_parsedArchiveData.get();
}
+
#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
ArchiveResource* DocumentLoader::archiveResourceForURL(const URL& url) const
{
if (!m_archiveResourceCollection)
- return 0;
-
- ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url);
-
- return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0;
+ return nullptr;
+ auto* resource = m_archiveResourceCollection->archiveResourceForURL(url);
+ if (!resource || resource->shouldIgnoreWhenUnarchiving())
+ return nullptr;
+ return resource;
}
-PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const
+RefPtr<ArchiveResource> DocumentLoader::mainResource() const
{
- const ResourceResponse& r = response();
-
- RefPtr<ResourceBuffer> mainResourceBuffer = mainResourceData();
- RefPtr<SharedBuffer> data = mainResourceBuffer ? mainResourceBuffer->sharedBuffer() : 0;
+ RefPtr<SharedBuffer> data = mainResourceData();
if (!data)
data = SharedBuffer::create();
-
- return ArchiveResource::create(data, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree().uniqueName());
+ auto& response = this->response();
+ return ArchiveResource::create(WTFMove(data), response.url(), response.mimeType(), response.textEncodingName(), frame()->tree().uniqueName());
}
-PassRefPtr<ArchiveResource> DocumentLoader::subresource(const URL& url) const
+RefPtr<ArchiveResource> DocumentLoader::subresource(const URL& url) const
{
if (!isCommitted())
- return 0;
+ return nullptr;
- CachedResource* resource = m_cachedResourceLoader->cachedResource(url);
+ auto* resource = m_cachedResourceLoader->cachedResource(url);
if (!resource || !resource->isLoaded())
return archiveResourceForURL(url);
if (resource->type() == CachedResource::MainResource)
- return 0;
+ return nullptr;
- // FIXME: This has the side effect of making the resource non-purgeable.
- // It would be better if it didn't have this permanent effect.
- if (!resource->makePurgeable(false))
- return 0;
-
- ResourceBuffer* data = resource->resourceBuffer();
+ auto* data = resource->resourceBuffer();
if (!data)
- return 0;
+ return nullptr;
- return ArchiveResource::create(data->sharedBuffer(), url, resource->response());
+ return ArchiveResource::create(data, url, resource->response());
}
-void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource>>& subresources) const
+Vector<Ref<ArchiveResource>> DocumentLoader::subresources() const
{
if (!isCommitted())
- return;
+ return { };
- const CachedResourceLoader::DocumentResourceMap& allResources = m_cachedResourceLoader->allCachedResources();
- CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
- for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
- RefPtr<ArchiveResource> subresource = this->subresource(URL(ParsedURLString, it->value->url()));
- if (subresource)
- subresources.append(subresource.release());
+ Vector<Ref<ArchiveResource>> subresources;
+ for (auto& handle : m_cachedResourceLoader->allCachedResources().values()) {
+ if (auto subresource = this->subresource({ ParsedURLString, handle->url() }))
+ subresources.append(subresource.releaseNonNull());
}
-
- return;
+ return subresources;
}
void DocumentLoader::deliverSubstituteResourcesAfterDelay()
{
if (m_pendingSubstituteResources.isEmpty())
return;
- ASSERT(m_frame && m_frame->page());
+ ASSERT(m_frame);
+ ASSERT(m_frame->page());
if (m_frame->page()->defersLoading())
return;
+
if (!m_substituteResourceDeliveryTimer.isActive())
m_substituteResourceDeliveryTimer.startOneShot(0);
}
-void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>&)
+void DocumentLoader::substituteResourceDeliveryTimerFired()
{
if (m_pendingSubstituteResources.isEmpty())
return;
- ASSERT(m_frame && m_frame->page());
+ ASSERT(m_frame);
+ ASSERT(m_frame->page());
if (m_frame->page()->defersLoading())
return;
- SubstituteResourceMap copy;
- copy.swap(m_pendingSubstituteResources);
-
- SubstituteResourceMap::const_iterator end = copy.end();
- for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) {
- RefPtr<ResourceLoader> loader = it->key;
- SubstituteResource* resource = it->value.get();
-
- if (resource) {
- SharedBuffer* data = resource->data();
-
- loader->didReceiveResponse(resource->response());
-
- // Calling ResourceLoader::didReceiveResponse can end up cancelling the load,
- // so we need to check if the loader has reached its terminal state.
- if (loader->reachedTerminalState())
- return;
-
- loader->didReceiveData(data->data(), data->size(), data->size(), DataPayloadWholeResource);
-
- // Calling ResourceLoader::didReceiveData can end up cancelling the load,
- // so we need to check if the loader has reached its terminal state.
- if (loader->reachedTerminalState())
- return;
-
- loader->didFinishLoading(0);
- } else {
+ auto pendingSubstituteResources = WTFMove(m_pendingSubstituteResources);
+ for (auto& pendingSubstituteResource : pendingSubstituteResources) {
+ auto& loader = pendingSubstituteResource.key;
+ if (auto& resource = pendingSubstituteResource.value)
+ resource->deliver(*loader);
+ else {
// A null resource means that we should fail the load.
// FIXME: Maybe we should use another error here - something like "not in cache".
loader->didFail(loader->cannotShowURLError());
@@ -1179,10 +1217,12 @@ void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>&
}
#ifndef NDEBUG
+
bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const
{
return m_pendingSubstituteResources.contains(loader);
}
+
#endif
void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader)
@@ -1195,37 +1235,41 @@ void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader)
}
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
-bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request)
+
+bool DocumentLoader::scheduleArchiveLoad(ResourceLoader& loader, const ResourceRequest& request)
{
- if (ArchiveResource* resource = archiveResourceForURL(request.url())) {
- m_pendingSubstituteResources.set(loader, resource);
- deliverSubstituteResourcesAfterDelay();
+ if (auto* resource = archiveResourceForURL(request.url())) {
+ scheduleSubstituteResourceLoad(loader, *resource);
return true;
}
if (!m_archive)
return false;
- switch (m_archive->type()) {
#if ENABLE(WEB_ARCHIVE)
- case Archive::WebArchive:
- // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive.
- return m_frame->settings().webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType());
+ // The idea of WebArchiveDebugMode is that we should fail instead of trying to fetch from the network.
+ // Returning true ensures the caller will not try to fetch from the network.
+ if (m_frame->settings().webArchiveDebugModeEnabled() && responseMIMEType() == "application/x-webarchive")
+ return true;
#endif
-#if ENABLE(MHTML)
- case Archive::MHTML:
- return true; // Always fail the load for resources not included in the MHTML.
+
+ // If we want to load from the archive only, then we should always return true so that the caller
+ // does not try to fetch form the network.
+ return m_archive->shouldLoadFromArchiveOnly();
+}
+
#endif
- default:
- return false;
- }
+
+void DocumentLoader::scheduleSubstituteResourceLoad(ResourceLoader& loader, SubstituteResource& resource)
+{
+ m_pendingSubstituteResources.set(&loader, &resource);
+ deliverSubstituteResourcesAfterDelay();
}
-#endif // ENABLE(WEB_ARCHIVE)
-void DocumentLoader::addResponse(const ResourceResponse& r)
+void DocumentLoader::addResponse(const ResourceResponse& response)
{
if (!m_stopRecordingResponses)
- m_responses.append(r);
+ m_responses.append(response);
}
void DocumentLoader::stopRecordingResponses()
@@ -1260,52 +1304,29 @@ bool DocumentLoader::urlForHistoryReflectsFailure() const
return m_substituteData.isValid() || m_response.httpStatusCode() >= 400;
}
-const URL& DocumentLoader::originalURL() const
-{
- return m_originalRequestCopy.url();
-}
-
-const URL& DocumentLoader::requestURL() const
-{
- return request().url();
-}
-
-const URL& DocumentLoader::responseURL() const
-{
- return m_response.url();
-}
-
URL DocumentLoader::documentURL() const
{
- URL url = substituteData().responseURL();
+ URL url = substituteData().response().url();
#if ENABLE(WEB_ARCHIVE)
- if (url.isEmpty() && m_archive && m_archive->type() == Archive::WebArchive)
+ if (url.isEmpty() && m_archive && m_archive->shouldUseMainResourceURL())
url = m_archive->mainResource()->url();
#endif
if (url.isEmpty())
- url = requestURL();
+ url = m_request.url();
if (url.isEmpty())
url = m_response.url();
return url;
}
-const String& DocumentLoader::responseMIMEType() const
-{
- return m_response.mimeType();
-}
-
#if PLATFORM(IOS)
+
// FIXME: This method seems to violate the encapsulation of this class.
void DocumentLoader::setResponseMIMEType(const String& responseMimeType)
{
m_response.setMimeType(responseMimeType);
}
-#endif
-const URL& DocumentLoader::unreachableURL() const
-{
- return m_substituteData.failingURL();
-}
+#endif
void DocumentLoader::setDefersLoading(bool defers)
{
@@ -1335,6 +1356,7 @@ void DocumentLoader::stopLoadingPlugIns()
void DocumentLoader::stopLoadingSubresources()
{
cancelAll(m_subresourceLoaders);
+ ASSERT(m_subresourceLoaders.isEmpty());
}
void DocumentLoader::addSubresourceLoader(ResourceLoader* loader)
@@ -1346,28 +1368,41 @@ void DocumentLoader::addSubresourceLoader(ResourceLoader* loader)
// if we are just starting the main resource load.
if (!m_gotFirstByte)
return;
- ASSERT(!m_subresourceLoaders.contains(loader));
+ ASSERT(loader->identifier());
+ ASSERT(!m_subresourceLoaders.contains(loader->identifier()));
ASSERT(!mainResourceLoader() || mainResourceLoader() != loader);
- m_subresourceLoaders.add(loader);
+
+ // A page in the PageCache or about to enter PageCache should not be able to start loads.
+ ASSERT_WITH_SECURITY_IMPLICATION(!document() || document()->pageCacheState() == Document::NotInPageCache);
+
+ m_subresourceLoaders.add(loader->identifier(), loader);
}
void DocumentLoader::removeSubresourceLoader(ResourceLoader* loader)
{
- if (!m_subresourceLoaders.remove(loader))
+ ASSERT(loader->identifier());
+
+ if (!m_subresourceLoaders.remove(loader->identifier()))
return;
checkLoadComplete();
if (Frame* frame = m_frame)
frame->loader().checkLoadComplete();
}
-void DocumentLoader::addPlugInStreamLoader(ResourceLoader* loader)
+void DocumentLoader::addPlugInStreamLoader(ResourceLoader& loader)
{
- m_plugInStreamLoaders.add(loader);
+ ASSERT(loader.identifier());
+ ASSERT(!m_plugInStreamLoaders.contains(loader.identifier()));
+
+ m_plugInStreamLoaders.add(loader.identifier(), &loader);
}
-void DocumentLoader::removePlugInStreamLoader(ResourceLoader* loader)
+void DocumentLoader::removePlugInStreamLoader(ResourceLoader& loader)
{
- m_plugInStreamLoaders.remove(loader);
+ ASSERT(loader.identifier());
+ ASSERT(&loader == m_plugInStreamLoaders.get(loader.identifier()));
+
+ m_plugInStreamLoaders.remove(loader.identifier());
checkLoadComplete();
}
@@ -1378,14 +1413,18 @@ bool DocumentLoader::isMultipartReplacingLoad() const
bool DocumentLoader::maybeLoadEmpty()
{
- bool shouldLoadEmpty = !m_substituteData.isValid() && (m_request.url().isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(m_request.url().protocol()));
- if (!shouldLoadEmpty && !frameLoader()->client().representationExistsForURLScheme(m_request.url().protocol()))
+ bool shouldLoadEmpty = !m_substituteData.isValid() && (m_request.url().isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(m_request.url().protocol().toStringWithoutCopying()));
+ if (!shouldLoadEmpty && !frameLoader()->client().representationExistsForURLScheme(m_request.url().protocol().toStringWithoutCopying()))
return false;
- if (m_request.url().isEmpty() && !frameLoader()->stateMachine()->creatingInitialEmptyDocument())
+ if (m_request.url().isEmpty() && !frameLoader()->stateMachine().creatingInitialEmptyDocument()) {
m_request.setURL(blankURL());
- String mimeType = shouldLoadEmpty ? "text/html" : frameLoader()->client().generatedMIMETypeForURLScheme(m_request.url().protocol());
- m_response = ResourceResponse(m_request.url(), mimeType, 0, String(), String());
+ if (isLoadingMainResource())
+ frameLoader()->client().dispatchDidChangeProvisionalURL();
+ }
+
+ String mimeType = shouldLoadEmpty ? "text/html" : frameLoader()->client().generatedMIMETypeForURLScheme(m_request.url().protocol().toStringWithoutCopying());
+ m_response = ResourceResponse(m_request.url(), mimeType, 0, String());
finishedLoading(monotonicallyIncreasingTime());
return true;
}
@@ -1393,13 +1432,19 @@ bool DocumentLoader::maybeLoadEmpty()
void DocumentLoader::startLoadingMainResource()
{
m_mainDocumentError = ResourceError();
- timing()->markNavigationStart();
+ timing().markStartTimeAndFetchStart();
ASSERT(!m_mainResource);
ASSERT(!m_loadingMainResource);
m_loadingMainResource = true;
- if (maybeLoadEmpty())
+ if (maybeLoadEmpty()) {
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Returning empty document (frame = %p, main = %d)", m_frame, m_frame ? m_frame->isMainFrame() : false);
return;
+ }
+
+#if ENABLE(CONTENT_FILTERING)
+ m_contentFilter = !m_substituteData.isValid() ? ContentFilter::create(*this) : nullptr;
+#endif
// FIXME: Is there any way the extra fields could have not been added by now?
// If not, it would be great to remove this line of code.
@@ -1407,18 +1452,22 @@ void DocumentLoader::startLoadingMainResource()
// because we pass a wrong loadType (see FIXME in addExtraFieldsToMainResourceRequest()).
frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
- ASSERT(timing()->navigationStart());
- ASSERT(!timing()->fetchStart());
- timing()->markFetchStart();
+ ASSERT(timing().startTime());
+ ASSERT(timing().fetchStart());
+
+ Ref<DocumentLoader> protectedThis(*this); // willSendRequest() may deallocate the provisional loader (which may be us) if it cancels the load.
willSendRequest(m_request, ResourceResponse());
// willSendRequest() may lead to our Frame being detached or cancelling the load via nulling the ResourceRequest.
- if (!m_frame || m_request.isNull())
+ if (!m_frame || m_request.isNull()) {
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Load canceled after willSendRequest (frame = %p, main = %d)", m_frame, m_frame ? m_frame->isMainFrame() : false);
return;
+ }
m_applicationCacheHost->maybeLoadMainResource(m_request, m_substituteData);
- if (m_substituteData.isValid()) {
+ if (m_substituteData.isValid() && m_frame->page()) {
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Returning cached main resource (frame = %p, main = %d)", m_frame, m_frame->isMainFrame());
m_identifierForLoadWithoutResourceLoader = m_frame->page()->progress().createUniqueIdentifier();
frameLoader()->notifier().assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, this, m_request);
frameLoader()->notifier().dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, m_request, ResourceResponse());
@@ -1426,21 +1475,38 @@ void DocumentLoader::startLoadingMainResource()
return;
}
-#if PLATFORM(IOS)
- // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to ResourceRequest.h.
- m_request.setMainResourceRequest(true);
+ ResourceRequest request(m_request);
+ request.setRequester(ResourceRequest::Requester::Main);
+ // If this is a reload the cache layer might have made the previous request conditional. DocumentLoader can't handle 304 responses itself.
+ request.makeUnconditional();
+
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Starting load (frame = %p, main = %d)", m_frame, m_frame->isMainFrame());
+
+ static NeverDestroyed<ResourceLoaderOptions> mainResourceLoadOptions(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientCredentialPolicy::MayAskClientForCredentials, FetchOptions::Credentials::Include, SkipSecurityCheck, FetchOptions::Mode::NoCors, IncludeCertificateInfo, ContentSecurityPolicyImposition::SkipPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, CachingPolicy::AllowCaching);
+ m_mainResource = m_cachedResourceLoader->requestMainResource(CachedResourceRequest(ResourceRequest(request), mainResourceLoadOptions));
+
+#if ENABLE(CONTENT_EXTENSIONS)
+ if (m_mainResource && m_mainResource->errorOccurred() && m_frame->page() && m_mainResource->resourceError().domain() == ContentExtensions::WebKitContentBlockerDomain) {
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Blocked by content blocker error (frame = %p, main = %d)", m_frame, m_frame->isMainFrame());
+ cancelMainResourceLoad(frameLoader()->blockedByContentBlockerError(m_request));
+ return;
+ }
#endif
- ResourceRequest request(m_request);
- static NeverDestroyed<ResourceLoaderOptions> mainResourceLoadOptions(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType);
- CachedResourceRequest cachedResourceRequest(request, mainResourceLoadOptions);
- m_mainResource = m_cachedResourceLoader->requestMainResource(cachedResourceRequest);
if (!m_mainResource) {
+ if (!m_request.url().isValid()) {
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Unable to load main resource, URL is invalid (frame = %p, main = %d)", m_frame, m_frame->isMainFrame());
+ cancelMainResourceLoad(frameLoader()->client().cannotShowURLError(m_request));
+ return;
+ }
+
+ RELEASE_LOG_IF_ALLOWED("startLoadingMainResource: Unable to load main resource, returning empty document (frame = %p, main = %d)", m_frame, m_frame->isMainFrame());
+
setRequest(ResourceRequest());
// If the load was aborted by clearing m_request, it's possible the ApplicationCacheHost
// is now in a state where starting an empty load will be inconsistent. Replace it with
// a new ApplicationCacheHost.
- m_applicationCacheHost = adoptPtr(new ApplicationCacheHost(this));
+ m_applicationCacheHost = std::make_unique<ApplicationCacheHost>(*this);
maybeLoadEmpty();
return;
}
@@ -1450,7 +1516,8 @@ void DocumentLoader::startLoadingMainResource()
frameLoader()->notifier().assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, this, request);
frameLoader()->notifier().dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
}
- m_mainResource->addClient(this);
+
+ becomeMainResourceClient();
// A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those.
if (mainResourceLoader())
@@ -1462,17 +1529,25 @@ void DocumentLoader::startLoadingMainResource()
setRequest(request);
}
-void DocumentLoader::cancelMainResourceLoad(const ResourceError& resourceError)
+void DocumentLoader::cancelPolicyCheckIfNeeded()
{
- Ref<DocumentLoader> protect(*this);
- ResourceError error = resourceError.isNull() ? frameLoader()->cancelledError(m_request) : resourceError;
+ RELEASE_ASSERT(frameLoader());
- m_dataLoadTimer.stop();
- if (m_waitingForContentPolicy) {
+ if (m_waitingForContentPolicy || m_waitingForNavigationPolicy) {
frameLoader()->policyChecker().cancelCheck();
- ASSERT(m_waitingForContentPolicy);
m_waitingForContentPolicy = false;
+ m_waitingForNavigationPolicy = false;
}
+}
+
+void DocumentLoader::cancelMainResourceLoad(const ResourceError& resourceError)
+{
+ Ref<DocumentLoader> protectedThis(*this);
+ ResourceError error = resourceError.isNull() ? frameLoader()->cancelledError(m_request) : resourceError;
+
+ m_dataLoadTimer.stop();
+
+ cancelPolicyCheckIfNeeded();
if (mainResourceLoader())
mainResourceLoader()->cancel(error);
@@ -1482,18 +1557,36 @@ void DocumentLoader::cancelMainResourceLoad(const ResourceError& resourceError)
mainReceivedError(error);
}
+void DocumentLoader::willContinueMainResourceLoadAfterRedirect(const ResourceRequest& newRequest)
+{
+ setRequest(newRequest);
+}
+
void DocumentLoader::clearMainResource()
{
- if (m_mainResource && m_mainResource->hasClient(this))
- m_mainResource->removeClient(this);
+ if (m_mainResource && m_mainResource->hasClient(*this))
+ m_mainResource->removeClient(*this);
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter)
+ m_contentFilter->stopFilteringMainResource();
+#endif
- m_mainResource = 0;
+ m_mainResource = nullptr;
}
void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
{
- m_multipartSubresourceLoaders.add(loader);
- m_subresourceLoaders.remove(loader);
+ unsigned long identifier = loader->identifier();
+ ASSERT(identifier);
+
+ if (!m_multipartSubresourceLoaders.add(identifier, loader).isNewEntry) {
+ ASSERT(m_multipartSubresourceLoaders.get(identifier) == loader);
+ ASSERT(!m_subresourceLoaders.contains(identifier));
+ } else {
+ ASSERT(m_subresourceLoaders.contains(identifier));
+ m_subresourceLoaders.remove(identifier);
+ }
+
checkLoadComplete();
if (Frame* frame = m_frame)
frame->loader().checkLoadComplete();
@@ -1506,7 +1599,7 @@ void DocumentLoader::maybeFinishLoadingMultipartContent()
frameLoader()->setupForReplace();
m_committed = false;
- RefPtr<ResourceBuffer> resourceData = mainResourceData();
+ RefPtr<SharedBuffer> resourceData = mainResourceData();
commitLoad(resourceData->data(), resourceData->size());
}
@@ -1526,13 +1619,13 @@ void DocumentLoader::getIconLoadDecisionForIconURL(const String& urlString)
if (m_iconLoadDecisionCallback)
m_iconLoadDecisionCallback->invalidate();
m_iconLoadDecisionCallback = IconLoadDecisionCallback::create(this, iconLoadDecisionCallback);
- iconDatabase().loadDecisionForIconURL(urlString, m_iconLoadDecisionCallback);
+ iconDatabase().loadDecisionForIconURL(urlString, *m_iconLoadDecisionCallback);
}
void DocumentLoader::continueIconLoadWithDecision(IconLoadDecision decision)
{
ASSERT(m_iconLoadDecisionCallback);
- m_iconLoadDecisionCallback = 0;
+ m_iconLoadDecisionCallback = nullptr;
if (m_frame)
m_frame->loader().icon().continueLoadWithDecision(decision);
}
@@ -1547,47 +1640,109 @@ void DocumentLoader::getIconDataForIconURL(const String& urlString)
if (m_iconDataCallback)
m_iconDataCallback->invalidate();
m_iconDataCallback = IconDataCallback::create(this, iconDataCallback);
- iconDatabase().iconDataForIconURL(urlString, m_iconDataCallback);
+ iconDatabase().iconDataForIconURL(urlString, *m_iconDataCallback);
}
-void DocumentLoader::handledOnloadEvents()
+void DocumentLoader::startIconLoading()
{
- m_wasOnloadHandled = true;
- applicationCacheHost()->stopDeferringEvents();
+ ASSERT(m_frame->loader().client().useIconLoadingClient());
+
+ static uint64_t nextIconCallbackID = 1;
+
+ auto* document = this->document();
+ if (!document)
+ return;
+
+ Vector<LinkIcon> icons = LinkIconCollector { *document }.iconsOfTypes({ LinkIconType::Favicon, LinkIconType::TouchIcon, LinkIconType::TouchPrecomposedIcon });
+
+ if (icons.isEmpty())
+ icons.append({ m_frame->document()->completeURL(ASCIILiteral("/favicon.ico")), LinkIconType::Favicon, String(), std::nullopt });
+
+ for (auto& icon : icons) {
+ auto result = m_iconsPendingLoadDecision.add(nextIconCallbackID++, icon);
+ m_frame->loader().client().getLoadDecisionForIcon(icon, result.iterator->key);
+ }
}
-#if USE(CONTENT_FILTERING)
-void DocumentLoader::setContentFilterForBlockedLoad(PassRefPtr<ContentFilter> contentFilter)
+void DocumentLoader::didGetLoadDecisionForIcon(bool decision, uint64_t loadIdentifier, uint64_t newCallbackID)
{
- ASSERT(!m_contentFilterForBlockedLoad);
- ASSERT(contentFilter);
- ASSERT(contentFilter->didBlockData());
- m_contentFilterForBlockedLoad = contentFilter;
+ auto icon = m_iconsPendingLoadDecision.take(loadIdentifier);
+ if (!decision || icon.url.isEmpty() || !m_frame)
+ return;
+
+ auto iconLoader = std::make_unique<IconLoader>(*this, icon.url);
+ iconLoader->startLoading();
+ m_iconLoaders.set(WTFMove(iconLoader), newCallbackID);
}
-bool DocumentLoader::handleContentFilterRequest(const ResourceRequest& request)
+void DocumentLoader::finishedLoadingIcon(IconLoader& loader, SharedBuffer* buffer)
{
- // FIXME: Remove PLATFORM(IOS)-guard once we upstream ContentFilterIOS.mm and
- // implement ContentFilter::requestUnblockAndDispatchIfSuccessful() for Mac.
-#if PLATFORM(IOS)
- if (!m_contentFilterForBlockedLoad)
- return false;
+ auto loadIdentifier = m_iconLoaders.take(&loader);
+ ASSERT(loadIdentifier);
- if (!request.url().protocolIs(ContentFilter::scheme()))
- return false;
+ m_frame->loader().client().finishedLoadingIcon(loadIdentifier, buffer);
+}
- if (equalIgnoringCase(request.url().host(), "unblock")) {
- // Tell the FrameLoader to reload if the unblock is successful.
- m_contentFilterForBlockedLoad->requestUnblockAndDispatchIfSuccessful(bind(&FrameLoader::reload, &(m_frame->loader()), false));
- return true;
- }
+void DocumentLoader::dispatchOnloadEvents()
+{
+ m_wasOnloadDispatched = true;
+ m_applicationCacheHost->stopDeferringEvents();
+}
- return false;
-#else
- UNUSED_PARAM(request);
- return false;
+void DocumentLoader::setTriggeringAction(const NavigationAction& action)
+{
+ m_triggeringAction = action.copyWithShouldOpenExternalURLsPolicy(m_frame ? shouldOpenExternalURLsPolicyToPropagate() : m_shouldOpenExternalURLsPolicy);
+}
+
+ShouldOpenExternalURLsPolicy DocumentLoader::shouldOpenExternalURLsPolicyToPropagate() const
+{
+ if (!m_frame || !m_frame->isMainFrame())
+ return ShouldOpenExternalURLsPolicy::ShouldNotAllow;
+
+ return m_shouldOpenExternalURLsPolicy;
+}
+
+void DocumentLoader::becomeMainResourceClient()
+{
+#if ENABLE(CONTENT_FILTERING)
+ if (m_contentFilter)
+ m_contentFilter->startFilteringMainResource(*m_mainResource);
#endif
+ m_mainResource->addClient(*this);
+}
+
+#if ENABLE(CONTENT_EXTENSIONS)
+void DocumentLoader::addPendingContentExtensionSheet(const String& identifier, StyleSheetContents& sheet)
+{
+ ASSERT(!m_gotFirstByte);
+ m_pendingNamedContentExtensionStyleSheets.set(identifier, &sheet);
+}
+
+void DocumentLoader::addPendingContentExtensionDisplayNoneSelector(const String& identifier, const String& selector, uint32_t selectorID)
+{
+ ASSERT(!m_gotFirstByte);
+ auto addResult = m_pendingContentExtensionDisplayNoneSelectors.add(identifier, Vector<std::pair<String, uint32_t>>());
+ addResult.iterator->value.append(std::make_pair(selector, selectorID));
}
#endif
+bool DocumentLoader::isAlwaysOnLoggingAllowed() const
+{
+ return !m_frame || m_frame->isAlwaysOnLoggingAllowed();
+}
+
+#if USE(QUICK_LOOK)
+
+void DocumentLoader::setPreviewConverter(std::unique_ptr<PreviewConverter>&& previewConverter)
+{
+ m_previewConverter = WTFMove(previewConverter);
+}
+
+PreviewConverter* DocumentLoader::previewConverter() const
+{
+ return m_previewConverter.get();
+}
+
+#endif
+
} // namespace WebCore