WebLoaderStrategy.cpp [plain text]
#include "config.h"
#include "WebLoaderStrategy.h"
#include "HangDetectionDisabler.h"
#include "Logging.h"
#include "NetworkConnectionToWebProcessMessages.h"
#include "NetworkProcessConnection.h"
#include "NetworkResourceLoadParameters.h"
#include "SessionTracker.h"
#include "WebCoreArgumentCoders.h"
#include "WebErrors.h"
#include "WebFrame.h"
#include "WebFrameLoaderClient.h"
#include "WebFrameNetworkingContext.h"
#include "WebPage.h"
#include "WebProcess.h"
#include "WebResourceLoader.h"
#include <WebCore/ApplicationCacheHost.h>
#include <WebCore/CachedResource.h>
#include <WebCore/DiagnosticLoggingClient.h>
#include <WebCore/DiagnosticLoggingKeys.h>
#include <WebCore/Document.h>
#include <WebCore/DocumentLoader.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameLoader.h>
#include <WebCore/NetscapePlugInStreamLoader.h>
#include <WebCore/PlatformStrategies.h>
#include <WebCore/ReferrerPolicy.h>
#include <WebCore/ResourceLoader.h>
#include <WebCore/SessionID.h>
#include <WebCore/Settings.h>
#include <WebCore/SubresourceLoader.h>
#include <wtf/text/CString.h>
using namespace WebCore;
#define RELEASE_LOG_IF_ALLOWED(...) RELEASE_LOG_IF(loadParameters.sessionID.isAlwaysOnLoggingAllowed(), __VA_ARGS__)
#define RELEASE_LOG_ERROR_IF_ALLOWED(...) RELEASE_LOG_ERROR_IF(loadParameters.sessionID.isAlwaysOnLoggingAllowed(), __VA_ARGS__)
namespace WebKit {
WebLoaderStrategy::WebLoaderStrategy()
: m_internallyFailedLoadTimer(RunLoop::main(), this, &WebLoaderStrategy::internallyFailedLoadTimerFired)
{
}
WebLoaderStrategy::~WebLoaderStrategy()
{
}
RefPtr<SubresourceLoader> WebLoaderStrategy::loadResource(Frame& frame, CachedResource& resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
{
RefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, resource, request, options);
if (loader)
scheduleLoad(*loader, &resource, frame.document()->referrerPolicy() == ReferrerPolicy::Default);
return loader;
}
RefPtr<NetscapePlugInStreamLoader> WebLoaderStrategy::schedulePluginStreamLoad(Frame& frame, NetscapePlugInStreamLoaderClient& client, const ResourceRequest& request)
{
RefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request);
if (loader)
scheduleLoad(*loader, 0, frame.document()->referrerPolicy() == ReferrerPolicy::Default);
return loader;
}
static std::chrono::milliseconds maximumBufferingTime(CachedResource* resource)
{
#if !ENABLE(NETWORK_CACHE)
return 0ms;
#endif
if (!resource)
return 0ms;
switch (resource->type()) {
case CachedResource::CSSStyleSheet:
case CachedResource::Script:
#if ENABLE(SVG_FONTS)
case CachedResource::SVGFontResource:
#endif
case CachedResource::FontResource:
return std::chrono::milliseconds::max();
case CachedResource::ImageResource:
return 500ms;
case CachedResource::MediaResource:
case CachedResource::MainResource:
case CachedResource::RawResource:
case CachedResource::SVGDocumentResource:
case CachedResource::LinkPreload:
#if ENABLE(LINK_PREFETCH)
case CachedResource::LinkPrefetch:
case CachedResource::LinkSubresource:
#endif
#if ENABLE(VIDEO_TRACK)
case CachedResource::TextTrackResource:
#endif
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
#endif
return 0ms;
}
ASSERT_NOT_REACHED();
return 0ms;
}
void WebLoaderStrategy::scheduleLoad(ResourceLoader& resourceLoader, CachedResource* resource, bool shouldClearReferrerOnHTTPSToHTTPRedirect)
{
ResourceLoadIdentifier identifier = resourceLoader.identifier();
ASSERT(identifier);
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
if (resourceLoader.documentLoader()->scheduleArchiveLoad(resourceLoader, resourceLoader.request())) {
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be handled as an archive resource.", resourceLoader.url().string().utf8().data());
m_webResourceLoaders.set(identifier, WebResourceLoader::create(resourceLoader));
return;
}
#endif
if (resourceLoader.documentLoader()->applicationCacheHost()->maybeLoadResource(resourceLoader, resourceLoader.request(), resourceLoader.request().url())) {
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be loaded from application cache.", resourceLoader.url().string().utf8().data());
m_webResourceLoaders.set(identifier, WebResourceLoader::create(resourceLoader));
return;
}
if (resourceLoader.request().url().protocolIsData()) {
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be loaded as data.", resourceLoader.url().string().utf8().data());
startLocalLoad(resourceLoader);
return;
}
#if USE(QUICK_LOOK)
if (resourceLoader.request().url().protocolIs(QLPreviewProtocol())) {
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be handled as a QuickLook resource.", resourceLoader.url().string().utf8().data());
startLocalLoad(resourceLoader);
return;
}
#endif
#if USE(SOUP)
if (resourceLoader.request().url().protocolIs("resource")) {
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be handled as a GResource.", resourceLoader.url().string().utf8().data());
startLocalLoad(resourceLoader);
return;
}
#endif
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be scheduled with the NetworkProcess with priority %d", resourceLoader.url().string().latin1().data(), static_cast<int>(resourceLoader.request().priority()));
ContentSniffingPolicy contentSniffingPolicy = resourceLoader.shouldSniffContent() ? SniffContent : DoNotSniffContent;
StoredCredentials allowStoredCredentials = resourceLoader.shouldUseCredentialStorage() ? AllowStoredCredentials : DoNotAllowStoredCredentials;
WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(resourceLoader.frameLoader()->client());
WebFrame* webFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : 0;
WebPage* webPage = webFrame ? webFrame->page() : 0;
NetworkResourceLoadParameters loadParameters;
loadParameters.identifier = identifier;
loadParameters.webPageID = webPage ? webPage->pageID() : 0;
loadParameters.webFrameID = webFrame ? webFrame->frameID() : 0;
loadParameters.sessionID = webPage ? webPage->sessionID() : SessionID::defaultSessionID();
loadParameters.request = resourceLoader.request();
loadParameters.contentSniffingPolicy = contentSniffingPolicy;
loadParameters.allowStoredCredentials = allowStoredCredentials;
loadParameters.clientCredentialPolicy = (webFrame && webPage && resourceLoader.isAllowedToAskUserForCredentials()) ? AskClientForAllCredentials : DoNotAskClientForAnyCredentials;
loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect = shouldClearReferrerOnHTTPSToHTTPRedirect;
loadParameters.defersLoading = resourceLoader.defersLoading();
loadParameters.needsCertificateInfo = resourceLoader.shouldIncludeCertificateInfo();
loadParameters.maximumBufferingTime = maximumBufferingTime(resource);
ASSERT((loadParameters.webPageID && loadParameters.webFrameID) || loadParameters.clientCredentialPolicy == DoNotAskClientForAnyCredentials);
if (!WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::ScheduleResourceLoad(loadParameters), 0)) {
RELEASE_LOG_ERROR_IF_ALLOWED("WebLoaderStrategy::scheduleLoad: Unable to schedule resource with the NetworkProcess with priority = %d, pageID = %llu, frameID = %llu", static_cast<int>(resourceLoader.request().priority()), static_cast<unsigned long long>(loadParameters.webPageID), static_cast<unsigned long long>(loadParameters.webFrameID));
scheduleInternallyFailedLoad(resourceLoader);
return;
}
auto webResourceLoader = WebResourceLoader::create(resourceLoader);
RELEASE_LOG_IF_ALLOWED("WebLoaderStrategy::scheduleLoad: Resource will be scheduled with the NetworkProcess with priority = %d, pageID = %llu, frameID = %llu, WebResourceLoader = %p", static_cast<int>(resourceLoader.request().priority()), static_cast<unsigned long long>(loadParameters.webPageID), static_cast<unsigned long long>(loadParameters.webFrameID), webResourceLoader.ptr());
m_webResourceLoaders.set(identifier, WTFMove(webResourceLoader));
}
void WebLoaderStrategy::scheduleInternallyFailedLoad(WebCore::ResourceLoader& resourceLoader)
{
m_internallyFailedResourceLoaders.add(&resourceLoader);
m_internallyFailedLoadTimer.startOneShot(0);
}
void WebLoaderStrategy::internallyFailedLoadTimerFired()
{
Vector<RefPtr<ResourceLoader>> internallyFailedResourceLoaders;
copyToVector(m_internallyFailedResourceLoaders, internallyFailedResourceLoaders);
for (size_t i = 0; i < internallyFailedResourceLoaders.size(); ++i)
internallyFailedResourceLoaders[i]->didFail(internalError(internallyFailedResourceLoaders[i]->url()));
}
void WebLoaderStrategy::startLocalLoad(WebCore::ResourceLoader& resourceLoader)
{
resourceLoader.start();
m_webResourceLoaders.set(resourceLoader.identifier(), WebResourceLoader::create(resourceLoader));
}
void WebLoaderStrategy::remove(ResourceLoader* resourceLoader)
{
ASSERT(resourceLoader);
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::remove, url '%s'", resourceLoader->url().string().utf8().data());
if (m_internallyFailedResourceLoaders.contains(resourceLoader)) {
m_internallyFailedResourceLoaders.remove(resourceLoader);
return;
}
ResourceLoadIdentifier identifier = resourceLoader->identifier();
if (!identifier) {
LOG_ERROR("WebLoaderStrategy removing a ResourceLoader that has no identifier.");
return;
}
RefPtr<WebResourceLoader> loader = m_webResourceLoaders.take(identifier);
if (!loader)
return;
WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::RemoveLoadIdentifier(identifier), 0);
loader->detachFromCoreLoader();
}
void WebLoaderStrategy::setDefersLoading(ResourceLoader* resourceLoader, bool defers)
{
ResourceLoadIdentifier identifier = resourceLoader->identifier();
WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetDefersLoading(identifier, defers), 0);
}
void WebLoaderStrategy::crossOriginRedirectReceived(ResourceLoader*, const URL&)
{
}
void WebLoaderStrategy::servePendingRequests(ResourceLoadPriority)
{
}
void WebLoaderStrategy::suspendPendingRequests()
{
}
void WebLoaderStrategy::resumePendingRequests()
{
}
void WebLoaderStrategy::networkProcessCrashed()
{
RELEASE_LOG_ERROR("WebLoaderStrategy::networkProcessCrashed: failing all pending resource loaders");
for (auto& loader : m_webResourceLoaders)
scheduleInternallyFailedLoad(*loader.value->resourceLoader());
m_webResourceLoaders.clear();
}
void WebLoaderStrategy::loadResourceSynchronously(NetworkingContext* context, unsigned long resourceLoadIdentifier, const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy clientCredentialPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
{
WebFrameNetworkingContext* webContext = static_cast<WebFrameNetworkingContext*>(context);
WebFrameLoaderClient* webFrameLoaderClient = webContext->webFrameLoaderClient();
WebFrame* webFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : 0;
WebPage* webPage = webFrame ? webFrame->page() : 0;
NetworkResourceLoadParameters loadParameters;
loadParameters.identifier = resourceLoadIdentifier;
loadParameters.webPageID = webPage ? webPage->pageID() : 0;
loadParameters.webFrameID = webFrame ? webFrame->frameID() : 0;
loadParameters.sessionID = webPage ? webPage->sessionID() : SessionID::defaultSessionID();
loadParameters.request = request;
loadParameters.contentSniffingPolicy = SniffContent;
loadParameters.allowStoredCredentials = storedCredentials;
loadParameters.clientCredentialPolicy = clientCredentialPolicy;
loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect = context->shouldClearReferrerOnHTTPSToHTTPRedirect();
data.resize(0);
HangDetectionDisabler hangDetectionDisabler;
if (!WebProcess::singleton().networkConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad(loadParameters), Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::Reply(error, response, data), 0)) {
RELEASE_LOG_ERROR_IF_ALLOWED("loadResourceSynchronously: failed sending synchronous network process message (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", loadParameters.webPageID, loadParameters.webFrameID, loadParameters.identifier);
if (auto* page = webPage->corePage())
page->diagnosticLoggingClient().logDiagnosticMessage(WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::synchronousMessageFailedKey(), WebCore::ShouldSample::No);
response = ResourceResponse();
error = internalError(request.url());
}
}
void WebLoaderStrategy::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, bool shouldUseCredentialStorage, bool shouldFollowRedirects)
{
if (!networkingContext)
return;
WebFrameNetworkingContext* webContext = static_cast<WebFrameNetworkingContext*>(networkingContext);
WebFrameLoaderClient* webFrameLoaderClient = webContext->webFrameLoaderClient();
WebFrame* webFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : nullptr;
WebPage* webPage = webFrame ? webFrame->page() : nullptr;
NetworkResourceLoadParameters loadParameters;
loadParameters.request = request;
loadParameters.sessionID = webPage ? webPage->sessionID() : SessionID::defaultSessionID();
loadParameters.allowStoredCredentials = shouldUseCredentialStorage ? AllowStoredCredentials : DoNotAllowStoredCredentials;
loadParameters.shouldFollowRedirects = shouldFollowRedirects;
loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect = networkingContext->shouldClearReferrerOnHTTPSToHTTPRedirect();
WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::LoadPing(loadParameters), 0);
}
}