blob: 546195538de0bb86c4c20dadd3e0decd438e763f [file] [log] [blame]
/*
* Copyright (C) 2012 Google, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "CachedResourceRequest.h"
#include "CachedResourceLoader.h"
#include "ContentExtensionActions.h"
#include "CrossOriginAccessControl.h"
#include "Document.h"
#include "Element.h"
#include "FrameLoader.h"
#include "HTTPHeaderValues.h"
#include "ImageDecoder.h"
#include "MemoryCache.h"
#include "SecurityPolicy.h"
#include "ServiceWorkerRegistrationData.h"
#include <wtf/NeverDestroyed.h>
namespace WebCore {
CachedResourceRequest::CachedResourceRequest(ResourceRequest&& resourceRequest, const ResourceLoaderOptions& options, Optional<ResourceLoadPriority> priority, String&& charset)
: m_resourceRequest(WTFMove(resourceRequest))
, m_charset(WTFMove(charset))
, m_options(options)
, m_priority(priority)
, m_fragmentIdentifier(splitFragmentIdentifierFromRequestURL(m_resourceRequest))
{
}
String CachedResourceRequest::splitFragmentIdentifierFromRequestURL(ResourceRequest& request)
{
if (!MemoryCache::shouldRemoveFragmentIdentifier(request.url()))
return { };
URL url = request.url();
String fragmentIdentifier = url.fragmentIdentifier();
url.removeFragmentIdentifier();
request.setURL(url);
return fragmentIdentifier;
}
void CachedResourceRequest::setInitiator(Element& element)
{
ASSERT(!m_initiatorElement);
ASSERT(m_initiatorName.isEmpty());
m_initiatorElement = &element;
}
void CachedResourceRequest::setInitiator(const AtomString& name)
{
ASSERT(!m_initiatorElement);
ASSERT(m_initiatorName.isEmpty());
m_initiatorName = name;
}
const AtomString& CachedResourceRequest::initiatorName() const
{
if (m_initiatorElement)
return m_initiatorElement->localName();
if (!m_initiatorName.isEmpty())
return m_initiatorName;
static NeverDestroyed<AtomString> defaultName("other", AtomString::ConstructFromLiteral);
return defaultName;
}
void CachedResourceRequest::updateForAccessControl(Document& document)
{
ASSERT(m_options.mode == FetchOptions::Mode::Cors);
m_origin = &document.securityOrigin();
updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.storedCredentialsPolicy);
}
void upgradeInsecureResourceRequestIfNeeded(ResourceRequest& request, Document& document)
{
URL url = request.url();
ASSERT(document.contentSecurityPolicy());
document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(url, ContentSecurityPolicy::InsecureRequestType::Load);
if (url == request.url())
return;
request.setURL(url);
}
void CachedResourceRequest::upgradeInsecureRequestIfNeeded(Document& document)
{
upgradeInsecureResourceRequestIfNeeded(m_resourceRequest, document);
}
void CachedResourceRequest::setDomainForCachePartition(Document& document)
{
m_resourceRequest.setDomainForCachePartition(document.domainForCachePartition());
}
void CachedResourceRequest::setDomainForCachePartition(const String& domain)
{
m_resourceRequest.setDomainForCachePartition(domain);
}
static inline String acceptHeaderValueFromType(CachedResource::Type type)
{
switch (type) {
case CachedResource::Type::MainResource:
return "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"_s;
case CachedResource::Type::ImageResource:
if (ImageDecoder::supportsMediaType(ImageDecoder::MediaType::Video))
return "image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5"_s;
return "image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5"_s;
case CachedResource::Type::CSSStyleSheet:
return "text/css,*/*;q=0.1"_s;
case CachedResource::Type::SVGDocumentResource:
return "image/svg+xml"_s;
#if ENABLE(XSLT)
case CachedResource::Type::XSLStyleSheet:
// FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
return "text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml"_s;
#endif
default:
return "*/*"_s;
}
}
void CachedResourceRequest::setAcceptHeaderIfNone(CachedResource::Type type)
{
if (!m_resourceRequest.hasHTTPHeader(HTTPHeaderName::Accept))
m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Accept, acceptHeaderValueFromType(type));
}
void CachedResourceRequest::updateAccordingCacheMode()
{
if (m_options.cache == FetchOptions::Cache::Default
&& (m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince)
|| m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch)
|| m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince)
|| m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfMatch)
|| m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfRange)))
m_options.cache = FetchOptions::Cache::NoStore;
switch (m_options.cache) {
case FetchOptions::Cache::NoCache:
m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::RefreshAnyCacheData);
m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0());
break;
case FetchOptions::Cache::NoStore:
m_options.cachingPolicy = CachingPolicy::DisallowCaching;
m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::DoNotUseAnyCache);
m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
break;
case FetchOptions::Cache::Reload:
m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
break;
case FetchOptions::Cache::Default:
break;
case FetchOptions::Cache::ForceCache:
m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataElseLoad);
break;
case FetchOptions::Cache::OnlyIfCached:
m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataDontLoad);
break;
}
}
void CachedResourceRequest::updateAcceptEncodingHeader()
{
if (!m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::Range))
return;
// FIXME: rdar://problem/40879225. Media engines triggering the load should not set this Accept-Encoding header.
ASSERT(!m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::AcceptEncoding) || m_options.destination == FetchOptions::Destination::Audio || m_options.destination == FetchOptions::Destination::Video);
m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::AcceptEncoding, "identity"_s);
}
void CachedResourceRequest::removeFragmentIdentifierIfNeeded()
{
URL url = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
if (url.string() != m_resourceRequest.url())
m_resourceRequest.setURL(url);
}
#if ENABLE(CONTENT_EXTENSIONS)
void CachedResourceRequest::applyResults(ContentRuleListResults&& results, Page* page)
{
ContentExtensions::applyResultsToRequest(WTFMove(results), page, m_resourceRequest);
}
#endif
void CachedResourceRequest::updateReferrerPolicy(ReferrerPolicy defaultPolicy)
{
if (m_options.referrerPolicy == ReferrerPolicy::EmptyString)
m_options.referrerPolicy = defaultPolicy;
}
void CachedResourceRequest::updateReferrerAndOriginHeaders(FrameLoader& frameLoader)
{
// Implementing step 9 to 11 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch as of 16 March 2018
String outgoingReferrer = frameLoader.outgoingReferrer();
if (m_resourceRequest.hasHTTPReferrer())
outgoingReferrer = m_resourceRequest.httpReferrer();
updateRequestReferrer(m_resourceRequest, m_options.referrerPolicy, outgoingReferrer);
if (doesRequestNeedHTTPOriginHeader(m_resourceRequest)) {
auto outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer);
// FIXME: Should take referrer-policy into account in some cases, see https://github.com/web-platform-tests/wpt/issues/22298.
auto referrerPolicy = (m_options.mode == FetchOptions::Mode::Cors) ? ReferrerPolicy::UnsafeUrl : m_options.referrerPolicy;
auto origin = SecurityPolicy::generateOriginHeader(referrerPolicy, m_resourceRequest.url(), outgoingOrigin);
m_resourceRequest.setHTTPOrigin(origin);
}
}
void CachedResourceRequest::updateUserAgentHeader(FrameLoader& frameLoader)
{
frameLoader.applyUserAgentIfNeeded(m_resourceRequest);
}
bool isRequestCrossOrigin(SecurityOrigin* origin, const URL& requestURL, const ResourceLoaderOptions& options)
{
if (!origin)
return false;
// Using same origin mode guarantees the loader will not do a cross-origin load, so we let it take care of it and just return false.
if (options.mode == FetchOptions::Mode::SameOrigin)
return false;
// FIXME: We should remove options.sameOriginDataURLFlag once https://github.com/whatwg/fetch/issues/393 is fixed.
if (requestURL.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set)
return false;
return !origin->canRequest(requestURL);
}
void CachedResourceRequest::setDestinationIfNotSet(FetchOptions::Destination destination)
{
if (m_options.destination != FetchOptions::Destination::EmptyString)
return;
m_options.destination = destination;
}
#if ENABLE(SERVICE_WORKER)
void CachedResourceRequest::setClientIdentifierIfNeeded(DocumentIdentifier clientIdentifier)
{
if (!m_options.clientIdentifier)
m_options.clientIdentifier = clientIdentifier;
}
void CachedResourceRequest::setSelectedServiceWorkerRegistrationIdentifierIfNeeded(ServiceWorkerRegistrationIdentifier identifier)
{
if (isNonSubresourceRequest(m_options.destination))
return;
if (isPotentialNavigationOrSubresourceRequest(m_options.destination))
return;
if (m_options.serviceWorkersMode == ServiceWorkersMode::None)
return;
if (m_options.serviceWorkerRegistrationIdentifier)
return;
m_options.serviceWorkerRegistrationIdentifier = identifier;
}
void CachedResourceRequest::setNavigationServiceWorkerRegistrationData(const Optional<ServiceWorkerRegistrationData>& data)
{
if (!data || !data->activeWorker) {
m_options.serviceWorkersMode = ServiceWorkersMode::None;
return;
}
m_options.serviceWorkerRegistrationIdentifier = data->identifier;
}
#endif
} // namespace WebCore