diff options
Diffstat (limited to 'Source/WebCore/loader/DocumentLoader.cpp')
-rw-r--r-- | Source/WebCore/loader/DocumentLoader.cpp | 1121 |
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 |