| /* |
| * 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 "ServiceWorkerRegistrationData.h" |
| #include <wtf/NeverDestroyed.h> |
| |
| namespace WebCore { |
| |
| CachedResourceRequest::CachedResourceRequest(ResourceRequest&& resourceRequest, const ResourceLoaderOptions& options, std::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 AtomicString& name) |
| { |
| ASSERT(!m_initiatorElement); |
| ASSERT(m_initiatorName.isEmpty()); |
| m_initiatorName = name; |
| } |
| |
| const AtomicString& CachedResourceRequest::initiatorName() const |
| { |
| if (m_initiatorElement) |
| return m_initiatorElement->localName(); |
| if (!m_initiatorName.isEmpty()) |
| return m_initiatorName; |
| |
| static NeverDestroyed<AtomicString> defaultName("other", AtomicString::ConstructFromLiteral); |
| return defaultName; |
| } |
| |
| void CachedResourceRequest::setAsPotentiallyCrossOrigin(const String& mode, Document& document) |
| { |
| ASSERT(m_options.mode == FetchOptions::Mode::NoCors); |
| |
| m_origin = &document.securityOrigin(); |
| |
| if (mode.isNull()) |
| return; |
| |
| m_options.mode = FetchOptions::Mode::Cors; |
| |
| FetchOptions::Credentials credentials = equalLettersIgnoringASCIICase(mode, "omit") |
| ? FetchOptions::Credentials::Omit : equalLettersIgnoringASCIICase(mode, "use-credentials") |
| ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin; |
| m_options.credentials = credentials; |
| m_options.storedCredentialsPolicy = credentials == FetchOptions::Credentials::Include ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse; |
| updateRequestForAccessControl(m_resourceRequest, document.securityOrigin(), m_options.storedCredentialsPolicy); |
| } |
| |
| 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 ASCIILiteral("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); |
| case CachedResource::Type::ImageResource: |
| if (ImageDecoder::supportsMediaType(ImageDecoder::MediaType::Video)) |
| return ASCIILiteral("image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5"); |
| return ASCIILiteral("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5"); |
| case CachedResource::Type::CSSStyleSheet: |
| return ASCIILiteral("text/css,*/*;q=0.1"); |
| case CachedResource::Type::SVGDocumentResource: |
| return ASCIILiteral("image/svg+xml"); |
| #if ENABLE(XSLT) |
| case CachedResource::Type::XSLStyleSheet: |
| // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example. |
| return ASCIILiteral("text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml"); |
| #endif |
| default: |
| return ASCIILiteral("*/*"); |
| } |
| } |
| |
| 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(RefreshAnyCacheData); |
| m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0()); |
| break; |
| case FetchOptions::Cache::NoStore: |
| m_options.cachingPolicy = CachingPolicy::DisallowCaching; |
| m_resourceRequest.setCachePolicy(DoNotUseAnyCache); |
| m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache()); |
| m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache()); |
| break; |
| case FetchOptions::Cache::Reload: |
| m_resourceRequest.setCachePolicy(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(ReturnCacheDataElseLoad); |
| break; |
| case FetchOptions::Cache::OnlyIfCached: |
| m_resourceRequest.setCachePolicy(ReturnCacheDataDontLoad); |
| break; |
| } |
| } |
| |
| 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::applyBlockedStatus(const ContentExtensions::BlockedStatus& blockedStatus, Page* page) |
| { |
| ContentExtensions::applyBlockedStatusToRequest(blockedStatus, page, m_resourceRequest); |
| } |
| |
| #endif |
| |
| void CachedResourceRequest::updateReferrerPolicy(ReferrerPolicy defaultPolicy) |
| { |
| if (m_options.referrerPolicy == ReferrerPolicy::EmptyString) |
| m_options.referrerPolicy = defaultPolicy; |
| } |
| |
| void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(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(); |
| String outgoingOrigin = frameLoader.outgoingOrigin(); |
| if (m_resourceRequest.hasHTTPReferrer()) { |
| outgoingReferrer = m_resourceRequest.httpReferrer(); |
| outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString(); |
| } |
| updateRequestReferrer(m_resourceRequest, m_options.referrerPolicy, outgoingReferrer); |
| |
| FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin); |
| |
| 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 std::optional<ServiceWorkerRegistrationData>& data) |
| { |
| if (!data || !data->activeWorker) { |
| m_options.serviceWorkersMode = ServiceWorkersMode::None; |
| return; |
| } |
| m_options.serviceWorkerRegistrationIdentifier = data->identifier; |
| } |
| #endif |
| |
| } // namespace WebCore |