diff options
Diffstat (limited to 'Source/WebCore/loader')
191 files changed, 15445 insertions, 11031 deletions
diff --git a/Source/WebCore/loader/ContentFilter.cpp b/Source/WebCore/loader/ContentFilter.cpp new file mode 100644 index 000000000..6f2e50376 --- /dev/null +++ b/Source/WebCore/loader/ContentFilter.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2013-2016 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "ContentFilter.h" + +#if ENABLE(CONTENT_FILTERING) + +#include "CachedRawResource.h" +#include "ContentFilterUnblockHandler.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "Logging.h" +#include "NetworkExtensionContentFilter.h" +#include "ParentalControlsContentFilter.h" +#include "ScriptController.h" +#include "SharedBuffer.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/Ref.h> +#include <wtf/SetForScope.h> +#include <wtf/Vector.h> + +#if !LOG_DISABLED +#include <wtf/text/CString.h> +#endif + +namespace WebCore { + +Vector<ContentFilter::Type>& ContentFilter::types() +{ + static NeverDestroyed<Vector<ContentFilter::Type>> types { + Vector<ContentFilter::Type> { +#if HAVE(PARENTAL_CONTROLS) + type<ParentalControlsContentFilter>(), +#endif +#if HAVE(NETWORK_EXTENSION) + type<NetworkExtensionContentFilter>() +#endif + } + }; + return types; +} + +std::unique_ptr<ContentFilter> ContentFilter::create(DocumentLoader& documentLoader) +{ + Container filters; + for (auto& type : types()) { + auto filter = type.create(); + ASSERT(filter); + filters.append(WTFMove(filter)); + } + + if (filters.isEmpty()) + return nullptr; + + return std::make_unique<ContentFilter>(WTFMove(filters), documentLoader); +} + +ContentFilter::ContentFilter(Container contentFilters, DocumentLoader& documentLoader) + : m_contentFilters { WTFMove(contentFilters) } + , m_documentLoader { documentLoader } +{ + LOG(ContentFiltering, "Creating ContentFilter with %zu platform content filter(s).\n", m_contentFilters.size()); + ASSERT(!m_contentFilters.isEmpty()); +} + +ContentFilter::~ContentFilter() +{ + LOG(ContentFiltering, "Destroying ContentFilter.\n"); +} + +bool ContentFilter::continueAfterWillSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + Ref<DocumentLoader> protectedDocumentLoader { m_documentLoader }; + + LOG(ContentFiltering, "ContentFilter received request for <%s> with redirect response from <%s>.\n", request.url().string().ascii().data(), redirectResponse.url().string().ascii().data()); +#if !LOG_DISABLED + ResourceRequest originalRequest { request }; +#endif + ASSERT(m_state == State::Stopped || m_state == State::Filtering); + forEachContentFilterUntilBlocked([&request, &redirectResponse](PlatformContentFilter& contentFilter) { + contentFilter.willSendRequest(request, redirectResponse); + }); + if (m_state == State::Blocked) + request = ResourceRequest(); +#if !LOG_DISABLED + if (request != originalRequest) + LOG(ContentFiltering, "ContentFilter changed request url to <%s>.\n", originalRequest.url().string().ascii().data()); +#endif + return !request.isNull(); +} + +void ContentFilter::startFilteringMainResource(CachedRawResource& resource) +{ + if (m_state != State::Stopped) + return; + + LOG(ContentFiltering, "ContentFilter will start filtering main resource at <%s>.\n", resource.url().string().ascii().data()); + m_state = State::Filtering; + ASSERT(!m_mainResource); + m_mainResource = &resource; +} + +void ContentFilter::stopFilteringMainResource() +{ + if (m_state != State::Blocked) + m_state = State::Stopped; + m_mainResource = nullptr; +} + +bool ContentFilter::continueAfterResponseReceived(const ResourceResponse& response) +{ + Ref<DocumentLoader> protectedDocumentLoader { m_documentLoader }; + + if (m_state == State::Filtering) { + LOG(ContentFiltering, "ContentFilter received response from <%s>.\n", response.url().string().ascii().data()); + forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) { + contentFilter.responseReceived(response); + }); + } + + return m_state != State::Blocked; +} + +bool ContentFilter::continueAfterDataReceived(const char* data, int length) +{ + Ref<DocumentLoader> protectedDocumentLoader { m_documentLoader }; + + if (m_state == State::Filtering) { + LOG(ContentFiltering, "ContentFilter received %d bytes of data from <%s>.\n", length, m_mainResource->url().string().ascii().data()); + forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) { + contentFilter.addData(data, length); + }); + + if (m_state == State::Allowed) + deliverResourceData(*m_mainResource); + return false; + } + + return m_state != State::Blocked; +} + +bool ContentFilter::continueAfterNotifyFinished(CachedResource& resource) +{ + ASSERT_UNUSED(resource, &resource == m_mainResource); + Ref<DocumentLoader> protectedDocumentLoader { m_documentLoader }; + + if (m_mainResource->errorOccurred()) + return true; + + if (m_state == State::Filtering) { + LOG(ContentFiltering, "ContentFilter will finish filtering main resource at <%s>.\n", m_mainResource->url().string().ascii().data()); + forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) { + contentFilter.finishedAddingData(); + }); + + if (m_state != State::Blocked) { + m_state = State::Allowed; + deliverResourceData(*m_mainResource); + } + + if (m_state == State::Stopped) + return false; + } + + return m_state != State::Blocked; +} + +template <typename Function> +inline void ContentFilter::forEachContentFilterUntilBlocked(Function&& function) +{ + bool allFiltersAllowedLoad { true }; + for (auto& contentFilter : m_contentFilters) { + if (!contentFilter->needsMoreData()) { + ASSERT(!contentFilter->didBlockData()); + continue; + } + + function(*contentFilter); + + if (contentFilter->didBlockData()) { + ASSERT(!m_blockingContentFilter); + m_blockingContentFilter = contentFilter.get(); + didDecide(State::Blocked); + return; + } else if (contentFilter->needsMoreData()) + allFiltersAllowedLoad = false; + } + + if (allFiltersAllowedLoad) + didDecide(State::Allowed); +} + +void ContentFilter::didDecide(State state) +{ + ASSERT(m_state != State::Allowed); + ASSERT(m_state != State::Blocked); + ASSERT(state == State::Allowed || state == State::Blocked); + LOG(ContentFiltering, "ContentFilter decided load should be %s for main resource at <%s>.\n", state == State::Allowed ? "allowed" : "blocked", m_mainResource ? m_mainResource->url().string().ascii().data() : ""); + m_state = state; + if (m_state != State::Blocked) + return; + + ContentFilterUnblockHandler unblockHandler { m_blockingContentFilter->unblockHandler() }; + unblockHandler.setUnreachableURL(m_documentLoader.documentURL()); + RefPtr<Frame> frame { m_documentLoader.frame() }; + String unblockRequestDeniedScript { m_blockingContentFilter->unblockRequestDeniedScript() }; + if (!unblockRequestDeniedScript.isEmpty() && frame) { + static_assert(std::is_base_of<ThreadSafeRefCounted<Frame>, Frame>::value, "Frame must be ThreadSafeRefCounted."); + unblockHandler.wrapWithDecisionHandler([frame = WTFMove(frame), script = unblockRequestDeniedScript.isolatedCopy()](bool unblocked) { + if (!unblocked) + frame->script().executeScript(script); + }); + } + m_documentLoader.frameLoader()->client().contentFilterDidBlockLoad(WTFMove(unblockHandler)); + + m_blockedError = m_documentLoader.frameLoader()->blockedByContentFilterError(m_documentLoader.request()); + m_documentLoader.cancelMainResourceLoad(m_blockedError); +} + +void ContentFilter::deliverResourceData(CachedResource& resource) +{ + ASSERT(m_state == State::Allowed); + ASSERT(resource.dataBufferingPolicy() == BufferData); + if (auto* resourceBuffer = resource.resourceBuffer()) + m_documentLoader.dataReceived(resource, resourceBuffer->data(), resourceBuffer->size()); +} + +static const URL& blockedPageURL() +{ + static LazyNeverDestroyed<URL> blockedPageURL; + static std::once_flag onceFlag; + std::call_once(onceFlag, [] { + auto webCoreBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebCore")); + auto blockedPageCFURL = adoptCF(CFBundleCopyResourceURL(webCoreBundle, CFSTR("ContentFilterBlockedPage"), CFSTR("html"), nullptr)); + blockedPageURL.construct(blockedPageCFURL.get()); + }); + + return blockedPageURL; +} + +bool ContentFilter::continueAfterSubstituteDataRequest(const DocumentLoader& activeLoader, const SubstituteData& substituteData) +{ + if (auto contentFilter = activeLoader.contentFilter()) { + if (contentFilter->m_state == State::Blocked && !contentFilter->m_isLoadingBlockedPage) + return contentFilter->m_blockedError.failingURL() != substituteData.failingURL(); + } + + if (activeLoader.request().url() == blockedPageURL()) { + ASSERT(activeLoader.substituteData().isValid()); + return activeLoader.substituteData().failingURL() != substituteData.failingURL(); + } + + return true; +} + +void ContentFilter::handleProvisionalLoadFailure(const ResourceError& error) +{ + if (m_state != State::Blocked) + return; + + if (m_blockedError.errorCode() != error.errorCode() || m_blockedError.domain() != error.domain()) + return; + + ASSERT(m_blockedError.failingURL() == error.failingURL()); + + RefPtr<SharedBuffer> replacementData { m_blockingContentFilter->replacementData() }; + ResourceResponse response { URL(), ASCIILiteral("text/html"), replacementData->size(), ASCIILiteral("UTF-8") }; + SubstituteData substituteData { WTFMove(replacementData), error.failingURL(), response, SubstituteData::SessionHistoryVisibility::Hidden }; + SetForScope<bool> loadingBlockedPage { m_isLoadingBlockedPage, true }; + m_documentLoader.frameLoader()->load(FrameLoadRequest(m_documentLoader.frame(), blockedPageURL(), ShouldOpenExternalURLsPolicy::ShouldNotAllow, substituteData)); +} + +} // namespace WebCore + +#endif // ENABLE(CONTENT_FILTERING) diff --git a/Source/WebCore/loader/ContentFilter.h b/Source/WebCore/loader/ContentFilter.h new file mode 100644 index 000000000..8b48166b5 --- /dev/null +++ b/Source/WebCore/loader/ContentFilter.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013-2016 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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. + */ + +#pragma once + +#if ENABLE(CONTENT_FILTERING) + +#include "CachedResourceHandle.h" +#include "PlatformContentFilter.h" +#include "ResourceError.h" +#include <functional> +#include <wtf/Vector.h> + +namespace WebCore { + +class CachedRawResource; +class DocumentLoader; +class ResourceRequest; +class ResourceResponse; +class SubstituteData; + +class ContentFilter { + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(ContentFilter); + +public: + template <typename T> static void addType() { types().append(type<T>()); } + + static std::unique_ptr<ContentFilter> create(DocumentLoader&); + ~ContentFilter(); + + static const char* urlScheme() { return "x-apple-content-filter"; } + + void startFilteringMainResource(CachedRawResource&); + void stopFilteringMainResource(); + + bool continueAfterWillSendRequest(ResourceRequest&, const ResourceResponse&); + bool continueAfterResponseReceived(const ResourceResponse&); + bool continueAfterDataReceived(const char* data, int length); + bool continueAfterNotifyFinished(CachedResource&); + + static bool continueAfterSubstituteDataRequest(const DocumentLoader& activeLoader, const SubstituteData&); + void handleProvisionalLoadFailure(const ResourceError&); + +private: + using State = PlatformContentFilter::State; + + struct Type { + const std::function<std::unique_ptr<PlatformContentFilter>()> create; + }; + template <typename T> static Type type(); + WEBCORE_EXPORT static Vector<Type>& types(); + + using Container = Vector<std::unique_ptr<PlatformContentFilter>>; + friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&, DocumentLoader&); + ContentFilter(Container, DocumentLoader&); + + template <typename Function> void forEachContentFilterUntilBlocked(Function&&); + void didDecide(State); + void deliverResourceData(CachedResource&); + + const Container m_contentFilters; + DocumentLoader& m_documentLoader; + CachedResourceHandle<CachedRawResource> m_mainResource; + PlatformContentFilter* m_blockingContentFilter { nullptr }; + State m_state { State::Stopped }; + ResourceError m_blockedError; + bool m_isLoadingBlockedPage { false }; +}; + +template <typename T> +ContentFilter::Type ContentFilter::type() +{ + static_assert(std::is_base_of<PlatformContentFilter, T>::value, "Type must be a PlatformContentFilter."); + return { T::create }; +} + +} // namespace WebCore + +#endif // ENABLE(CONTENT_FILTERING) diff --git a/Source/WebCore/loader/CookieJar.cpp b/Source/WebCore/loader/CookieJar.cpp index e93d1ca20..ef93dff5e 100644 --- a/Source/WebCore/loader/CookieJar.cpp +++ b/Source/WebCore/loader/CookieJar.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,69 +30,62 @@ #include "Document.h" #include "Frame.h" #include "FrameLoader.h" +#include "NetworkStorageSession.h" #include "NetworkingContext.h" #include "PlatformCookieJar.h" #include "PlatformStrategies.h" namespace WebCore { -static NetworkingContext* networkingContext(const Document* document) +static NetworkingContext* networkingContext(const Document& document) { // FIXME: Returning 0 means falling back to default context. That's not a choice that is appropriate to do at runtime - if (!document) - return 0; - Frame* frame = document->frame(); + Frame* frame = document.frame(); if (!frame) - return 0; + return nullptr; return frame->loader().networkingContext(); } -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(SOUP) -inline NetworkStorageSession& storageSession(const Document* document) +inline NetworkStorageSession& storageSession(const Document& document) { NetworkingContext* context = networkingContext(document); return context ? context->storageSession() : NetworkStorageSession::defaultStorageSession(); } -#define LOCAL_SESSION(document) NetworkStorageSession& session = storageSession(document); -#else -#define LOCAL_SESSION(document) NetworkStorageSession session(networkingContext(document)); -#endif -String cookies(const Document* document, const URL& url) +String cookies(const Document& document, const URL& url) { - LOCAL_SESSION(document) - return platformStrategies()->cookiesStrategy()->cookiesForDOM(session, document->firstPartyForCookies(), url); + return platformStrategies()->cookiesStrategy()->cookiesForDOM(storageSession(document), document.firstPartyForCookies(), url); } -void setCookies(Document* document, const URL& url, const String& cookieString) +void setCookies(Document& document, const URL& url, const String& cookieString) { - LOCAL_SESSION(document) - platformStrategies()->cookiesStrategy()->setCookiesFromDOM(session, document->firstPartyForCookies(), url, cookieString); + platformStrategies()->cookiesStrategy()->setCookiesFromDOM(storageSession(document), document.firstPartyForCookies(), url, cookieString); } -bool cookiesEnabled(const Document* document) +bool cookiesEnabled(const Document& document) { - LOCAL_SESSION(document) - return platformStrategies()->cookiesStrategy()->cookiesEnabled(session, document->firstPartyForCookies(), document->cookieURL()); + return platformStrategies()->cookiesStrategy()->cookiesEnabled(storageSession(document), document.firstPartyForCookies(), document.cookieURL()); } -String cookieRequestHeaderFieldValue(const Document* document, const URL& url) +String cookieRequestHeaderFieldValue(const Document& document, const URL& url) { - LOCAL_SESSION(document) - return platformStrategies()->cookiesStrategy()->cookieRequestHeaderFieldValue(session, document->firstPartyForCookies(), url); + return platformStrategies()->cookiesStrategy()->cookieRequestHeaderFieldValue(storageSession(document), document.firstPartyForCookies(), url); } -bool getRawCookies(const Document* document, const URL& url, Vector<Cookie>& cookies) +bool getRawCookies(const Document& document, const URL& url, Vector<Cookie>& cookies) { - LOCAL_SESSION(document) - return platformStrategies()->cookiesStrategy()->getRawCookies(session, document->firstPartyForCookies(), url, cookies); + return platformStrategies()->cookiesStrategy()->getRawCookies(storageSession(document), document.firstPartyForCookies(), url, cookies); } -void deleteCookie(const Document* document, const URL& url, const String& cookieName) +void deleteCookie(const Document& document, const URL& url, const String& cookieName) { - LOCAL_SESSION(document) - platformStrategies()->cookiesStrategy()->deleteCookie(session, url, cookieName); + platformStrategies()->cookiesStrategy()->deleteCookie(storageSession(document), url, cookieName); +} + +void addCookie(const Document& document, const URL& url, const Cookie& cookie) +{ + platformStrategies()->cookiesStrategy()->addCookie(storageSession(document), url, cookie); } } diff --git a/Source/WebCore/loader/CookieJar.h b/Source/WebCore/loader/CookieJar.h index a96ae45ac..0a53d9644 100644 --- a/Source/WebCore/loader/CookieJar.h +++ b/Source/WebCore/loader/CookieJar.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2012, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,11 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CookieJar_h -#define CookieJar_h +#pragma once #include <wtf/Forward.h> -#include <wtf/HashSet.h> #include <wtf/Vector.h> #include <wtf/text/WTFString.h> @@ -40,14 +38,13 @@ struct Cookie; // Functions in this file take a Document pointer to determine which cookie storage to use. We should merge that into call sites, and use PlatformCookieJar directly. // These two functions implement document.cookie API, with special rules for HttpOnly cookies. -String cookies(const Document*, const URL&); -void setCookies(Document*, const URL&, const String& cookieString); +WEBCORE_EXPORT String cookies(const Document&, const URL&); +WEBCORE_EXPORT void setCookies(Document&, const URL&, const String& cookieString); -bool cookiesEnabled(const Document*); -String cookieRequestHeaderFieldValue(const Document*, const URL&); -bool getRawCookies(const Document*, const URL&, Vector<Cookie>&); -void deleteCookie(const Document*, const URL&, const String& cookieName); +WEBCORE_EXPORT bool cookiesEnabled(const Document&); +WEBCORE_EXPORT String cookieRequestHeaderFieldValue(const Document&, const URL&); +WEBCORE_EXPORT bool getRawCookies(const Document&, const URL&, Vector<Cookie>&); +WEBCORE_EXPORT void deleteCookie(const Document&, const URL&, const String& cookieName); +WEBCORE_EXPORT void addCookie(const Document&, const URL&, const Cookie&); -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/CrossOriginAccessControl.cpp b/Source/WebCore/loader/CrossOriginAccessControl.cpp index 257b43ed8..36b55a12d 100644 --- a/Source/WebCore/loader/CrossOriginAccessControl.cpp +++ b/Source/WebCore/loader/CrossOriginAccessControl.cpp @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -27,11 +27,14 @@ #include "config.h" #include "CrossOriginAccessControl.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "SchemeRegistry.h" #include "SecurityOrigin.h" #include <mutex> +#include <wtf/NeverDestroyed.h> #include <wtf/text/AtomicString.h> #include <wtf/text/StringBuilder.h> @@ -42,34 +45,13 @@ bool isOnAccessControlSimpleRequestMethodWhitelist(const String& method) return method == "GET" || method == "HEAD" || method == "POST"; } -bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name, const String& value) -{ - if (equalIgnoringCase(name, "accept") - || equalIgnoringCase(name, "accept-language") - || equalIgnoringCase(name, "content-language") - || equalIgnoringCase(name, "origin") - || equalIgnoringCase(name, "referer")) - return true; - - // Preflight is required for MIME types that can not be sent via form submission. - if (equalIgnoringCase(name, "content-type")) { - String mimeType = extractMIMETypeFromMediaType(value); - return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") - || equalIgnoringCase(mimeType, "multipart/form-data") - || equalIgnoringCase(mimeType, "text/plain"); - } - - return false; -} - bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& headerMap) { if (!isOnAccessControlSimpleRequestMethodWhitelist(method)) return false; - HTTPHeaderMap::const_iterator end = headerMap.end(); - for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { - if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->key, it->value)) + for (const auto& header : headerMap) { + if (!header.keyAsHTTPHeaderName || !isCrossOriginSafeRequestHeader(header.keyAsHTTPHeaderName.value(), header.value)) return false; } @@ -79,82 +61,111 @@ bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& bool isOnAccessControlResponseHeaderWhitelist(const String& name) { static std::once_flag onceFlag; - static HTTPHeaderSet* allowedCrossOriginResponseHeaders; + static LazyNeverDestroyed<HTTPHeaderSet> allowedCrossOriginResponseHeaders; std::call_once(onceFlag, []{ - allowedCrossOriginResponseHeaders = std::make_unique<HTTPHeaderSet, std::initializer_list<String>>({ + allowedCrossOriginResponseHeaders.construct<std::initializer_list<String>>({ "cache-control", "content-language", "content-type", "expires", "last-modified", "pragma" - }).release(); + }); }); - return allowedCrossOriginResponseHeaders->contains(name); + return allowedCrossOriginResponseHeaders.get().contains(name); } -void updateRequestForAccessControl(ResourceRequest& request, SecurityOrigin* securityOrigin, StoredCredentials allowCredentials) +void updateRequestForAccessControl(ResourceRequest& request, SecurityOrigin& securityOrigin, StoredCredentials allowCredentials) { request.removeCredentials(); request.setAllowCookies(allowCredentials == AllowStoredCredentials); - request.setHTTPOrigin(securityOrigin->toString()); + request.setHTTPOrigin(securityOrigin.toString()); } -ResourceRequest createAccessControlPreflightRequest(const ResourceRequest& request, SecurityOrigin* securityOrigin) +ResourceRequest createAccessControlPreflightRequest(const ResourceRequest& request, SecurityOrigin& securityOrigin, const String& referrer) { ResourceRequest preflightRequest(request.url()); + static const double platformDefaultTimeout = 0; + preflightRequest.setTimeoutInterval(platformDefaultTimeout); updateRequestForAccessControl(preflightRequest, securityOrigin, DoNotAllowStoredCredentials); preflightRequest.setHTTPMethod("OPTIONS"); - preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", request.httpMethod()); + preflightRequest.setHTTPHeaderField(HTTPHeaderName::AccessControlRequestMethod, request.httpMethod()); preflightRequest.setPriority(request.priority()); + if (!referrer.isNull()) + preflightRequest.setHTTPReferrer(referrer); const HTTPHeaderMap& requestHeaderFields = request.httpHeaderFields(); - if (requestHeaderFields.size() > 0) { - StringBuilder headerBuffer; - HTTPHeaderMap::const_iterator it = requestHeaderFields.begin(); - headerBuffer.append(it->key); - ++it; - - HTTPHeaderMap::const_iterator end = requestHeaderFields.end(); - for (; it != end; ++it) { - headerBuffer.append(','); - headerBuffer.append(' '); - headerBuffer.append(it->key); + if (!requestHeaderFields.isEmpty()) { + Vector<String> unsafeHeaders; + for (const auto& headerField : requestHeaderFields.commonHeaders()) { + if (!isCrossOriginSafeRequestHeader(headerField.key, headerField.value)) + unsafeHeaders.append(httpHeaderNameString(headerField.key).toStringWithoutCopying().convertToASCIILowercase()); } + for (const auto& headerField : requestHeaderFields.uncommonHeaders()) + unsafeHeaders.append(headerField.key.convertToASCIILowercase()); + + std::sort(unsafeHeaders.begin(), unsafeHeaders.end(), WTF::codePointCompareLessThan); + + StringBuilder headerBuffer; + + bool appendComma = false; + for (const auto& headerField : unsafeHeaders) { + if (appendComma) + headerBuffer.append(','); + else + appendComma = true; - preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", headerBuffer.toString().lower()); + headerBuffer.append(headerField); + } + if (!headerBuffer.isEmpty()) + preflightRequest.setHTTPHeaderField(HTTPHeaderName::AccessControlRequestHeaders, headerBuffer.toString()); } return preflightRequest; } -bool passesAccessControlCheck(const ResourceResponse& response, StoredCredentials includeCredentials, SecurityOrigin* securityOrigin, String& errorDescription) +bool isValidCrossOriginRedirectionURL(const URL& redirectURL) +{ + return SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(redirectURL.protocol().toStringWithoutCopying()) + && redirectURL.user().isEmpty() + && redirectURL.pass().isEmpty(); +} + +void cleanRedirectedRequestForAccessControl(ResourceRequest& request) +{ + // Remove headers that may have been added by the network layer that cause access control to fail. + request.clearHTTPContentType(); + request.clearHTTPReferrer(); + request.clearHTTPOrigin(); + request.clearHTTPUserAgent(); + request.clearHTTPAccept(); + request.clearHTTPAcceptEncoding(); +} + +bool passesAccessControlCheck(const ResourceResponse& response, StoredCredentials includeCredentials, SecurityOrigin& securityOrigin, String& errorDescription) { // A wildcard Access-Control-Allow-Origin can not be used if credentials are to be sent, // even with Access-Control-Allow-Credentials set to true. - const String& accessControlOriginString = response.httpHeaderField("access-control-allow-origin"); + const String& accessControlOriginString = response.httpHeaderField(HTTPHeaderName::AccessControlAllowOrigin); if (accessControlOriginString == "*" && includeCredentials == DoNotAllowStoredCredentials) return true; - if (securityOrigin->isUnique()) { - errorDescription = "Cannot make any requests from " + securityOrigin->toString() + "."; - return false; - } - - // FIXME: Access-Control-Allow-Origin can contain a list of origins. - if (accessControlOriginString != securityOrigin->toString()) { + String securityOriginString = securityOrigin.toString(); + if (accessControlOriginString != securityOriginString) { if (accessControlOriginString == "*") - errorDescription = "Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true."; + errorDescription = ASCIILiteral("Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true."); + else if (accessControlOriginString.find(',') != notFound) + errorDescription = ASCIILiteral("Access-Control-Allow-Origin cannot contain more than one origin."); else - errorDescription = "Origin " + securityOrigin->toString() + " is not allowed by Access-Control-Allow-Origin."; + errorDescription = makeString("Origin ", securityOriginString, " is not allowed by Access-Control-Allow-Origin."); return false; } if (includeCredentials == AllowStoredCredentials) { - const String& accessControlCredentialsString = response.httpHeaderField("access-control-allow-credentials"); + const String& accessControlCredentialsString = response.httpHeaderField(HTTPHeaderName::AccessControlAllowCredentials); if (accessControlCredentialsString != "true") { errorDescription = "Credentials flag is true, but Access-Control-Allow-Credentials is not \"true\"."; return false; @@ -164,15 +175,4 @@ bool passesAccessControlCheck(const ResourceResponse& response, StoredCredential return true; } -void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet& headerSet) -{ - Vector<String> headers; - headerValue.split(',', false, headers); - for (unsigned headerCount = 0; headerCount < headers.size(); headerCount++) { - String strippedHeader = headers[headerCount].stripWhiteSpace(); - if (!strippedHeader.isEmpty()) - headerSet.add(strippedHeader); - } -} - } // namespace WebCore diff --git a/Source/WebCore/loader/CrossOriginAccessControl.h b/Source/WebCore/loader/CrossOriginAccessControl.h index 0b2d272bd..8a710437d 100644 --- a/Source/WebCore/loader/CrossOriginAccessControl.h +++ b/Source/WebCore/loader/CrossOriginAccessControl.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -24,34 +24,30 @@ * */ -#ifndef CrossOriginAccessControl_h -#define CrossOriginAccessControl_h +#pragma once #include "ResourceHandleTypes.h" #include <wtf/Forward.h> -#include <wtf/HashSet.h> -#include <wtf/text/StringHash.h> namespace WebCore { -typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet; - class HTTPHeaderMap; +enum class HTTPHeaderName; class ResourceRequest; class ResourceResponse; class SecurityOrigin; +class URL; bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap&); bool isOnAccessControlSimpleRequestMethodWhitelist(const String&); -bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name, const String& value); bool isOnAccessControlResponseHeaderWhitelist(const String&); -void updateRequestForAccessControl(ResourceRequest&, SecurityOrigin*, StoredCredentials); -ResourceRequest createAccessControlPreflightRequest(const ResourceRequest&, SecurityOrigin*); +void updateRequestForAccessControl(ResourceRequest&, SecurityOrigin&, StoredCredentials); +ResourceRequest createAccessControlPreflightRequest(const ResourceRequest&, SecurityOrigin&, const String&); -bool passesAccessControlCheck(const ResourceResponse&, StoredCredentials, SecurityOrigin*, String& errorDescription); -void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet&); +bool isValidCrossOriginRedirectionURL(const URL&); +void cleanRedirectedRequestForAccessControl(ResourceRequest&); -} // namespace WebCore +bool passesAccessControlCheck(const ResourceResponse&, StoredCredentials, SecurityOrigin&, String& errorDescription); -#endif // CrossOriginAccessControl_h +} // namespace WebCore diff --git a/Source/WebCore/loader/CrossOriginPreflightChecker.cpp b/Source/WebCore/loader/CrossOriginPreflightChecker.cpp new file mode 100644 index 000000000..9886c5180 --- /dev/null +++ b/Source/WebCore/loader/CrossOriginPreflightChecker.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 Canon 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Canon Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "CrossOriginPreflightChecker.h" + +#include "CachedRawResource.h" +#include "CachedResourceLoader.h" +#include "CachedResourceRequest.h" +#include "ContentSecurityPolicy.h" +#include "CrossOriginAccessControl.h" +#include "CrossOriginPreflightResultCache.h" +#include "DocumentThreadableLoader.h" +#include "InspectorInstrumentation.h" +#include "RuntimeEnabledFeatures.h" + +namespace WebCore { + +CrossOriginPreflightChecker::CrossOriginPreflightChecker(DocumentThreadableLoader& loader, ResourceRequest&& request) + : m_loader(loader) + , m_request(WTFMove(request)) +{ +} + +CrossOriginPreflightChecker::~CrossOriginPreflightChecker() +{ + if (m_resource) + m_resource->removeClient(*this); +} + +void CrossOriginPreflightChecker::validatePreflightResponse(DocumentThreadableLoader& loader, ResourceRequest&& request, unsigned long identifier, const ResourceResponse& response) +{ + Frame* frame = loader.document().frame(); + ASSERT(frame); + + if (!response.isSuccessful()) { + loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), ASCIILiteral("Preflight response is not successful"), ResourceError::Type::AccessControl)); + return; + } + + String description; + if (!passesAccessControlCheck(response, loader.options().allowCredentials, loader.securityOrigin(), description)) { + loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), description, ResourceError::Type::AccessControl)); + return; + } + + auto result = std::make_unique<CrossOriginPreflightResultCacheItem>(loader.options().allowCredentials); + if (!result->parse(response, description) + || !result->allowsCrossOriginMethod(request.httpMethod(), description) + || !result->allowsCrossOriginHeaders(request.httpHeaderFields(), description)) { + loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), description, ResourceError::Type::AccessControl)); + return; + } + + // FIXME: <https://webkit.org/b/164889> Web Inspector: Show Preflight Request information in inspector + // This is only showing success preflight requests and responses but we should show network events + // for preflight failures and distinguish them better from non-preflight requests. + InspectorInstrumentation::didReceiveResourceResponse(*frame, identifier, frame->loader().documentLoader(), response, nullptr); + InspectorInstrumentation::didFinishLoading(frame, frame->loader().documentLoader(), identifier, 0); + + CrossOriginPreflightResultCache::singleton().appendEntry(loader.securityOrigin().toString(), request.url(), WTFMove(result)); + loader.preflightSuccess(WTFMove(request)); +} + +void CrossOriginPreflightChecker::notifyFinished(CachedResource& resource) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + if (m_resource->loadFailedOrCanceled()) { + ResourceError preflightError = m_resource->resourceError(); + // If the preflight was cancelled by underlying code, it probably means the request was blocked due to some access control policy. + // FIXME:: According fetch, we should just pass the error to the layer above. But this may impact some clients like XHR or EventSource. + if (preflightError.isNull() || preflightError.isCancellation() || preflightError.isGeneral()) + preflightError.setType(ResourceError::Type::AccessControl); + + m_loader.preflightFailure(m_resource->identifier(), preflightError); + return; + } + validatePreflightResponse(m_loader, WTFMove(m_request), m_resource->identifier(), m_resource->response()); +} + +void CrossOriginPreflightChecker::startPreflight() +{ + ResourceLoaderOptions options; + options.referrerPolicy = m_loader.options().referrerPolicy; + options.redirect = FetchOptions::Redirect::Manual; + options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck; + + CachedResourceRequest preflightRequest(createAccessControlPreflightRequest(m_request, m_loader.securityOrigin(), m_loader.referrer()), options); + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) + preflightRequest.setInitiator(m_loader.options().initiator); + + ASSERT(!m_resource); + m_resource = m_loader.document().cachedResourceLoader().requestRawResource(WTFMove(preflightRequest)); + if (m_resource) + m_resource->addClient(*this); +} + +void CrossOriginPreflightChecker::doPreflight(DocumentThreadableLoader& loader, ResourceRequest&& request) +{ + if (!loader.document().frame()) + return; + + ResourceRequest preflightRequest = createAccessControlPreflightRequest(request, loader.securityOrigin(), loader.referrer()); + ResourceError error; + ResourceResponse response; + RefPtr<SharedBuffer> data; + + unsigned identifier = loader.document().frame()->loader().loadResourceSynchronously(preflightRequest, DoNotAllowStoredCredentials, ClientCredentialPolicy::CannotAskClientForCredentials, error, response, data); + + if (!error.isNull()) { + // If the preflight was cancelled by underlying code, it probably means the request was blocked due to some access control policy. + // FIXME:: According fetch, we should just pass the error to the layer above. But this may impact some clients like XHR or EventSource. + if (error.isCancellation() || error.isGeneral()) + error.setType(ResourceError::Type::AccessControl); + loader.preflightFailure(identifier, error); + return; + } + + // FIXME: Ideally, we should ask platformLoadResourceSynchronously to set ResourceResponse isRedirected and use it here. + bool isRedirect = preflightRequest.url().strippedForUseAsReferrer() != response.url().strippedForUseAsReferrer(); + if (isRedirect || !response.isSuccessful()) { + loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), ASCIILiteral("Preflight response is not successful"), ResourceError::Type::AccessControl)); + return; + } + + validatePreflightResponse(loader, WTFMove(request), identifier, response); +} + +void CrossOriginPreflightChecker::setDefersLoading(bool value) +{ + if (m_resource) + m_resource->setDefersLoading(value); +} + +bool CrossOriginPreflightChecker::isXMLHttpRequest() const +{ + return m_loader.isXMLHttpRequest(); +} + +} // namespace WebCore diff --git a/Source/WebCore/loader/DocumentThreadableLoaderClient.h b/Source/WebCore/loader/CrossOriginPreflightChecker.h index 146a166ed..6971c31f9 100644 --- a/Source/WebCore/loader/DocumentThreadableLoaderClient.h +++ b/Source/WebCore/loader/CrossOriginPreflightChecker.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016, Canon Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -11,7 +11,7 @@ * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. - * * Neither the name of Google Inc. nor the names of its + * * Neither the name of Canon Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * @@ -28,28 +28,41 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentThreadableLoaderClient_h -#define DocumentThreadableLoaderClient_h +#pragma once -#include "ThreadableLoaderClient.h" +#include "CachedRawResourceClient.h" +#include "CachedResourceHandle.h" +#include "ResourceRequest.h" namespace WebCore { -class ResourceRequest; -class ResourceResponse; +class CachedRawResource; +class Document; +class DocumentThreadableLoader; +class ResourceError; -class DocumentThreadableLoaderClient : public ThreadableLoaderClient { - WTF_MAKE_NONCOPYABLE(DocumentThreadableLoaderClient); - WTF_MAKE_FAST_ALLOCATED; +class CrossOriginPreflightChecker final : private CachedRawResourceClient { public: - virtual bool isDocumentThreadableLoaderClient() { return true; } + static void doPreflight(DocumentThreadableLoader&, ResourceRequest&&); - virtual void willSendRequest(ResourceRequest& /*newRequest*/, const ResourceResponse& /*redirectResponse*/) { } + CrossOriginPreflightChecker(DocumentThreadableLoader&, ResourceRequest&&); + ~CrossOriginPreflightChecker(); -protected: - DocumentThreadableLoaderClient() { } + void startPreflight(); + + void setDefersLoading(bool); + +private: + void notifyFinished(CachedResource&) final; + + static void handleLoadingFailure(DocumentThreadableLoader&, unsigned long, const ResourceError&); + static void validatePreflightResponse(DocumentThreadableLoader&, ResourceRequest&&, unsigned long, const ResourceResponse&); + + bool isXMLHttpRequest() const final; + + DocumentThreadableLoader& m_loader; + CachedResourceHandle<CachedRawResource> m_resource; + ResourceRequest m_request; }; } // namespace WebCore - -#endif // DocumentThreadableLoaderClient_h diff --git a/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp b/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp index 7398d0f95..47cf73650 100644 --- a/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp +++ b/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -28,22 +28,28 @@ #include "CrossOriginPreflightResultCache.h" #include "CrossOriginAccessControl.h" +#include "HTTPHeaderNames.h" +#include "HTTPParsers.h" #include "ResourceResponse.h" -#include <wtf/CurrentTime.h> #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> namespace WebCore { // These values are at the discretion of the user agent. -static const unsigned defaultPreflightCacheTimeoutSeconds = 5; -static const unsigned maxPreflightCacheTimeoutSeconds = 600; // Should be short enough to minimize the risk of using a poisoned cache after switching to a secure network. +static const auto defaultPreflightCacheTimeout = 5s; +static const auto maxPreflightCacheTimeout = 600s; // Should be short enough to minimize the risk of using a poisoned cache after switching to a secure network. -static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta) +CrossOriginPreflightResultCache::CrossOriginPreflightResultCache() +{ +} + +static bool parseAccessControlMaxAge(const String& string, std::chrono::seconds& expiryDelta) { // FIXME: this will not do the correct thing for a number starting with a '+' bool ok = false; - expiryDelta = string.toUIntStrict(&ok); + expiryDelta = std::chrono::seconds(string.toUIntStrict(&ok)); return ok; } @@ -88,25 +94,25 @@ static bool parseAccessControlAllowList(const String& string, HashSet<String, Ha bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response, String& errorDescription) { m_methods.clear(); - if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods)) { + if (!parseAccessControlAllowList(response.httpHeaderField(HTTPHeaderName::AccessControlAllowMethods), m_methods)) { errorDescription = "Cannot parse Access-Control-Allow-Methods response header field."; return false; } m_headers.clear(); - if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers)) { + if (!parseAccessControlAllowList(response.httpHeaderField(HTTPHeaderName::AccessControlAllowHeaders), m_headers)) { errorDescription = "Cannot parse Access-Control-Allow-Headers response header field."; return false; } - unsigned expiryDelta; - if (parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) { - if (expiryDelta > maxPreflightCacheTimeoutSeconds) - expiryDelta = maxPreflightCacheTimeoutSeconds; + std::chrono::seconds expiryDelta; + if (parseAccessControlMaxAge(response.httpHeaderField(HTTPHeaderName::AccessControlMaxAge), expiryDelta)) { + if (expiryDelta > maxPreflightCacheTimeout) + expiryDelta = maxPreflightCacheTimeout; } else - expiryDelta = defaultPreflightCacheTimeoutSeconds; + expiryDelta = defaultPreflightCacheTimeout; - m_absoluteExpiryTime = monotonicallyIncreasingTime() + expiryDelta; + m_absoluteExpiryTime = std::chrono::steady_clock::now() + expiryDelta; return true; } @@ -121,10 +127,11 @@ bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHeaderMap& requestHeaders, String& errorDescription) const { - HTTPHeaderMap::const_iterator end = requestHeaders.end(); - for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { - if (!m_headers.contains(it->key) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->key, it->value)) { - errorDescription = "Request header field " + it->key.string() + " is not allowed by Access-Control-Allow-Headers."; + for (const auto& header : requestHeaders) { + if (header.keyAsHTTPHeaderName && isCrossOriginSafeRequestHeader(header.keyAsHTTPHeaderName.value(), header.value)) + continue; + if (!m_headers.contains(header.key)) { + errorDescription = "Request header field " + header.key + " is not allowed by Access-Control-Allow-Headers."; return false; } } @@ -134,7 +141,7 @@ bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHea bool CrossOriginPreflightResultCacheItem::allowsRequest(StoredCredentials includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const { String ignoredExplanation; - if (m_absoluteExpiryTime < monotonicallyIncreasingTime()) + if (m_absoluteExpiryTime < std::chrono::steady_clock::now()) return false; if (includeCredentials == AllowStoredCredentials && m_credentials == DoNotAllowStoredCredentials) return false; @@ -145,30 +152,31 @@ bool CrossOriginPreflightResultCacheItem::allowsRequest(StoredCredentials includ return true; } -CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::shared() +CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::singleton() { - DEFINE_STATIC_LOCAL(CrossOriginPreflightResultCache, cache, ()); ASSERT(isMainThread()); + + static NeverDestroyed<CrossOriginPreflightResultCache> cache; return cache; } -void CrossOriginPreflightResultCache::appendEntry(const String& origin, const URL& url, PassOwnPtr<CrossOriginPreflightResultCacheItem> preflightResult) +void CrossOriginPreflightResultCache::appendEntry(const String& origin, const URL& url, std::unique_ptr<CrossOriginPreflightResultCacheItem> preflightResult) { ASSERT(isMainThread()); - m_preflightHashMap.set(std::make_pair(origin, url), preflightResult); + m_preflightHashMap.set(std::make_pair(origin, url), WTFMove(preflightResult)); } bool CrossOriginPreflightResultCache::canSkipPreflight(const String& origin, const URL& url, StoredCredentials includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) { ASSERT(isMainThread()); - CrossOriginPreflightResultHashMap::iterator cacheIt = m_preflightHashMap.find(std::make_pair(origin, url)); - if (cacheIt == m_preflightHashMap.end()) + auto it = m_preflightHashMap.find(std::make_pair(origin, url)); + if (it == m_preflightHashMap.end()) return false; - if (cacheIt->value->allowsRequest(includeCredentials, method, requestHeaders)) + if (it->value->allowsRequest(includeCredentials, method, requestHeaders)) return true; - m_preflightHashMap.remove(cacheIt); + m_preflightHashMap.remove(it); return false; } diff --git a/Source/WebCore/loader/CrossOriginPreflightResultCache.h b/Source/WebCore/loader/CrossOriginPreflightResultCache.h index 1c95a439f..094496e58 100644 --- a/Source/WebCore/loader/CrossOriginPreflightResultCache.h +++ b/Source/WebCore/loader/CrossOriginPreflightResultCache.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -24,65 +24,59 @@ * */ -#ifndef CrossOriginPreflightResultCache_h -#define CrossOriginPreflightResultCache_h +#pragma once #include "URLHash.h" #include "ResourceHandleTypes.h" +#include <chrono> #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/PassOwnPtr.h> #include <wtf/text/StringHash.h> namespace WebCore { - class HTTPHeaderMap; - class ResourceResponse; +class HTTPHeaderMap; +class ResourceResponse; - class CrossOriginPreflightResultCacheItem { - WTF_MAKE_NONCOPYABLE(CrossOriginPreflightResultCacheItem); WTF_MAKE_FAST_ALLOCATED; - public: - CrossOriginPreflightResultCacheItem(StoredCredentials credentials) - : m_absoluteExpiryTime(0) - , m_credentials(credentials) - { - } +class CrossOriginPreflightResultCacheItem { + WTF_MAKE_NONCOPYABLE(CrossOriginPreflightResultCacheItem); WTF_MAKE_FAST_ALLOCATED; +public: + explicit CrossOriginPreflightResultCacheItem(StoredCredentials credentials) + : m_credentials(credentials) + { + } - bool parse(const ResourceResponse&, String& errorDescription); - bool allowsCrossOriginMethod(const String&, String& errorDescription) const; - bool allowsCrossOriginHeaders(const HTTPHeaderMap&, String& errorDescription) const; - bool allowsRequest(StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const; + bool parse(const ResourceResponse&, String& errorDescription); + bool allowsCrossOriginMethod(const String&, String& errorDescription) const; + bool allowsCrossOriginHeaders(const HTTPHeaderMap&, String& errorDescription) const; + bool allowsRequest(StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const; - private: - typedef HashSet<String, CaseFoldingHash> HeadersSet; +private: + // FIXME: A better solution to holding onto the absolute expiration time might be + // to start a timer for the expiration delta that removes this from the cache when + // it fires. + std::chrono::steady_clock::time_point m_absoluteExpiryTime; + StoredCredentials m_credentials; + HashSet<String> m_methods; + HashSet<String, ASCIICaseInsensitiveHash> m_headers; +}; - // FIXME: A better solution to holding onto the absolute expiration time might be - // to start a timer for the expiration delta that removes this from the cache when - // it fires. - double m_absoluteExpiryTime; - StoredCredentials m_credentials; - HashSet<String> m_methods; - HeadersSet m_headers; - }; +class CrossOriginPreflightResultCache { + WTF_MAKE_NONCOPYABLE(CrossOriginPreflightResultCache); WTF_MAKE_FAST_ALLOCATED; - class CrossOriginPreflightResultCache { - WTF_MAKE_NONCOPYABLE(CrossOriginPreflightResultCache); WTF_MAKE_FAST_ALLOCATED; - public: - static CrossOriginPreflightResultCache& shared(); +public: + WEBCORE_EXPORT static CrossOriginPreflightResultCache& singleton(); - void appendEntry(const String& origin, const URL&, PassOwnPtr<CrossOriginPreflightResultCacheItem>); - bool canSkipPreflight(const String& origin, const URL&, StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders); + void appendEntry(const String& origin, const URL&, std::unique_ptr<CrossOriginPreflightResultCacheItem>); + bool canSkipPreflight(const String& origin, const URL&, StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders); - void empty(); + WEBCORE_EXPORT void empty(); - private: - CrossOriginPreflightResultCache() { } +private: + friend NeverDestroyed<CrossOriginPreflightResultCache>; + CrossOriginPreflightResultCache(); - typedef HashMap<std::pair<String, URL>, OwnPtr<CrossOriginPreflightResultCacheItem>> CrossOriginPreflightResultHashMap; - - CrossOriginPreflightResultHashMap m_preflightHashMap; - }; + HashMap<std::pair<String, URL>, std::unique_ptr<CrossOriginPreflightResultCacheItem>> m_preflightHashMap; +}; } // namespace WebCore - -#endif diff --git a/Source/WebCore/loader/DocumentLoadTiming.h b/Source/WebCore/loader/DocumentLoadTiming.h deleted file mode 100644 index 8540901eb..000000000 --- a/Source/WebCore/loader/DocumentLoadTiming.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2010 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 GOOGLE 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. - */ - -#ifndef DocumentLoadTiming_h -#define DocumentLoadTiming_h - -#include <wtf/CurrentTime.h> - -namespace WebCore { - -class Frame; -class URL; - -class DocumentLoadTiming { -public: - DocumentLoadTiming(); - - double monotonicTimeToZeroBasedDocumentTime(double) const; - double monotonicTimeToPseudoWallTime(double) const; - - void markNavigationStart(); - void setNavigationStart(double); - void addRedirect(const URL& redirectingUrl, const URL& redirectedUrl); - - void markUnloadEventStart() { m_unloadEventStart = monotonicallyIncreasingTime(); } - void markUnloadEventEnd() { m_unloadEventEnd = monotonicallyIncreasingTime(); } - void markRedirectStart() { m_redirectStart = monotonicallyIncreasingTime(); } - void markRedirectEnd() { m_redirectEnd = monotonicallyIncreasingTime(); } - void markFetchStart() { m_fetchStart = monotonicallyIncreasingTime(); } - void setResponseEnd(double monotonicTime) { m_responseEnd = monotonicTime; } - void markLoadEventStart() { m_loadEventStart = monotonicallyIncreasingTime(); } - void markLoadEventEnd() { m_loadEventEnd = monotonicallyIncreasingTime(); } - - void setHasSameOriginAsPreviousDocument(bool value) { m_hasSameOriginAsPreviousDocument = value; } - - double navigationStart() const { return m_navigationStart; } - double unloadEventStart() const { return m_unloadEventStart; } - double unloadEventEnd() const { return m_unloadEventEnd; } - double redirectStart() const { return m_redirectStart; } - double redirectEnd() const { return m_redirectEnd; } - short redirectCount() const { return m_redirectCount; } - double fetchStart() const { return m_fetchStart; } - double responseEnd() const { return m_responseEnd; } - double loadEventStart() const { return m_loadEventStart; } - double loadEventEnd() const { return m_loadEventEnd; } - bool hasCrossOriginRedirect() const { return m_hasCrossOriginRedirect; } - bool hasSameOriginAsPreviousDocument() const { return m_hasSameOriginAsPreviousDocument; } - -private: - double m_referenceMonotonicTime; - double m_referenceWallTime; - double m_navigationStart; - double m_unloadEventStart; - double m_unloadEventEnd; - double m_redirectStart; - double m_redirectEnd; - short m_redirectCount; - double m_fetchStart; - double m_responseEnd; - double m_loadEventStart; - double m_loadEventEnd; - bool m_hasCrossOriginRedirect; - bool m_hasSameOriginAsPreviousDocument; -}; - -} // namespace WebCore - -#endif 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 diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h index 866fd7caa..138883f82 100644 --- a/Source/WebCore/loader/DocumentLoader.h +++ b/Source/WebCore/loader/DocumentLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 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. * @@ -27,20 +27,21 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentLoader_h -#define DocumentLoader_h +#pragma once #include "CachedRawResourceClient.h" #include "CachedResourceHandle.h" -#include "DocumentLoadTiming.h" #include "DocumentWriter.h" #include "IconDatabaseBase.h" +#include "LinkIcon.h" +#include "LoadTiming.h" #include "NavigationAction.h" #include "ResourceError.h" #include "ResourceLoaderOptions.h" #include "ResourceRequest.h" #include "ResourceResponse.h" #include "StringWithDirection.h" +#include "StyleSheetContents.h" #include "SubstituteData.h" #include "Timer.h" #include <wtf/HashSet.h> @@ -51,392 +52,524 @@ #include <wtf/RunLoopTimer.h> #endif -namespace WTF { -class SchedulePair; -} +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) +#include <wtf/SchedulePair.h> +#endif namespace WebCore { - class ApplicationCacheHost; -#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - class Archive; -#endif - class ArchiveResource; - class ArchiveResourceCollection; - class CachedRawResource; - class CachedResourceLoader; - class ContentFilter; - class FormState; - class Frame; - class FrameLoader; - class Page; - class ResourceBuffer; - class ResourceLoader; - class SharedBuffer; - class SubstituteResource; - - typedef HashSet<RefPtr<ResourceLoader>> ResourceLoaderSet; - typedef Vector<ResourceResponse> ResponseVector; - - class DocumentLoader : public RefCounted<DocumentLoader>, private CachedRawResourceClient { - WTF_MAKE_FAST_ALLOCATED; - public: - static PassRefPtr<DocumentLoader> create(const ResourceRequest& request, const SubstituteData& data) - { - return adoptRef(new DocumentLoader(request, data)); - } - virtual ~DocumentLoader(); - - void setFrame(Frame*); - Frame* frame() const { return m_frame; } - - virtual void attachToFrame(); - virtual void detachFromFrame(); - - FrameLoader* frameLoader() const; - ResourceLoader* mainResourceLoader() const; - PassRefPtr<ResourceBuffer> mainResourceData() const; - - DocumentWriter& writer() const { return m_writer; } - - const ResourceRequest& originalRequest() const; - const ResourceRequest& originalRequestCopy() const; - - const ResourceRequest& request() const; - ResourceRequest& request(); - - CachedResourceLoader& cachedResourceLoader() { return m_cachedResourceLoader.get(); } - - const SubstituteData& substituteData() const { return m_substituteData; } - - // FIXME: This is the same as requestURL(). We should remove one of them. - const URL& url() const; - const URL& unreachableURL() const; - - const URL& originalURL() const; - const URL& requestURL() const; - const URL& responseURL() const; - const String& responseMIMEType() const; + +class ApplicationCacheHost; +class Archive; +class ArchiveResource; +class ArchiveResourceCollection; +class CachedRawResource; +class CachedResourceLoader; +class ContentFilter; +class FormState; +class Frame; +class FrameLoader; +class IconLoader; +class Page; +class PreviewConverter; +class ResourceLoader; +class SharedBuffer; +class SubresourceLoader; +class SubstituteResource; + +using ResourceLoaderMap = HashMap<unsigned long, RefPtr<ResourceLoader>>; + +enum class AutoplayPolicy { + Default, // Uses policies specified in document settings. + Allow, + AllowWithoutSound, + Deny, +}; + +class DocumentLoader : public RefCounted<DocumentLoader>, private CachedRawResourceClient { + WTF_MAKE_FAST_ALLOCATED; + friend class ContentFilter; +public: + static Ref<DocumentLoader> create(const ResourceRequest& request, const SubstituteData& data) + { + return adoptRef(*new DocumentLoader(request, data)); + } + WEBCORE_EXPORT virtual ~DocumentLoader(); + + void attachToFrame(Frame&); + Frame* frame() const { return m_frame; } + + WEBCORE_EXPORT virtual void detachFromFrame(); + + WEBCORE_EXPORT FrameLoader* frameLoader() const; + WEBCORE_EXPORT SubresourceLoader* mainResourceLoader() const; + WEBCORE_EXPORT RefPtr<SharedBuffer> mainResourceData() const; + + DocumentWriter& writer() const { return m_writer; } + + const ResourceRequest& originalRequest() const; + const ResourceRequest& originalRequestCopy() const; + + const ResourceRequest& request() const; + ResourceRequest& request(); + + CachedResourceLoader& cachedResourceLoader() { return m_cachedResourceLoader; } + + const SubstituteData& substituteData() const { return m_substituteData; } + + const URL& url() const; + const URL& unreachableURL() const; + + const URL& originalURL() const; + const URL& responseURL() const; + const String& responseMIMEType() const; #if PLATFORM(IOS) - // FIXME: This method seems to violate the encapsulation of this class. - void setResponseMIMEType(const String&); + // FIXME: This method seems to violate the encapsulation of this class. + WEBCORE_EXPORT void setResponseMIMEType(const String&); #endif + const String& currentContentType() const; + void replaceRequestURLForSameDocumentNavigation(const URL&); + bool isStopping() const { return m_isStopping; } + void stopLoading(); + void setCommitted(bool committed) { m_committed = committed; } + bool isCommitted() const { return m_committed; } + WEBCORE_EXPORT bool isLoading() const; - void replaceRequestURLForSameDocumentNavigation(const URL&); - bool isStopping() const { return m_isStopping; } - void stopLoading(); - void setCommitted(bool committed) { m_committed = committed; } - bool isCommitted() const { return m_committed; } - bool isLoading() const; + const ResourceError& mainDocumentError() const { return m_mainDocumentError; } - const ResourceError& mainDocumentError() const { return m_mainDocumentError; } - - const ResourceResponse& response() const { return m_response; } + const ResourceResponse& response() const { return m_response; } #if PLATFORM(IOS) - // FIXME: This method seems to violate the encapsulation of this class. - void setResponse(const ResourceResponse& response) { m_response = response; } + // FIXME: This method seems to violate the encapsulation of this class. + void setResponse(const ResourceResponse& response) { m_response = response; } #endif - bool isClientRedirect() const { return m_isClientRedirect; } - void setIsClientRedirect(bool isClientRedirect) { m_isClientRedirect = isClientRedirect; } - void handledOnloadEvents(); - bool wasOnloadHandled() { return m_wasOnloadHandled; } - bool isLoadingInAPISense() const; - void setTitle(const StringWithDirection&); - const String& overrideEncoding() const { return m_overrideEncoding; } - -#if PLATFORM(MAC) - void schedule(WTF::SchedulePair*); - void unschedule(WTF::SchedulePair*); + bool isClientRedirect() const { return m_isClientRedirect; } + void setIsClientRedirect(bool isClientRedirect) { m_isClientRedirect = isClientRedirect; } + void dispatchOnloadEvents(); + bool wasOnloadDispatched() { return m_wasOnloadDispatched; } + WEBCORE_EXPORT bool isLoadingInAPISense() const; + WEBCORE_EXPORT void setTitle(const StringWithDirection&); + const String& overrideEncoding() const { return m_overrideEncoding; } + +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + void schedule(SchedulePair&); + void unschedule(SchedulePair&); #endif #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - void setArchive(PassRefPtr<Archive>); - void addAllArchiveResources(Archive*); - void addArchiveResource(PassRefPtr<ArchiveResource>); - PassRefPtr<Archive> popArchiveForSubframe(const String& frameName, const URL&); - SharedBuffer* parsedArchiveData() const; + void setArchive(Ref<Archive>&&); + WEBCORE_EXPORT void addAllArchiveResources(Archive&); + WEBCORE_EXPORT void addArchiveResource(Ref<ArchiveResource>&&); + RefPtr<Archive> popArchiveForSubframe(const String& frameName, const URL&); + WEBCORE_EXPORT SharedBuffer* parsedArchiveData() const; - bool scheduleArchiveLoad(ResourceLoader*, const ResourceRequest&); -#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) + WEBCORE_EXPORT bool scheduleArchiveLoad(ResourceLoader&, const ResourceRequest&); +#endif - // Return the ArchiveResource for the URL only when loading an Archive - ArchiveResource* archiveResourceForURL(const URL&) const; + void scheduleSubstituteResourceLoad(ResourceLoader&, SubstituteResource&); - PassRefPtr<ArchiveResource> mainResource() const; + // Return the ArchiveResource for the URL only when loading an Archive + WEBCORE_EXPORT ArchiveResource* archiveResourceForURL(const URL&) const; - // Return an ArchiveResource for the URL, either creating from live data or - // pulling from the ArchiveResourceCollection - PassRefPtr<ArchiveResource> subresource(const URL&) const; - void getSubresources(Vector<PassRefPtr<ArchiveResource>>&) const; + WEBCORE_EXPORT RefPtr<ArchiveResource> mainResource() const; + // Return an ArchiveResource for the URL, either creating from live data or + // pulling from the ArchiveResourceCollection. + WEBCORE_EXPORT RefPtr<ArchiveResource> subresource(const URL&) const; + + WEBCORE_EXPORT Vector<Ref<ArchiveResource>> subresources() const; #ifndef NDEBUG - bool isSubstituteLoadPending(ResourceLoader*) const; -#endif - void cancelPendingSubstituteLoad(ResourceLoader*); - - void addResponse(const ResourceResponse&); - const ResponseVector& responses() const { return m_responses; } - - const NavigationAction& triggeringAction() const { return m_triggeringAction; } - void setTriggeringAction(const NavigationAction& action) { m_triggeringAction = action; } - void setOverrideEncoding(const String& encoding) { m_overrideEncoding = encoding; } - void setLastCheckedRequest(const ResourceRequest& request) { m_lastCheckedRequest = request; } - const ResourceRequest& lastCheckedRequest() { return m_lastCheckedRequest; } - - void stopRecordingResponses(); - const StringWithDirection& title() const { return m_pageTitle; } - - URL urlForHistory() const; - bool urlForHistoryReflectsFailure() const; - - // These accessors accommodate WebCore's somewhat fickle custom of creating history - // items for redirects, but only sometimes. For "source" and "destination", - // these accessors return the URL that would have been used if a history - // item were created. This allows WebKit to link history items reflecting - // redirects into a chain from start to finish. - String clientRedirectSourceForHistory() const { return m_clientRedirectSourceForHistory; } // null if no client redirect occurred. - String clientRedirectDestinationForHistory() const { return urlForHistory(); } - void setClientRedirectSourceForHistory(const String& clientRedirectSourceForHistory) { m_clientRedirectSourceForHistory = clientRedirectSourceForHistory; } - - String serverRedirectSourceForHistory() const { return (urlForHistory() == url() || url() == blankURL()) ? String() : urlForHistory().string(); } // null if no server redirect occurred. - String serverRedirectDestinationForHistory() const { return url(); } - - bool didCreateGlobalHistoryEntry() const { return m_didCreateGlobalHistoryEntry; } - void setDidCreateGlobalHistoryEntry(bool didCreateGlobalHistoryEntry) { m_didCreateGlobalHistoryEntry = didCreateGlobalHistoryEntry; } - - bool subresourceLoadersArePageCacheAcceptable() const { return m_subresourceLoadersArePageCacheAcceptable; } - - void setDefersLoading(bool); - void setMainResourceDataBufferingPolicy(DataBufferingPolicy); - - void startLoadingMainResource(); - void cancelMainResourceLoad(const ResourceError&); - - // Support iconDatabase in synchronous mode. - void iconLoadDecisionAvailable(); - - // Support iconDatabase in asynchronous mode. - void continueIconLoadWithDecision(IconLoadDecision); - void getIconLoadDecisionForIconURL(const String&); - void getIconDataForIconURL(const String&); - - bool isLoadingMainResource() const { return m_loadingMainResource; } - bool isLoadingMultipartContent() const { return m_isLoadingMultipartContent; } - - void stopLoadingPlugIns(); - void stopLoadingSubresources(); - - void addSubresourceLoader(ResourceLoader*); - void removeSubresourceLoader(ResourceLoader*); - void addPlugInStreamLoader(ResourceLoader*); - void removePlugInStreamLoader(ResourceLoader*); - - void subresourceLoaderFinishedLoadingOnePart(ResourceLoader*); - - void setDeferMainResourceDataLoad(bool defer) { m_deferMainResourceDataLoad = defer; } - - void didTellClientAboutLoad(const String& url) - { -#if !PLATFORM(MAC) - // Don't include data urls here, as if a lot of data is loaded - // that way, we hold on to the (large) url string for too long. - if (protocolIs(url, "data")) - return; + bool isSubstituteLoadPending(ResourceLoader*) const; #endif - if (!url.isEmpty()) - m_resourcesClientKnowsAbout.add(url); - } - bool haveToldClientAboutLoad(const String& url) { return m_resourcesClientKnowsAbout.contains(url); } - void recordMemoryCacheLoadForFutureClientNotification(const ResourceRequest&); - void takeMemoryCacheLoadsForClientNotification(Vector<ResourceRequest>& loads); + void cancelPendingSubstituteLoad(ResourceLoader*); + + void addResponse(const ResourceResponse&); + const Vector<ResourceResponse>& responses() const { return m_responses; } + + const NavigationAction& triggeringAction() const { return m_triggeringAction; } + void setTriggeringAction(const NavigationAction&); + void setOverrideEncoding(const String& encoding) { m_overrideEncoding = encoding; } + void setLastCheckedRequest(const ResourceRequest& request) { m_lastCheckedRequest = request; } + const ResourceRequest& lastCheckedRequest() { return m_lastCheckedRequest; } + + void stopRecordingResponses(); + const StringWithDirection& title() const { return m_pageTitle; } + + WEBCORE_EXPORT URL urlForHistory() const; + WEBCORE_EXPORT bool urlForHistoryReflectsFailure() const; + + // These accessors accommodate WebCore's somewhat fickle custom of creating history + // items for redirects, but only sometimes. For "source" and "destination", + // these accessors return the URL that would have been used if a history + // item were created. This allows WebKit to link history items reflecting + // redirects into a chain from start to finish. + String clientRedirectSourceForHistory() const { return m_clientRedirectSourceForHistory; } // null if no client redirect occurred. + String clientRedirectDestinationForHistory() const { return urlForHistory(); } + void setClientRedirectSourceForHistory(const String& clientRedirectSourceForHistory) { m_clientRedirectSourceForHistory = clientRedirectSourceForHistory; } + + String serverRedirectSourceForHistory() const { return (urlForHistory() == url() || url() == blankURL()) ? String() : urlForHistory().string(); } // null if no server redirect occurred. + String serverRedirectDestinationForHistory() const { return url(); } + + bool didCreateGlobalHistoryEntry() const { return m_didCreateGlobalHistoryEntry; } + void setDidCreateGlobalHistoryEntry(bool didCreateGlobalHistoryEntry) { m_didCreateGlobalHistoryEntry = didCreateGlobalHistoryEntry; } + + bool subresourceLoadersArePageCacheAcceptable() const { return m_subresourceLoadersArePageCacheAcceptable; } + + void setDefersLoading(bool); + void setMainResourceDataBufferingPolicy(DataBufferingPolicy); + + void startLoadingMainResource(); + WEBCORE_EXPORT void cancelMainResourceLoad(const ResourceError&); + void willContinueMainResourceLoadAfterRedirect(const ResourceRequest&); + + // Support iconDatabase in synchronous mode. + void iconLoadDecisionAvailable(); + + // Support iconDatabase in asynchronous mode. + void continueIconLoadWithDecision(IconLoadDecision); + void getIconLoadDecisionForIconURL(const String&); + void getIconDataForIconURL(const String&); - DocumentLoadTiming* timing() { return &m_documentLoadTiming; } - void resetTiming() { m_documentLoadTiming = DocumentLoadTiming(); } + bool isLoadingMainResource() const { return m_loadingMainResource; } + bool isLoadingMultipartContent() const { return m_isLoadingMultipartContent; } - // The WebKit layer calls this function when it's ready for the data to - // actually be added to the document. - void commitData(const char* bytes, size_t length); + void stopLoadingPlugIns(); + void stopLoadingSubresources(); - ApplicationCacheHost* applicationCacheHost() const { return m_applicationCacheHost.get(); } + bool userContentExtensionsEnabled() const { return m_userContentExtensionsEnabled; } + void setUserContentExtensionsEnabled(bool enabled) { m_userContentExtensionsEnabled = enabled; } - void checkLoadComplete(); + AutoplayPolicy autoplayPolicy() const { return m_autoplayPolicy; } + void setAutoplayPolicy(AutoplayPolicy policy) { m_autoplayPolicy = policy; } -#if USE(CONTENT_FILTERING) - void setContentFilterForBlockedLoad(PassRefPtr<ContentFilter>); - bool handleContentFilterRequest(const ResourceRequest&); + void addSubresourceLoader(ResourceLoader*); + void removeSubresourceLoader(ResourceLoader*); + void addPlugInStreamLoader(ResourceLoader&); + void removePlugInStreamLoader(ResourceLoader&); + + void subresourceLoaderFinishedLoadingOnePart(ResourceLoader*); + + void setDeferMainResourceDataLoad(bool defer) { m_deferMainResourceDataLoad = defer; } + + void didTellClientAboutLoad(const String& url); + bool haveToldClientAboutLoad(const String& url) { return m_resourcesClientKnowsAbout.contains(url); } + void recordMemoryCacheLoadForFutureClientNotification(const ResourceRequest&); + void takeMemoryCacheLoadsForClientNotification(Vector<ResourceRequest>& loads); + + LoadTiming& timing() { return m_loadTiming; } + void resetTiming() { m_loadTiming = LoadTiming(); } + + // The WebKit layer calls this function when it's ready for the data to actually be added to the document. + WEBCORE_EXPORT void commitData(const char* bytes, size_t length); + + ApplicationCacheHost& applicationCacheHost() const; + ApplicationCacheHost* applicationCacheHostUnlessBeingDestroyed() const; + + void checkLoadComplete(); + + // The URL of the document resulting from this DocumentLoader. + URL documentURL() const; + +#if USE(QUICK_LOOK) + void setPreviewConverter(std::unique_ptr<PreviewConverter>&&); + PreviewConverter* previewConverter() const; +#endif + +#if ENABLE(CONTENT_EXTENSIONS) + void addPendingContentExtensionSheet(const String& identifier, StyleSheetContents&); + void addPendingContentExtensionDisplayNoneSelector(const String& identifier, const String& selector, uint32_t selectorID); +#endif + + void setShouldOpenExternalURLsPolicy(ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) { m_shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicy; } + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicyToPropagate() const; + +#if ENABLE(CONTENT_FILTERING) + ContentFilter* contentFilter() const; #endif - // The URL of the document resulting from this DocumentLoader. - URL documentURL() const; + bool isAlwaysOnLoggingAllowed() const; - protected: - DocumentLoader(const ResourceRequest&, const SubstituteData&); + void startIconLoading(); + WEBCORE_EXPORT void didGetLoadDecisionForIcon(bool decision, uint64_t loadIdentifier, uint64_t newCallbackID); + void finishedLoadingIcon(IconLoader&, SharedBuffer*); - bool m_deferMainResourceDataLoad; +protected: + WEBCORE_EXPORT DocumentLoader(const ResourceRequest&, const SubstituteData&); - private: - Document* document() const; + WEBCORE_EXPORT virtual void attachToFrame(); - void setRequest(const ResourceRequest&); + bool m_deferMainResourceDataLoad { true }; - void commitIfReady(); - void setMainDocumentError(const ResourceError&); - void commitLoad(const char*, int); - void clearMainResourceLoader(); +private: + Document* document() const; - void setupForReplace(); - void maybeFinishLoadingMultipartContent(); - - bool maybeCreateArchive(); + void setRequest(const ResourceRequest&); + + void commitIfReady(); + void setMainDocumentError(const ResourceError&); + void commitLoad(const char*, int); + void clearMainResourceLoader(); + + void setupForReplace(); + void maybeFinishLoadingMultipartContent(); + + bool maybeCreateArchive(); #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - void clearArchiveResources(); + void clearArchiveResources(); #endif - void willSendRequest(ResourceRequest&, const ResourceResponse&); - void finishedLoading(double finishTime); - void mainReceivedError(const ResourceError&); - virtual void redirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&) override; - virtual void responseReceived(CachedResource*, const ResourceResponse&) override; - virtual void dataReceived(CachedResource*, const char* data, int length) override; - virtual void notifyFinished(CachedResource*) override; + void willSendRequest(ResourceRequest&, const ResourceResponse&); + void finishedLoading(double finishTime); + void mainReceivedError(const ResourceError&); + WEBCORE_EXPORT void redirectReceived(CachedResource&, ResourceRequest&, const ResourceResponse&) override; + WEBCORE_EXPORT void responseReceived(CachedResource&, const ResourceResponse&) override; + WEBCORE_EXPORT void dataReceived(CachedResource&, const char* data, int length) override; + WEBCORE_EXPORT void notifyFinished(CachedResource&) override; + + void responseReceived(const ResourceResponse&); + void dataReceived(const char* data, int length); - bool maybeLoadEmpty(); + bool maybeLoadEmpty(); - bool isMultipartReplacingLoad() const; - bool isPostOrRedirectAfterPost(const ResourceRequest&, const ResourceResponse&); + bool isMultipartReplacingLoad() const; + bool isPostOrRedirectAfterPost(const ResourceRequest&, const ResourceResponse&); - void continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue); + void continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue); + void continueAfterContentPolicy(PolicyAction); - void continueAfterContentPolicy(PolicyAction); + void stopLoadingForPolicyChange(); + ResourceError interruptedForPolicyChangeError() const; - void stopLoadingForPolicyChange(); - ResourceError interruptedForPolicyChangeError() const; + void stopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied(unsigned long identifier, const ResourceResponse&); #if HAVE(RUNLOOP_TIMER) - typedef RunLoopTimer<DocumentLoader> DocumentLoaderTimer; + typedef RunLoopTimer<DocumentLoader> DocumentLoaderTimer; #else - typedef Timer<DocumentLoader> DocumentLoaderTimer; + typedef Timer DocumentLoaderTimer; #endif - void handleSubstituteDataLoadSoon(); - void handleSubstituteDataLoadNow(DocumentLoaderTimer*); - void startDataLoadTimer(); - - void deliverSubstituteResourcesAfterDelay(); - void substituteResourceDeliveryTimerFired(Timer<DocumentLoader>&); - - void clearMainResource(); - - Frame* m_frame; - Ref<CachedResourceLoader> m_cachedResourceLoader; - - CachedResourceHandle<CachedRawResource> m_mainResource; - ResourceLoaderSet m_subresourceLoaders; - ResourceLoaderSet m_multipartSubresourceLoaders; - ResourceLoaderSet m_plugInStreamLoaders; - - mutable DocumentWriter m_writer; - - // A reference to actual request used to create the data source. - // This should only be used by the resourceLoadDelegate's - // identifierForInitialRequest:fromDatasource: method. It is - // not guaranteed to remain unchanged, as requests are mutable. - ResourceRequest m_originalRequest; - - SubstituteData m_substituteData; - - // A copy of the original request used to create the data source. - // We have to copy the request because requests are mutable. - ResourceRequest m_originalRequestCopy; - - // The 'working' request. It may be mutated - // several times from the original request to include additional - // headers, cookie information, canonicalization and redirects. - ResourceRequest m_request; - - ResourceResponse m_response; + void handleSubstituteDataLoadSoon(); + void handleSubstituteDataLoadNow(); + void startDataLoadTimer(); + + void deliverSubstituteResourcesAfterDelay(); + void substituteResourceDeliveryTimerFired(); + + void clearMainResource(); + + void cancelPolicyCheckIfNeeded(); + void becomeMainResourceClient(); + + Frame* m_frame { nullptr }; + Ref<CachedResourceLoader> m_cachedResourceLoader; + + CachedResourceHandle<CachedRawResource> m_mainResource; + ResourceLoaderMap m_subresourceLoaders; + ResourceLoaderMap m_multipartSubresourceLoaders; + ResourceLoaderMap m_plugInStreamLoaders; - ResourceError m_mainDocumentError; - - bool m_originalSubstituteDataWasValid; - bool m_committed; - bool m_isStopping; - bool m_gotFirstByte; - bool m_isClientRedirect; - bool m_isLoadingMultipartContent; - - // FIXME: Document::m_processingLoadEvent and DocumentLoader::m_wasOnloadHandled are roughly the same - // and should be merged. - bool m_wasOnloadHandled; - - StringWithDirection m_pageTitle; - - String m_overrideEncoding; - - // The action that triggered loading - we keep this around for the - // benefit of the various policy handlers. - NavigationAction m_triggeringAction; - - // The last request that we checked click policy for - kept around - // so we can avoid asking again needlessly. - ResourceRequest m_lastCheckedRequest; - - // We retain all the received responses so we can play back the - // WebResourceLoadDelegate messages if the item is loaded from the - // page cache. - ResponseVector m_responses; - bool m_stopRecordingResponses; - - typedef HashMap<RefPtr<ResourceLoader>, RefPtr<SubstituteResource>> SubstituteResourceMap; - SubstituteResourceMap m_pendingSubstituteResources; - Timer<DocumentLoader> m_substituteResourceDeliveryTimer; - - OwnPtr<ArchiveResourceCollection> m_archiveResourceCollection; + mutable DocumentWriter m_writer; + + // A reference to actual request used to create the data source. + // This should only be used by the resourceLoadDelegate's + // identifierForInitialRequest:fromDatasource: method. It is + // not guaranteed to remain unchanged, as requests are mutable. + ResourceRequest m_originalRequest; + + SubstituteData m_substituteData; + + // A copy of the original request used to create the data source. + // We have to copy the request because requests are mutable. + ResourceRequest m_originalRequestCopy; + + // The 'working' request. It may be mutated + // several times from the original request to include additional + // headers, cookie information, canonicalization and redirects. + ResourceRequest m_request; + + ResourceResponse m_response; + + ResourceError m_mainDocumentError; + + bool m_originalSubstituteDataWasValid; + bool m_committed { false }; + bool m_isStopping { false }; + bool m_gotFirstByte { false }; + bool m_isClientRedirect { false }; + bool m_isLoadingMultipartContent { false }; + + // FIXME: Document::m_processingLoadEvent and DocumentLoader::m_wasOnloadDispatched are roughly the same + // and should be merged. + bool m_wasOnloadDispatched { false }; + + StringWithDirection m_pageTitle; + + String m_overrideEncoding; + + // The action that triggered loading - we keep this around for the + // benefit of the various policy handlers. + NavigationAction m_triggeringAction; + + // The last request that we checked click policy for - kept around + // so we can avoid asking again needlessly. + ResourceRequest m_lastCheckedRequest; + + // We retain all the received responses so we can play back the + // WebResourceLoadDelegate messages if the item is loaded from the + // page cache. + Vector<ResourceResponse> m_responses; + bool m_stopRecordingResponses { false }; + + typedef HashMap<RefPtr<ResourceLoader>, RefPtr<SubstituteResource>> SubstituteResourceMap; + SubstituteResourceMap m_pendingSubstituteResources; + Timer m_substituteResourceDeliveryTimer; + + std::unique_ptr<ArchiveResourceCollection> m_archiveResourceCollection; #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - RefPtr<Archive> m_archive; - RefPtr<SharedBuffer> m_parsedArchiveData; + RefPtr<Archive> m_archive; + RefPtr<SharedBuffer> m_parsedArchiveData; #endif - HashSet<String> m_resourcesClientKnowsAbout; - Vector<ResourceRequest> m_resourcesLoadedFromMemoryCacheForClientNotification; - - String m_clientRedirectSourceForHistory; - bool m_didCreateGlobalHistoryEntry; + HashSet<String> m_resourcesClientKnowsAbout; + Vector<ResourceRequest> m_resourcesLoadedFromMemoryCacheForClientNotification; + + String m_clientRedirectSourceForHistory; + bool m_didCreateGlobalHistoryEntry { false }; - bool m_loadingMainResource; - DocumentLoadTiming m_documentLoadTiming; + bool m_loadingMainResource { false }; + LoadTiming m_loadTiming; - double m_timeOfLastDataReceived; - unsigned long m_identifierForLoadWithoutResourceLoader; + MonotonicTime m_timeOfLastDataReceived; + unsigned long m_identifierForLoadWithoutResourceLoader { 0 }; - DocumentLoaderTimer m_dataLoadTimer; - bool m_waitingForContentPolicy; + DocumentLoaderTimer m_dataLoadTimer; + bool m_waitingForContentPolicy { false }; + bool m_waitingForNavigationPolicy { false }; - RefPtr<IconLoadDecisionCallback> m_iconLoadDecisionCallback; - RefPtr<IconDataCallback> m_iconDataCallback; + // For IconDatabase-style loads + RefPtr<IconLoadDecisionCallback> m_iconLoadDecisionCallback; + RefPtr<IconDataCallback> m_iconDataCallback; - bool m_subresourceLoadersArePageCacheAcceptable; + // For IconLoadingClient-style loads + HashMap<uint64_t, LinkIcon> m_iconsPendingLoadDecision; + HashMap<std::unique_ptr<IconLoader>, uint64_t> m_iconLoaders; - friend class ApplicationCacheHost; // for substitute resource delivery - OwnPtr<ApplicationCacheHost> m_applicationCacheHost; + bool m_subresourceLoadersArePageCacheAcceptable { false }; + ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow }; -#if USE(CONTENT_FILTERING) - RefPtr<ContentFilter> m_contentFilter; - RefPtr<ContentFilter> m_contentFilterForBlockedLoad; + std::unique_ptr<ApplicationCacheHost> m_applicationCacheHost; + +#if ENABLE(CONTENT_FILTERING) + std::unique_ptr<ContentFilter> m_contentFilter; #endif - }; - inline void DocumentLoader::recordMemoryCacheLoadForFutureClientNotification(const ResourceRequest& request) - { - m_resourcesLoadedFromMemoryCacheForClientNotification.append(request); - } +#if USE(QUICK_LOOK) + std::unique_ptr<PreviewConverter> m_previewConverter; +#endif - inline void DocumentLoader::takeMemoryCacheLoadsForClientNotification(Vector<ResourceRequest>& loadsSet) - { - loadsSet.swap(m_resourcesLoadedFromMemoryCacheForClientNotification); - m_resourcesLoadedFromMemoryCacheForClientNotification.clear(); - } +#if ENABLE(CONTENT_EXTENSIONS) + HashMap<String, RefPtr<StyleSheetContents>> m_pendingNamedContentExtensionStyleSheets; + HashMap<String, Vector<std::pair<String, uint32_t>>> m_pendingContentExtensionDisplayNoneSelectors; +#endif + bool m_userContentExtensionsEnabled { true }; + AutoplayPolicy m_autoplayPolicy { AutoplayPolicy::Default }; + +#ifndef NDEBUG + bool m_hasEverBeenAttached { false }; +#endif +}; + +inline void DocumentLoader::recordMemoryCacheLoadForFutureClientNotification(const ResourceRequest& request) +{ + m_resourcesLoadedFromMemoryCacheForClientNotification.append(request); +} + +inline void DocumentLoader::takeMemoryCacheLoadsForClientNotification(Vector<ResourceRequest>& loadsSet) +{ + loadsSet.swap(m_resourcesLoadedFromMemoryCacheForClientNotification); + m_resourcesLoadedFromMemoryCacheForClientNotification.clear(); +} + +inline const ResourceRequest& DocumentLoader::originalRequest() const +{ + return m_originalRequest; +} + +inline const ResourceRequest& DocumentLoader::originalRequestCopy() const +{ + return m_originalRequestCopy; +} + +inline const ResourceRequest& DocumentLoader::request() const +{ + return m_request; +} + +inline ResourceRequest& DocumentLoader::request() +{ + return m_request; +} + +inline const URL& DocumentLoader::url() const +{ + return m_request.url(); +} + +inline const URL& DocumentLoader::originalURL() const +{ + return m_originalRequestCopy.url(); +} + +inline const URL& DocumentLoader::responseURL() const +{ + return m_response.url(); +} + +inline const String& DocumentLoader::responseMIMEType() const +{ + return m_response.mimeType(); +} + +inline const String& DocumentLoader::currentContentType() const +{ + return m_writer.mimeType(); +} + +inline const URL& DocumentLoader::unreachableURL() const +{ + return m_substituteData.failingURL(); +} + +inline ApplicationCacheHost& DocumentLoader::applicationCacheHost() const +{ + // For a short time while the document loader is being destroyed, m_applicationCacheHost is null. + // It's not acceptable to call this function during that time. + ASSERT(m_applicationCacheHost); + return *m_applicationCacheHost; +} + +inline ApplicationCacheHost* DocumentLoader::applicationCacheHostUnlessBeingDestroyed() const +{ + return m_applicationCacheHost.get(); +} +#if ENABLE(CONTENT_FILTERING) + +inline ContentFilter* DocumentLoader::contentFilter() const +{ + return m_contentFilter.get(); } -#endif // DocumentLoader_h +#endif + +inline void DocumentLoader::didTellClientAboutLoad(const String& url) +{ +#if !PLATFORM(COCOA) + // Don't include data URLs here, as if a lot of data is loaded that way, we hold on to the (large) URL string for too long. + if (protocolIs(url, "data")) + return; +#endif + if (!url.isEmpty()) + m_resourcesClientKnowsAbout.add(url); +} + +} diff --git a/Source/WebCore/loader/DocumentThreadableLoader.cpp b/Source/WebCore/loader/DocumentThreadableLoader.cpp index a6013706b..b6b91e627 100644 --- a/Source/WebCore/loader/DocumentThreadableLoader.cpp +++ b/Source/WebCore/loader/DocumentThreadableLoader.cpp @@ -34,15 +34,22 @@ #include "CachedRawResource.h" #include "CachedResourceLoader.h" #include "CachedResourceRequest.h" +#include "CachedResourceRequestInitiators.h" #include "CrossOriginAccessControl.h" +#include "CrossOriginPreflightChecker.h" #include "CrossOriginPreflightResultCache.h" +#include "DOMWindow.h" #include "Document.h" -#include "DocumentThreadableLoaderClient.h" #include "Frame.h" #include "FrameLoader.h" #include "InspectorInstrumentation.h" +#include "LoadTiming.h" +#include "Performance.h" +#include "ProgressTracker.h" #include "ResourceError.h" #include "ResourceRequest.h" +#include "ResourceTiming.h" +#include "RuntimeEnabledFeatures.h" #include "SchemeRegistry.h" #include "SecurityOrigin.h" #include "SubresourceLoader.h" @@ -50,118 +57,153 @@ #include <wtf/Assertions.h> #include <wtf/Ref.h> -#if ENABLE(INSPECTOR) -#include "ProgressTracker.h" -#endif - namespace WebCore { -void DocumentThreadableLoader::loadResourceSynchronously(Document* document, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options) +void DocumentThreadableLoader::loadResourceSynchronously(Document& document, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, RefPtr<SecurityOrigin>&& origin, std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy) { // The loader will be deleted as soon as this function exits. - RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, &client, LoadSynchronously, request, options)); + Ref<DocumentThreadableLoader> loader = adoptRef(*new DocumentThreadableLoader(document, client, LoadSynchronously, WTFMove(request), options, WTFMove(origin), WTFMove(contentSecurityPolicy), String(), ShouldLogError::Yes)); ASSERT(loader->hasOneRef()); } -PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options) +void DocumentThreadableLoader::loadResourceSynchronously(Document& document, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options) +{ + loadResourceSynchronously(document, WTFMove(request), client, options, nullptr, nullptr); +} + +RefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient& client, +ResourceRequest&& request, const ThreadableLoaderOptions& options, RefPtr<SecurityOrigin>&& origin, +std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy, String&& referrer, ShouldLogError shouldLogError) +{ + RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, WTFMove(request), options, WTFMove(origin), WTFMove(contentSecurityPolicy), WTFMove(referrer), shouldLogError)); + if (!loader->isLoading()) + loader = nullptr; + return loader; +} + +RefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient& client, ResourceRequest&& request, const ThreadableLoaderOptions& options, String&& referrer) { - RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options)); - if (!loader->m_resource) - loader = 0; - return loader.release(); + return create(document, client, WTFMove(request), options, nullptr, nullptr, WTFMove(referrer), ShouldLogError::Yes); } -DocumentThreadableLoader::DocumentThreadableLoader(Document* document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options) - : m_client(client) +DocumentThreadableLoader::DocumentThreadableLoader(Document& document, ThreadableLoaderClient& client, BlockingBehavior blockingBehavior, ResourceRequest&& request, const ThreadableLoaderOptions& options, RefPtr<SecurityOrigin>&& origin, std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy, String&& referrer, ShouldLogError shouldLogError) + : m_client(&client) , m_document(document) , m_options(options) - , m_sameOriginRequest(securityOrigin()->canRequest(request.url())) + , m_origin(WTFMove(origin)) + , m_referrer(WTFMove(referrer)) + , m_sameOriginRequest(securityOrigin().canRequest(request.url())) , m_simpleRequest(true) , m_async(blockingBehavior == LoadAsynchronously) + , m_contentSecurityPolicy(WTFMove(contentSecurityPolicy)) + , m_shouldLogError(shouldLogError) { - ASSERT(document); - ASSERT(client); - // Setting an outgoing referer is only supported in the async code path. - ASSERT(m_async || request.httpReferrer().isEmpty()); + relaxAdoptionRequirement(); + + // Setting a referrer header is only supported in the async code path. + ASSERT(m_async || m_referrer.isEmpty()); + + // Referrer and Origin headers should be set after the preflight if any. + ASSERT(!request.hasHTTPReferrer() && !request.hasHTTPOrigin()); + + ASSERT_WITH_SECURITY_IMPLICATION(isAllowedByContentSecurityPolicy(request.url(), ContentSecurityPolicy::RedirectResponseReceived::No)); + + m_options.allowCredentials = (m_options.credentials == FetchOptions::Credentials::Include || (m_options.credentials == FetchOptions::Credentials::SameOrigin && m_sameOriginRequest)) ? AllowStoredCredentials : DoNotAllowStoredCredentials; + + ASSERT(!request.httpHeaderFields().contains(HTTPHeaderName::Origin)); + + // Copy headers if we need to replay the request after a redirection. + if (m_async && m_options.mode == FetchOptions::Mode::Cors) + m_originalHeaders = request.httpHeaderFields(); + + if (document.page() && document.page()->isRunningUserScripts() && SchemeRegistry::isUserExtensionScheme(request.url().protocol().toStringWithoutCopying())) { + m_options.mode = FetchOptions::Mode::NoCors; + m_options.filteringPolicy = ResponseFilteringPolicy::Disable; + } - if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) { - loadRequest(request, DoSecurityCheck); + // As per step 11 of https://fetch.spec.whatwg.org/#main-fetch, data scheme (if same-origin data-URL flag is set) and about scheme are considered same-origin. + if (request.url().protocolIsData()) + m_sameOriginRequest = options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set; + + if (m_sameOriginRequest || m_options.mode == FetchOptions::Mode::NoCors) { + loadRequest(WTFMove(request), DoSecurityCheck); return; } - if (m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) { - m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, request.url().string(), "Cross origin requests are not supported.")); + if (m_options.mode == FetchOptions::Mode::SameOrigin) { + logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, request.url(), "Cross origin requests are not allowed when using same-origin fetch mode.")); return; } - makeCrossOriginAccessRequest(request); + makeCrossOriginAccessRequest(WTFMove(request)); } -void DocumentThreadableLoader::makeCrossOriginAccessRequest(const ResourceRequest& request) +void DocumentThreadableLoader::makeCrossOriginAccessRequest(ResourceRequest&& request) { - ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl); - - OwnPtr<ResourceRequest> crossOriginRequest = adoptPtr(new ResourceRequest(request)); - updateRequestForAccessControl(*crossOriginRequest, securityOrigin(), m_options.allowCredentials); + ASSERT(m_options.mode == FetchOptions::Mode::Cors); - if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(crossOriginRequest->httpMethod(), crossOriginRequest->httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight) - makeSimpleCrossOriginAccessRequest(*crossOriginRequest); + if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight) + makeSimpleCrossOriginAccessRequest(WTFMove(request)); else { m_simpleRequest = false; - m_actualRequest = crossOriginRequest.release(); - - if (CrossOriginPreflightResultCache::shared().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), m_options.allowCredentials, m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields())) - preflightSuccess(); + if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin().toString(), request.url(), m_options.allowCredentials, request.httpMethod(), request.httpHeaderFields())) + preflightSuccess(WTFMove(request)); else - makeCrossOriginAccessRequestWithPreflight(*m_actualRequest); + makeCrossOriginAccessRequestWithPreflight(WTFMove(request)); } } -void DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest(const ResourceRequest& request) +void DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest(ResourceRequest&& request) { ASSERT(m_options.preflightPolicy != ForcePreflight); ASSERT(m_options.preflightPolicy == PreventPreflight || isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields())); // Cross-origin requests are only allowed for HTTP and registered schemes. We would catch this when checking response headers later, but there is no reason to send a request that's guaranteed to be denied. - if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())) { - m_client->didFailAccessControlCheck(ResourceError(errorDomainWebKitInternal, 0, request.url().string(), "Cross origin requests are only supported for HTTP.")); + if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol().toStringWithoutCopying())) { + logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, request.url(), "Cross origin requests are only supported for HTTP.", ResourceError::Type::AccessControl)); return; } - loadRequest(request, DoSecurityCheck); + updateRequestForAccessControl(request, securityOrigin(), m_options.allowCredentials); + loadRequest(WTFMove(request), DoSecurityCheck); } -void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request) +void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request) { - ResourceRequest preflightRequest = createAccessControlPreflightRequest(request, securityOrigin()); - loadRequest(preflightRequest, DoSecurityCheck); + if (m_async) { + m_preflightChecker.emplace(*this, WTFMove(request)); + m_preflightChecker->startPreflight(); + return; + } + CrossOriginPreflightChecker::doPreflight(*this, WTFMove(request)); } DocumentThreadableLoader::~DocumentThreadableLoader() { if (m_resource) - m_resource->removeClient(this); + m_resource->removeClient(*this); } void DocumentThreadableLoader::cancel() { - Ref<DocumentThreadableLoader> protect(*this); + Ref<DocumentThreadableLoader> protectedThis(*this); // Cancel can re-enter and m_resource might be null here as a result. if (m_client && m_resource) { // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use FrameLoaderClient::cancelledError() instead. - ResourceError error(errorDomainWebKitInternal, 0, m_resource->url(), "Load cancelled"); - error.setIsCancellation(true); - didFail(m_resource->identifier(), error); + ResourceError error(errorDomainWebKitInternal, 0, m_resource->url(), "Load cancelled", ResourceError::Type::Cancellation); + m_client->didFail(error); } clearResource(); - m_client = 0; + m_client = nullptr; } void DocumentThreadableLoader::setDefersLoading(bool value) { if (m_resource) m_resource->setDefersLoading(value); + if (m_preflightChecker) + m_preflightChecker->setDefersLoading(value); } void DocumentThreadableLoader::clearResource() @@ -171,142 +213,146 @@ void DocumentThreadableLoader::clearResource() // this DocumentThreadableLoader. Save off a copy of m_resource and clear it to // prevent the reentrancy. if (CachedResourceHandle<CachedRawResource> resource = m_resource) { - m_resource = 0; - resource->removeClient(this); + m_resource = nullptr; + resource->removeClient(*this); } + if (m_preflightChecker) + m_preflightChecker = std::nullopt; } -void DocumentThreadableLoader::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse) +void DocumentThreadableLoader::redirectReceived(CachedResource& resource, ResourceRequest& request, const ResourceResponse& redirectResponse) { ASSERT(m_client); - ASSERT_UNUSED(resource, resource == m_resource); + ASSERT_UNUSED(resource, &resource == m_resource); - Ref<DocumentThreadableLoader> protect(*this); - // Allow same origin requests to continue after allowing clients to audit the redirect. - if (isAllowedRedirect(request.url())) { - if (m_client->isDocumentThreadableLoaderClient()) - static_cast<DocumentThreadableLoaderClient*>(m_client)->willSendRequest(request, redirectResponse); + Ref<DocumentThreadableLoader> protectedThis(*this); + --m_options.maxRedirectCount; + + // FIXME: We restrict this check to Fetch API for the moment, as this might disrupt WorkerScriptLoader. + // Reassess this check based on https://github.com/whatwg/fetch/issues/393 discussions. + // We should also disable that check in navigation mode. + if (!request.url().protocolIsInHTTPFamily() && m_options.initiator == cachedResourceRequestInitiators().fetch) { + reportRedirectionWithBadScheme(request.url()); + clearResource(); return; } - // When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported - // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check. - if (m_options.crossOriginRequestPolicy == UseAccessControl) { - bool allowRedirect = false; - if (m_simpleRequest) { - String accessControlErrorDescription; - allowRedirect = SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol()) - && request.url().user().isEmpty() - && request.url().pass().isEmpty() - && passesAccessControlCheck(redirectResponse, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription); - } - - if (allowRedirect) { - if (m_resource) - clearResource(); - - RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::createFromString(redirectResponse.url()); - RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::createFromString(request.url()); - // If the request URL origin is not same origin with the original URL origin, set source origin to a globally unique identifier. - if (!originalOrigin->isSameSchemeHostPort(requestOrigin.get())) - m_options.securityOrigin = SecurityOrigin::createUnique(); - // Force any subsequent requests to use these checks. - m_sameOriginRequest = false; - - // Remove any headers that may have been added by the network layer that cause access control to fail. - request.clearHTTPContentType(); - request.clearHTTPReferrer(); - request.clearHTTPOrigin(); - request.clearHTTPUserAgent(); - request.clearHTTPAccept(); - makeCrossOriginAccessRequest(request); - return; - } + if (!isAllowedByContentSecurityPolicy(request.url(), redirectResponse.isNull() ? ContentSecurityPolicy::RedirectResponseReceived::No : ContentSecurityPolicy::RedirectResponseReceived::Yes)) { + reportContentSecurityPolicyError(redirectResponse.url()); + clearResource(); + return; } - m_client->didFailRedirectCheck(); - request = ResourceRequest(); + // Allow same origin requests to continue after allowing clients to audit the redirect. + if (isAllowedRedirect(request.url())) + return; + + // Force any subsequent request to use these checks. + m_sameOriginRequest = false; + + ASSERT(m_resource); + ASSERT(m_originalHeaders); + + // Use a unique for subsequent loads if needed. + // https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10). + ASSERT(m_options.mode == FetchOptions::Mode::Cors); + if (!securityOrigin().canRequest(redirectResponse.url()) && !protocolHostAndPortAreEqual(redirectResponse.url(), request.url())) + m_origin = SecurityOrigin::createUnique(); + + // Except in case where preflight is needed, loading should be able to continue on its own. + // But we also handle credentials here if it is restricted to SameOrigin. + if (m_options.credentials != FetchOptions::Credentials::SameOrigin && m_simpleRequest && isSimpleCrossOriginAccessRequest(request.httpMethod(), *m_originalHeaders)) + return; + + m_options.allowCredentials = DoNotAllowStoredCredentials; + + clearResource(); + + // Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm). + // Do not copy the Authorization header if removed by the network layer. + if (!request.httpHeaderFields().contains(HTTPHeaderName::Authorization)) + m_originalHeaders->remove(HTTPHeaderName::Authorization); + request.setHTTPHeaderFields(*m_originalHeaders); + + makeCrossOriginAccessRequest(ResourceRequest(request)); } -void DocumentThreadableLoader::dataSent(CachedResource* resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +void DocumentThreadableLoader::dataSent(CachedResource& resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { ASSERT(m_client); - ASSERT_UNUSED(resource, resource == m_resource); + ASSERT_UNUSED(resource, &resource == m_resource); m_client->didSendData(bytesSent, totalBytesToBeSent); } -void DocumentThreadableLoader::responseReceived(CachedResource* resource, const ResourceResponse& response) +void DocumentThreadableLoader::responseReceived(CachedResource& resource, const ResourceResponse& response) { - ASSERT_UNUSED(resource, resource == m_resource); - didReceiveResponse(m_resource->identifier(), response); + ASSERT_UNUSED(resource, &resource == m_resource); + didReceiveResponse(m_resource->identifier(), response, m_resource->responseTainting()); } -void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) +void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response, ResourceResponse::Tainting tainting) { ASSERT(m_client); + ASSERT(response.type() != ResourceResponse::Type::Error); - String accessControlErrorDescription; - if (m_actualRequest) { -#if ENABLE(INSPECTOR) - DocumentLoader* loader = m_document->frame()->loader().documentLoader(); - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_document->frame(), identifier, response); - InspectorInstrumentation::didReceiveResourceResponse(cookie, identifier, loader, response, 0); -#endif + InspectorInstrumentation::didReceiveThreadableLoaderResponse(*this, identifier); - if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) { - preflightFailure(identifier, response.url(), accessControlErrorDescription); - return; - } + if (options().filteringPolicy == ResponseFilteringPolicy::Disable) { + m_client->didReceiveResponse(identifier, response); + return; + } - OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(m_options.allowCredentials)); - if (!preflightResult->parse(response, accessControlErrorDescription) - || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription) - || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) { - preflightFailure(identifier, response.url(), accessControlErrorDescription); - return; + if (response.type() == ResourceResponse::Type::Default) { + m_client->didReceiveResponse(identifier, ResourceResponse::filterResponse(response, tainting)); + if (tainting == ResourceResponse::Tainting::Opaque) { + clearResource(); + if (m_client) + m_client->didFinishLoading(identifier, 0.0); } - - CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release()); } else { - if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) { - if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) { - m_client->didFailAccessControlCheck(ResourceError(errorDomainWebKitInternal, 0, response.url().string(), accessControlErrorDescription)); - return; - } - } - + ASSERT(response.type() == ResourceResponse::Type::Opaqueredirect); m_client->didReceiveResponse(identifier, response); } } -void DocumentThreadableLoader::dataReceived(CachedResource* resource, const char* data, int dataLength) +void DocumentThreadableLoader::dataReceived(CachedResource& resource, const char* data, int dataLength) { - ASSERT_UNUSED(resource, resource == m_resource); + ASSERT_UNUSED(resource, &resource == m_resource); didReceiveData(m_resource->identifier(), data, dataLength); } -void DocumentThreadableLoader::didReceiveData(unsigned long identifier, const char* data, int dataLength) +void DocumentThreadableLoader::didReceiveData(unsigned long, const char* data, int dataLength) { ASSERT(m_client); - // Preflight data should be invisible to clients. - if (m_actualRequest) { -#if ENABLE(INSPECTOR) - InspectorInstrumentation::didReceiveData(m_document->frame(), identifier, 0, 0, dataLength); -#else - UNUSED_PARAM(identifier); + m_client->didReceiveData(data, dataLength); +} + +void DocumentThreadableLoader::finishedTimingForWorkerLoad(CachedResource& resource, const ResourceTiming& resourceTiming) +{ + ASSERT(m_client); + ASSERT_UNUSED(resource, &resource == m_resource); + UNUSED_PARAM(resourceTiming); + +#if ENABLE(WEB_TIMING) + finishedTimingForWorkerLoad(resourceTiming); #endif - return; - } +} - m_client->didReceiveData(data, dataLength); +#if ENABLE(WEB_TIMING) +void DocumentThreadableLoader::finishedTimingForWorkerLoad(const ResourceTiming& resourceTiming) +{ + ASSERT(m_options.initiatorContext == InitiatorContext::Worker); + + m_client->didFinishTiming(resourceTiming); } +#endif -void DocumentThreadableLoader::notifyFinished(CachedResource* resource) +void DocumentThreadableLoader::notifyFinished(CachedResource& resource) { ASSERT(m_client); - ASSERT_UNUSED(resource, resource == m_resource); - + ASSERT_UNUSED(resource, &resource == m_resource); + if (m_resource->errorOccurred()) didFail(m_resource->identifier(), m_resource->resourceError()); else @@ -315,137 +361,226 @@ void DocumentThreadableLoader::notifyFinished(CachedResource* resource) void DocumentThreadableLoader::didFinishLoading(unsigned long identifier, double finishTime) { - if (m_actualRequest) { -#if ENABLE(INSPECTOR) - InspectorInstrumentation::didFinishLoading(m_document->frame(), m_document->frame()->loader().documentLoader(), identifier, finishTime); -#endif - ASSERT(!m_sameOriginRequest); - ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl); - preflightSuccess(); - } else - m_client->didFinishLoading(identifier, finishTime); + ASSERT(m_client); + m_client->didFinishLoading(identifier, finishTime); } -void DocumentThreadableLoader::didFail(unsigned long identifier, const ResourceError& error) +void DocumentThreadableLoader::didFail(unsigned long, const ResourceError& error) { -#if ENABLE(INSPECTOR) - if (m_actualRequest) - InspectorInstrumentation::didFailLoading(m_document->frame(), m_document->frame()->loader().documentLoader(), identifier, error); -#else - UNUSED_PARAM(identifier); -#endif - - m_client->didFail(error); + ASSERT(m_client); + logErrorAndFail(error); } -void DocumentThreadableLoader::preflightSuccess() +void DocumentThreadableLoader::preflightSuccess(ResourceRequest&& request) { - OwnPtr<ResourceRequest> actualRequest; - actualRequest.swap(m_actualRequest); + ResourceRequest actualRequest(WTFMove(request)); + updateRequestForAccessControl(actualRequest, securityOrigin(), m_options.allowCredentials); - actualRequest->setHTTPOrigin(securityOrigin()->toString()); - - clearResource(); + m_preflightChecker = std::nullopt; // It should be ok to skip the security check since we already asked about the preflight request. - loadRequest(*actualRequest, SkipSecurityCheck); + loadRequest(WTFMove(actualRequest), SkipSecurityCheck); } -void DocumentThreadableLoader::preflightFailure(unsigned long identifier, const String& url, const String& errorDescription) +void DocumentThreadableLoader::preflightFailure(unsigned long identifier, const ResourceError& error) { - ResourceError error(errorDomainWebKitInternal, 0, url, errorDescription); -#if ENABLE(INSPECTOR) - if (m_actualRequest) - InspectorInstrumentation::didFailLoading(m_document->frame(), m_document->frame()->loader().documentLoader(), identifier, error); -#else - UNUSED_PARAM(identifier); -#endif - m_actualRequest = nullptr; // Prevent didFinishLoading() from bypassing access check. - m_client->didFailAccessControlCheck(error); + m_preflightChecker = std::nullopt; + + InspectorInstrumentation::didFailLoading(m_document.frame(), m_document.frame()->loader().documentLoader(), identifier, error); + ASSERT(m_client); + logErrorAndFail(error); } -void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, SecurityCheckPolicy securityCheck) +void DocumentThreadableLoader::loadRequest(ResourceRequest&& request, SecurityCheckPolicy securityCheck) { + Ref<DocumentThreadableLoader> protectedThis(*this); + // Any credential should have been removed from the cross-site requests. const URL& requestURL = request.url(); m_options.securityCheck = securityCheck; ASSERT(m_sameOriginRequest || requestURL.user().isEmpty()); ASSERT(m_sameOriginRequest || requestURL.pass().isEmpty()); + if (!m_referrer.isNull()) + request.setHTTPReferrer(m_referrer); + if (m_async) { - ThreadableLoaderOptions options = m_options; - options.clientCredentialPolicy = DoNotAskClientForCrossOriginCredentials; - if (m_actualRequest) { - // Don't sniff content or send load callbacks for the preflight request. - options.sendLoadCallbacks = DoNotSendCallbacks; - options.sniffContent = DoNotSniffContent; - // Keep buffering the data for the preflight request. - options.dataBufferingPolicy = BufferData; - } + ResourceLoaderOptions options = m_options; + options.clientCredentialPolicy = m_sameOriginRequest ? ClientCredentialPolicy::MayAskClientForCredentials : ClientCredentialPolicy::CannotAskClientForCredentials; + options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck; + + request.setAllowCookies(m_options.allowCredentials == AllowStoredCredentials); + CachedResourceRequest newRequest(WTFMove(request), options); + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) + newRequest.setInitiator(m_options.initiator); + newRequest.setOrigin(securityOrigin()); - CachedResourceRequest newRequest(request, options); -#if ENABLE(RESOURCE_TIMING) - newRequest.setInitiator(m_options.initiator); -#endif ASSERT(!m_resource); - m_resource = m_document->cachedResourceLoader()->requestRawResource(newRequest); if (m_resource) { -#if ENABLE(INSPECTOR) - if (m_resource->loader()) { - unsigned long identifier = m_resource->loader()->identifier(); - InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(m_document, identifier, m_client); - } -#endif - m_resource->addClient(this); + CachedResourceHandle<CachedRawResource> resource = std::exchange(m_resource, nullptr); + resource->removeClient(*this); + } + + // We create an URL here as the request will be moved in requestRawResource + URL requestUrl = newRequest.resourceRequest().url(); + m_resource = m_document.cachedResourceLoader().requestRawResource(WTFMove(newRequest)); + if (m_resource) + m_resource->addClient(*this); + else { + // FIXME: Since we receive a synchronous error, this is probably due to some AccessControl checks. We should try to retrieve the actual error. + logErrorAndFail(ResourceError(String(), 0, requestUrl, String(), ResourceError::Type::AccessControl)); } return; } - + + // If credentials mode is 'Omit', we should disable cookie sending. + ASSERT(m_options.credentials != FetchOptions::Credentials::Omit); + +#if ENABLE(WEB_TIMING) + LoadTiming loadTiming; + loadTiming.markStartTimeAndFetchStart(); +#endif + // FIXME: ThreadableLoaderOptions.sniffContent is not supported for synchronous requests. - Vector<char> data; + RefPtr<SharedBuffer> data; ResourceError error; ResourceResponse response; unsigned long identifier = std::numeric_limits<unsigned long>::max(); - if (m_document->frame()) - identifier = m_document->frame()->loader().loadResourceSynchronously(request, m_options.allowCredentials, m_options.clientCredentialPolicy, error, response, data); + if (m_document.frame()) { + auto& frameLoader = m_document.frame()->loader(); + if (!frameLoader.mixedContentChecker().canRunInsecureContent(m_document.securityOrigin(), requestURL)) + return; + identifier = frameLoader.loadResourceSynchronously(request, m_options.allowCredentials, m_options.clientCredentialPolicy, error, response, data); + } - InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(m_document, identifier, m_client); +#if ENABLE(WEB_TIMING) + loadTiming.setResponseEnd(MonotonicTime::now()); +#endif - // No exception for file:/// resources, see <rdar://problem/4962298>. - // Also, if we have an HTTP response, then it wasn't a network error in fact. - if (!error.isNull() && !requestURL.isLocalFile() && response.httpStatusCode() <= 0) { - m_client->didFail(error); + if (!error.isNull() && response.httpStatusCode() <= 0) { + if (requestURL.isLocalFile()) { + // We don't want XMLHttpRequest to raise an exception for file:// resources, see <rdar://problem/4962298>. + // FIXME: XMLHttpRequest quirks should be in XMLHttpRequest code, not in DocumentThreadableLoader.cpp. + didReceiveResponse(identifier, response, ResourceResponse::Tainting::Basic); + didFinishLoading(identifier, 0.0); + return; + } + logErrorAndFail(error); return; } // FIXME: FrameLoader::loadSynchronously() does not tell us whether a redirect happened or not, so we guess by comparing the // request and response URLs. This isn't a perfect test though, since a server can serve a redirect to the same URL that was // requested. Also comparing the request and response URLs as strings will fail if the requestURL still has its credentials. - if (requestURL != response.url() && !isAllowedRedirect(response.url())) { - m_client->didFailRedirectCheck(); - return; + bool didRedirect = requestURL != response.url(); + if (didRedirect) { + if (!isAllowedByContentSecurityPolicy(response.url(), ContentSecurityPolicy::RedirectResponseReceived::Yes)) { + reportContentSecurityPolicyError(requestURL); + return; + } + if (!isAllowedRedirect(response.url())) { + reportCrossOriginResourceSharingError(requestURL); + return; + } } - didReceiveResponse(identifier, response); - - const char* bytes = static_cast<const char*>(data.data()); - int len = static_cast<int>(data.size()); - didReceiveData(identifier, bytes, len); + ResourceResponse::Tainting tainting = ResourceResponse::Tainting::Basic; + if (!m_sameOriginRequest) { + if (m_options.mode == FetchOptions::Mode::NoCors) + tainting = ResourceResponse::Tainting::Opaque; + else { + ASSERT(m_options.mode == FetchOptions::Mode::Cors); + tainting = ResourceResponse::Tainting::Cors; + String accessControlErrorDescription; + if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) { + logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, response.url(), accessControlErrorDescription, ResourceError::Type::AccessControl)); + return; + } + } + } + didReceiveResponse(identifier, response, tainting); + + if (data) + didReceiveData(identifier, data->data(), data->size()); + +#if ENABLE(WEB_TIMING) + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) { + ResourceTiming resourceTiming = ResourceTiming::fromSynchronousLoad(requestURL, m_options.initiator, loadTiming, response.networkLoadTiming(), response, securityOrigin()); + if (options().initiatorContext == InitiatorContext::Worker) + finishedTimingForWorkerLoad(resourceTiming); + else { + if (document().domWindow() && document().domWindow()->performance()) + document().domWindow()->performance()->addResourceTiming(WTFMove(resourceTiming)); + } + } +#endif didFinishLoading(identifier, 0.0); } +bool DocumentThreadableLoader::isAllowedByContentSecurityPolicy(const URL& url, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived) +{ + switch (m_options.contentSecurityPolicyEnforcement) { + case ContentSecurityPolicyEnforcement::DoNotEnforce: + return true; + case ContentSecurityPolicyEnforcement::EnforceChildSrcDirective: + return contentSecurityPolicy().allowChildContextFromSource(url, redirectResponseReceived); + case ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective: + return contentSecurityPolicy().allowConnectToSource(url, redirectResponseReceived); + case ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective: + return contentSecurityPolicy().allowScriptFromSource(url, redirectResponseReceived); + } + ASSERT_NOT_REACHED(); + return false; +} + bool DocumentThreadableLoader::isAllowedRedirect(const URL& url) { - if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) + if (m_options.mode == FetchOptions::Mode::NoCors) return true; - return m_sameOriginRequest && securityOrigin()->canRequest(url); + return m_sameOriginRequest && securityOrigin().canRequest(url); +} + +bool DocumentThreadableLoader::isXMLHttpRequest() const +{ + return m_options.initiator == cachedResourceRequestInitiators().xmlhttprequest; } -SecurityOrigin* DocumentThreadableLoader::securityOrigin() const +SecurityOrigin& DocumentThreadableLoader::securityOrigin() const { - return m_options.securityOrigin ? m_options.securityOrigin.get() : m_document->securityOrigin(); + return m_origin ? *m_origin : m_document.securityOrigin(); +} + +const ContentSecurityPolicy& DocumentThreadableLoader::contentSecurityPolicy() const +{ + if (m_contentSecurityPolicy) + return *m_contentSecurityPolicy.get(); + ASSERT(m_document.contentSecurityPolicy()); + return *m_document.contentSecurityPolicy(); +} + +void DocumentThreadableLoader::reportRedirectionWithBadScheme(const URL& url) +{ + logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Redirection to URL with a scheme that is not HTTP(S).", ResourceError::Type::AccessControl)); +} + +void DocumentThreadableLoader::reportContentSecurityPolicyError(const URL& url) +{ + logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Cross-origin redirection denied by Content Security Policy.", ResourceError::Type::AccessControl)); +} + +void DocumentThreadableLoader::reportCrossOriginResourceSharingError(const URL& url) +{ + logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Cross-origin redirection denied by Cross-Origin Resource Sharing policy.", ResourceError::Type::AccessControl)); +} + +void DocumentThreadableLoader::logErrorAndFail(const ResourceError& error) +{ + if (m_shouldLogError == ShouldLogError::Yes) + logError(m_document, error, m_options.initiator); + ASSERT(m_client); + m_client->didFail(error); } } // namespace WebCore diff --git a/Source/WebCore/loader/DocumentThreadableLoader.h b/Source/WebCore/loader/DocumentThreadableLoader.h index bf31ba310..37f951f75 100644 --- a/Source/WebCore/loader/DocumentThreadableLoader.h +++ b/Source/WebCore/loader/DocumentThreadableLoader.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2009, 2012 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,44 +29,45 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentThreadableLoader_h -#define DocumentThreadableLoader_h +#pragma once -#include "CachedRawResourceClient.h" -#include "CachedResourceHandle.h" -#include "FrameLoaderTypes.h" +#include "ContentSecurityPolicy.h" +#include "CrossOriginPreflightChecker.h" +#include "ResourceResponse.h" +#include "SecurityOrigin.h" #include "ThreadableLoader.h" -#include <wtf/Forward.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/text/WTFString.h> namespace WebCore { class CachedRawResource; + class ContentSecurityPolicy; class Document; - class URL; - class ResourceRequest; - class SecurityOrigin; class ThreadableLoaderClient; class DocumentThreadableLoader : public RefCounted<DocumentThreadableLoader>, public ThreadableLoader, private CachedRawResourceClient { WTF_MAKE_FAST_ALLOCATED; public: - static void loadResourceSynchronously(Document*, const ResourceRequest&, ThreadableLoaderClient&, const ThreadableLoaderOptions&); - static PassRefPtr<DocumentThreadableLoader> create(Document*, ThreadableLoaderClient*, const ResourceRequest&, const ThreadableLoaderOptions&); + static void loadResourceSynchronously(Document&, ResourceRequest&&, ThreadableLoaderClient&, const ThreadableLoaderOptions&, RefPtr<SecurityOrigin>&&, std::unique_ptr<ContentSecurityPolicy>&&); + static void loadResourceSynchronously(Document&, ResourceRequest&&, ThreadableLoaderClient&, const ThreadableLoaderOptions&); + + enum class ShouldLogError { No, Yes }; + static RefPtr<DocumentThreadableLoader> create(Document&, ThreadableLoaderClient&, ResourceRequest&&, const ThreadableLoaderOptions&, RefPtr<SecurityOrigin>&&, std::unique_ptr<ContentSecurityPolicy>&&, String&& referrer, ShouldLogError); + static RefPtr<DocumentThreadableLoader> create(Document&, ThreadableLoaderClient&, ResourceRequest&&, const ThreadableLoaderOptions&, String&& referrer = String()); + virtual ~DocumentThreadableLoader(); - virtual void cancel(); + void cancel() override; virtual void setDefersLoading(bool); + friend CrossOriginPreflightChecker; + friend class InspectorInstrumentation; + friend class InspectorNetworkAgent; + using RefCounted<DocumentThreadableLoader>::ref; using RefCounted<DocumentThreadableLoader>::deref; protected: - virtual void refThreadableLoader() { ref(); } - virtual void derefThreadableLoader() { deref(); } + void refThreadableLoader() override { ref(); } + void derefThreadableLoader() override { deref(); } private: enum BlockingBehavior { @@ -73,42 +75,65 @@ namespace WebCore { LoadAsynchronously }; - DocumentThreadableLoader(Document*, ThreadableLoaderClient*, BlockingBehavior, const ResourceRequest&, const ThreadableLoaderOptions&); + DocumentThreadableLoader(Document&, ThreadableLoaderClient&, BlockingBehavior, ResourceRequest&&, const ThreadableLoaderOptions&, RefPtr<SecurityOrigin>&&, std::unique_ptr<ContentSecurityPolicy>&&, String&&, ShouldLogError); void clearResource(); // CachedRawResourceClient - virtual void dataSent(CachedResource*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); - virtual void responseReceived(CachedResource*, const ResourceResponse&); - virtual void dataReceived(CachedResource*, const char* data, int dataLength); - virtual void redirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&); - virtual void notifyFinished(CachedResource*); - - void didReceiveResponse(unsigned long identifier, const ResourceResponse&); + void dataSent(CachedResource&, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + void responseReceived(CachedResource&, const ResourceResponse&) override; + void dataReceived(CachedResource&, const char* data, int dataLength) override; + void redirectReceived(CachedResource&, ResourceRequest&, const ResourceResponse&) override; + void finishedTimingForWorkerLoad(CachedResource&, const ResourceTiming&) override; + void notifyFinished(CachedResource&) override; + + void didReceiveResponse(unsigned long identifier, const ResourceResponse&, ResourceResponse::Tainting); void didReceiveData(unsigned long identifier, const char* data, int dataLength); void didFinishLoading(unsigned long identifier, double finishTime); void didFail(unsigned long identifier, const ResourceError&); - void makeCrossOriginAccessRequest(const ResourceRequest&); - void makeSimpleCrossOriginAccessRequest(const ResourceRequest& request); - void makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request); - void preflightSuccess(); - void preflightFailure(unsigned long identifier, const String& url, const String& errorDescription); + void makeCrossOriginAccessRequest(ResourceRequest&&); + void makeSimpleCrossOriginAccessRequest(ResourceRequest&&); + void makeCrossOriginAccessRequestWithPreflight(ResourceRequest&&); + void preflightSuccess(ResourceRequest&&); + void preflightFailure(unsigned long identifier, const ResourceError&); + +#if ENABLE(WEB_TIMING) + void finishedTimingForWorkerLoad(const ResourceTiming&); +#endif - void loadRequest(const ResourceRequest&, SecurityCheckPolicy); + void loadRequest(ResourceRequest&&, SecurityCheckPolicy); bool isAllowedRedirect(const URL&); + bool isAllowedByContentSecurityPolicy(const URL&, ContentSecurityPolicy::RedirectResponseReceived); - SecurityOrigin* securityOrigin() const; + bool isXMLHttpRequest() const final; + + SecurityOrigin& securityOrigin() const; + const ContentSecurityPolicy& contentSecurityPolicy() const; + + Document& document() { return m_document; } + const ThreadableLoaderOptions& options() const { return m_options; } + const String& referrer() const { return m_referrer; } + bool isLoading() { return m_resource || m_preflightChecker; } + + void reportRedirectionWithBadScheme(const URL&); + void reportContentSecurityPolicyError(const URL&); + void reportCrossOriginResourceSharingError(const URL&); + void logErrorAndFail(const ResourceError&); CachedResourceHandle<CachedRawResource> m_resource; ThreadableLoaderClient* m_client; - Document* m_document; + Document& m_document; ThreadableLoaderOptions m_options; + RefPtr<SecurityOrigin> m_origin; + String m_referrer; bool m_sameOriginRequest; bool m_simpleRequest; bool m_async; - OwnPtr<ResourceRequest> m_actualRequest; // non-null during Access Control preflight checks + std::unique_ptr<ContentSecurityPolicy> m_contentSecurityPolicy; + std::optional<CrossOriginPreflightChecker> m_preflightChecker; + std::optional<HTTPHeaderMap> m_originalHeaders; + + ShouldLogError m_shouldLogError; }; } // namespace WebCore - -#endif // DocumentThreadableLoader_h diff --git a/Source/WebCore/loader/DocumentWriter.cpp b/Source/WebCore/loader/DocumentWriter.cpp index 742eb9907..24b214cd3 100644 --- a/Source/WebCore/loader/DocumentWriter.cpp +++ b/Source/WebCore/loader/DocumentWriter.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010. Adam Barth. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,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. * @@ -29,6 +30,7 @@ #include "config.h" #include "DocumentWriter.h" +#include "ContentSecurityPolicy.h" #include "DOMImplementation.h" #include "DOMWindow.h" #include "Frame.h" @@ -36,26 +38,25 @@ #include "FrameLoaderClient.h" #include "FrameLoaderStateMachine.h" #include "FrameView.h" +#include "MIMETypeRegistry.h" +#include "MainFrame.h" #include "PluginDocument.h" #include "RawDataDocumentParser.h" #include "ScriptController.h" #include "ScriptableDocumentParser.h" #include "SecurityOrigin.h" +#include "SecurityOriginPolicy.h" #include "SegmentedString.h" #include "Settings.h" #include "SinkDocument.h" #include "TextResourceDecoder.h" #include <wtf/Ref.h> -#if PLATFORM(IOS) -#include "PDFDocument.h" -#endif - namespace WebCore { static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame) { - return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin()); + return parentFrame && parentFrame->document()->securityOrigin().canAccess(frame->document()->securityOrigin()); } DocumentWriter::DocumentWriter(Frame* frame) @@ -72,12 +73,23 @@ DocumentWriter::DocumentWriter(Frame* frame) void DocumentWriter::replaceDocument(const String& source, Document* ownerDocument) { m_frame->loader().stopAllLoaders(); + + // If we are in the midst of changing the frame's document, don't execute script + // that modifies the document further: + if (m_frame->documentIsBeingReplaced()) + return; + begin(m_frame->document()->url(), true, ownerDocument); + // begin() might fire an unload event, which will result in a situation where no new document has been attached, + // and the old document has been detached. Therefore, bail out if no document is attached. + if (!m_frame->document()) + return; + if (!source.isNull()) { if (!m_hasReceivedSomeData) { m_hasReceivedSomeData = true; - m_frame->document()->setCompatibilityMode(Document::NoQuirksMode); + m_frame->document()->setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode); } // FIXME: This should call DocumentParser::appendBytes instead of append @@ -91,7 +103,7 @@ void DocumentWriter::replaceDocument(const String& source, Document* ownerDocume void DocumentWriter::clear() { - m_decoder = 0; + m_decoder = nullptr; m_hasReceivedSomeData = false; if (!m_encodingWasChosenByUser) m_encoding = String(); @@ -102,17 +114,17 @@ void DocumentWriter::begin() begin(URL()); } -PassRefPtr<Document> DocumentWriter::createDocument(const URL& url) +Ref<Document> DocumentWriter::createDocument(const URL& url) { - if (!m_frame->loader().stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->loader().client().shouldAlwaysUsePluginDocument(m_mimeType)) + if (!m_frame->loader().stateMachine().isDisplayingInitialEmptyDocument() && m_frame->loader().client().shouldAlwaysUsePluginDocument(m_mimeType)) return PluginDocument::create(m_frame, url); #if PLATFORM(IOS) - if (equalIgnoringCase(m_mimeType, "application/pdf")) - return PDFDocument::create(m_frame, url); + if (MIMETypeRegistry::isPDFMIMEType(m_mimeType) && (m_frame->isMainFrame() || !m_frame->settings().useImageDocumentForSubframePDF())) + return SinkDocument::create(m_frame, url); #endif if (!m_frame->loader().client().hasHTMLView()) return Document::createNonRenderedPlaceholder(m_frame, url); - return DOMImplementation::createDocument(m_mimeType, m_frame, url, m_frame->inViewSourceMode()); + return DOMImplementation::createDocument(m_mimeType, m_frame, url); } void DocumentWriter::begin(const URL& urlReference, bool dispatch, Document* ownerDocument) @@ -124,7 +136,7 @@ void DocumentWriter::begin(const URL& urlReference, bool dispatch, Document* own // Create a new document before clearing the frame, because it may need to // inherit an aliased security context. - RefPtr<Document> document = createDocument(url); + Ref<Document> document = createDocument(url); // If the new document is for a Plugin but we're supposed to be sandboxed from Plugins, // then replace the document with one whose parser will ignore the incoming data (bug 39323) @@ -133,26 +145,42 @@ void DocumentWriter::begin(const URL& urlReference, bool dispatch, Document* own // FIXME: Do we need to consult the content security policy here about blocked plug-ins? - bool shouldReuseDefaultView = m_frame->loader().stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->document()->isSecureTransitionTo(url); + bool shouldReuseDefaultView = m_frame->loader().stateMachine().isDisplayingInitialEmptyDocument() && m_frame->document()->isSecureTransitionTo(url); if (shouldReuseDefaultView) document->takeDOMWindowFrom(m_frame->document()); else document->createDOMWindow(); - m_frame->loader().clear(document.get(), !shouldReuseDefaultView, !shouldReuseDefaultView); + // Per <http://www.w3.org/TR/upgrade-insecure-requests/>, we need to retain an ongoing set of upgraded + // requests in new navigation contexts. Although this information is present when we construct the + // Document object, it is discard in the subsequent 'clear' statements below. So, we must capture it + // so we can restore it. + HashSet<RefPtr<SecurityOrigin>> insecureNavigationRequestsToUpgrade; + if (auto* existingDocument = m_frame->document()) + insecureNavigationRequestsToUpgrade = existingDocument->contentSecurityPolicy()->takeNavigationRequestsToUpgrade(); + + m_frame->loader().clear(document.ptr(), !shouldReuseDefaultView, !shouldReuseDefaultView); clear(); + // m_frame->loader().clear() might fire unload event which could remove the view of the document. + // Bail out if document has no view. + if (!document->view()) + return; + if (!shouldReuseDefaultView) m_frame->script().updatePlatformScriptObjects(); m_frame->loader().setOutgoingReferrer(url); - m_frame->setDocument(document); + m_frame->setDocument(document.copyRef()); + + document->contentSecurityPolicy()->setInsecureNavigationRequestsToUpgrade(WTFMove(insecureNavigationRequestsToUpgrade)); if (m_decoder) document->setDecoder(m_decoder.get()); if (ownerDocument) { document->setCookieURL(ownerDocument->cookieURL()); - document->setSecurityOrigin(ownerDocument->securityOrigin()); + document->setSecurityOriginPolicy(ownerDocument->securityOriginPolicy()); + document->setStrictMixedContentMode(ownerDocument->isStrictMixedContentMode()); } m_frame->loader().didBeginDocument(dispatch); @@ -190,7 +218,7 @@ TextResourceDecoder* DocumentWriter::createDecoderIfNeeded() m_decoder->setHintEncoding(parentFrame->document()->decoder()); if (m_encoding.isEmpty()) { if (canReferToParentFrameEncoding(m_frame, parentFrame)) - m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame); + m_decoder->setEncoding(parentFrame->document()->textEncoding(), TextResourceDecoder::EncodingFromParentFrame); } else { m_decoder->setEncoding(m_encoding, m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader); @@ -246,7 +274,7 @@ void DocumentWriter::end() if (!m_parser) return; m_parser->finish(); - m_parser = 0; + m_parser = nullptr; } void DocumentWriter::setEncoding(const String& name, bool userChosen) diff --git a/Source/WebCore/loader/DocumentWriter.h b/Source/WebCore/loader/DocumentWriter.h index 81d0f129d..3f9518440 100644 --- a/Source/WebCore/loader/DocumentWriter.h +++ b/Source/WebCore/loader/DocumentWriter.h @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentWriter_h -#define DocumentWriter_h +#pragma once #include "URL.h" #include <wtf/text/WTFString.h> @@ -52,11 +51,11 @@ public: void begin(); void begin(const URL&, bool dispatchWindowObjectAvailable = true, Document* ownerDocument = 0); void addData(const char* bytes, size_t length); - void end(); + WEBCORE_EXPORT void end(); void setFrame(Frame* frame) { m_frame = frame; } - void setEncoding(const String& encoding, bool userChosen); + WEBCORE_EXPORT void setEncoding(const String& encoding, bool userChosen); const String& mimeType() const { return m_mimeType; } void setMIMEType(const String& type) { m_mimeType = type; } @@ -68,7 +67,7 @@ public: void setDocumentWasLoadedAsPartOfNavigation(); private: - PassRefPtr<Document> createDocument(const URL&); + Ref<Document> createDocument(const URL&); void clear(); Frame* m_frame; @@ -90,5 +89,3 @@ private: }; } // namespace WebCore - -#endif // DocumentWriter_h diff --git a/Source/WebCore/loader/EmptyClients.cpp b/Source/WebCore/loader/EmptyClients.cpp index faf4ce8f3..fb537cbb3 100644 --- a/Source/WebCore/loader/EmptyClients.cpp +++ b/Source/WebCore/loader/EmptyClients.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Eric Seidel <eric@webkit.org> - * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2011. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,167 +28,715 @@ #include "config.h" #include "EmptyClients.h" -#include "DateTimeChooser.h" +#include "ApplicationCacheStorage.h" +#include "BackForwardClient.h" +#include "ColorChooser.h" +#include "ContextMenuClient.h" +#include "DatabaseProvider.h" +#include "DiagnosticLoggingClient.h" +#include "DocumentFragment.h" #include "DocumentLoader.h" +#include "DragClient.h" +#include "EditorClient.h" #include "FileChooser.h" #include "FormState.h" #include "Frame.h" +#include "FrameLoaderClient.h" #include "FrameNetworkingContext.h" #include "HTMLFormElement.h" +#include "InProcessIDBServer.h" +#include "InspectorClient.h" +#include "NetworkStorageSession.h" +#include "Page.h" +#include "PageConfiguration.h" +#include "PaymentCoordinatorClient.h" +#include "PluginInfoProvider.h" +#include "ProgressTrackerClient.h" +#include "SecurityOriginData.h" +#include "StorageArea.h" +#include "StorageNamespace.h" +#include "StorageNamespaceProvider.h" +#include "StorageType.h" +#include "TextCheckerClient.h" +#include "ThreadableWebSocketChannel.h" +#include "UserContentProvider.h" +#include "VisitedLinkStore.h" +#include <heap/HeapInlines.h> #include <wtf/NeverDestroyed.h> -#if ENABLE(INPUT_TYPE_COLOR) -#include "ColorChooser.h" +#if ENABLE(CONTENT_EXTENSIONS) +#include "CompiledContentExtension.h" +#endif + +#if USE(QUICK_LOOK) +#include "QuickLookHandleClient.h" #endif namespace WebCore { -void fillWithEmptyClients(Page::PageClients& pageClients) -{ - static NeverDestroyed<EmptyChromeClient> dummyChromeClient; - pageClients.chromeClient = &dummyChromeClient.get(); +class UserMessageHandlerDescriptor; + +class EmptyBackForwardClient final : public BackForwardClient { + void addItem(Ref<HistoryItem>&&) final { } + void goToItem(HistoryItem*) final { } + HistoryItem* itemAtIndex(int) final { return nullptr; } + int backListCount() final { return 0; } + int forwardListCount() final { return 0; } + void close() final { } +}; #if ENABLE(CONTEXT_MENUS) - static NeverDestroyed<EmptyContextMenuClient> dummyContextMenuClient; - pageClients.contextMenuClient = &dummyContextMenuClient.get(); + +class EmptyContextMenuClient final : public ContextMenuClient { + void contextMenuDestroyed() final { } + + void downloadURL(const URL&) final { } + void searchWithGoogle(const Frame*) final { } + void lookUpInDictionary(Frame*) final { } + bool isSpeaking() final { return false; } + void speak(const String&) final { } + void stopSpeaking() final { } + +#if PLATFORM(COCOA) + void searchWithSpotlight() final { } +#endif + +#if USE(ACCESSIBILITY_CONTEXT_MENUS) + void showContextMenu() final { } #endif +}; + +#endif // ENABLE(CONTEXT_MENUS) + +class EmptyDatabaseProvider final : public DatabaseProvider { +#if ENABLE(INDEXED_DATABASE) + IDBClient::IDBConnectionToServer& idbConnectionToServerForSession(const SessionID&) final + { + static NeverDestroyed<Ref<InProcessIDBServer>> sharedConnection(InProcessIDBServer::create()); + return sharedConnection.get()->connectionToServer(); + } +#endif +}; + +class EmptyDiagnosticLoggingClient final : public DiagnosticLoggingClient { + void logDiagnosticMessage(const String&, const String&, ShouldSample) final { } + void logDiagnosticMessageWithResult(const String&, const String&, DiagnosticLoggingResultType, ShouldSample) final { } + void logDiagnosticMessageWithValue(const String&, const String&, double, unsigned, ShouldSample) final { } + void logDiagnosticMessageWithEnhancedPrivacy(const String&, const String&, ShouldSample) final { } +}; #if ENABLE(DRAG_SUPPORT) - static NeverDestroyed<EmptyDragClient> dummyDragClient; - pageClients.dragClient = &dummyDragClient.get(); + +class EmptyDragClient final : public DragClient { + void willPerformDragDestinationAction(DragDestinationAction, const DragData&) final { } + void willPerformDragSourceAction(DragSourceAction, const IntPoint&, DataTransfer&) final { } + DragDestinationAction actionMaskForDrag(const DragData&) final { return DragDestinationActionNone; } + DragSourceAction dragSourceActionMaskForPoint(const IntPoint&) final { return DragSourceActionNone; } + void startDrag(DragImage, const IntPoint&, const IntPoint&, const FloatPoint&, DataTransfer&, Frame&, DragSourceAction) final { } + void dragControllerDestroyed() final { } +}; + +#endif // ENABLE(DRAG_SUPPORT) + +class EmptyEditorClient final : public EditorClient { + WTF_MAKE_FAST_ALLOCATED; + +public: + EmptyEditorClient() = default; + +private: + bool shouldDeleteRange(Range*) final { return false; } + bool smartInsertDeleteEnabled() final { return false; } + bool isSelectTrailingWhitespaceEnabled() final { return false; } + bool isContinuousSpellCheckingEnabled() final { return false; } + void toggleContinuousSpellChecking() final { } + bool isGrammarCheckingEnabled() final { return false; } + void toggleGrammarChecking() final { } + int spellCheckerDocumentTag() final { return -1; } + + bool shouldBeginEditing(Range*) final { return false; } + bool shouldEndEditing(Range*) final { return false; } + bool shouldInsertNode(Node*, Range*, EditorInsertAction) final { return false; } + bool shouldInsertText(const String&, Range*, EditorInsertAction) final { return false; } + bool shouldChangeSelectedRange(Range*, Range*, EAffinity, bool) final { return false; } + + bool shouldApplyStyle(StyleProperties*, Range*) final { return false; } + void didApplyStyle() final { } + bool shouldMoveRangeAfterDelete(Range*, Range*) final { return false; } + + void didBeginEditing() final { } + void respondToChangedContents() final { } + void respondToChangedSelection(Frame*) final { } + void didChangeSelectionAndUpdateLayout() final { } + void updateEditorStateAfterLayoutIfEditabilityChanged() final { } + void discardedComposition(Frame*) final { } + void canceledComposition() final { } + void didEndEditing() final { } + void willWriteSelectionToPasteboard(Range*) final { } + void didWriteSelectionToPasteboard() final { } + void getClientPasteboardDataForRange(Range*, Vector<String>&, Vector<RefPtr<SharedBuffer>>&) final { } + void requestCandidatesForSelection(const VisibleSelection&) final { } + void handleAcceptedCandidateWithSoftSpaces(TextCheckingResult) final { } + + void registerUndoStep(UndoStep&) final; + void registerRedoStep(UndoStep&) final; + void clearUndoRedoOperations() final { } + + bool canCopyCut(Frame*, bool defaultValue) const final { return defaultValue; } + bool canPaste(Frame*, bool defaultValue) const final { return defaultValue; } + bool canUndo() const final { return false; } + bool canRedo() const final { return false; } + + void undo() final { } + void redo() final { } + + void handleKeyboardEvent(KeyboardEvent*) final { } + void handleInputMethodKeydown(KeyboardEvent*) final { } + + void textFieldDidBeginEditing(Element*) final { } + void textFieldDidEndEditing(Element*) final { } + void textDidChangeInTextField(Element*) final { } + bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) final { return false; } + void textWillBeDeletedInTextField(Element*) final { } + void textDidChangeInTextArea(Element*) final { } + void overflowScrollPositionChanged() final { } + +#if PLATFORM(IOS) + void startDelayingAndCoalescingContentChangeNotifications() final { } + void stopDelayingAndCoalescingContentChangeNotifications() final { } + void writeDataToPasteboard(NSDictionary*) final { } + NSArray* supportedPasteboardTypesForCurrentSelection() final { return nullptr; } + NSArray* readDataFromPasteboard(NSString*, int) final { return nullptr; } + bool hasRichlyEditableSelection() final { return false; } + int getPasteboardItemsCount() final { return 0; } + RefPtr<DocumentFragment> documentFragmentFromDelegate(int) final { return nullptr; } + bool performsTwoStepPaste(DocumentFragment*) final { return false; } + int pasteboardChangeCount() final { return 0; } #endif - static NeverDestroyed<EmptyEditorClient> dummyEditorClient; - pageClients.editorClient = &dummyEditorClient.get(); +#if PLATFORM(COCOA) + NSString *userVisibleString(NSURL *) final { return nullptr; } + void setInsertionPasteboard(const String&) final { }; + NSURL *canonicalizeURL(NSURL *) final { return nullptr; } + NSURL *canonicalizeURLString(NSString *) final { return nullptr; } +#endif - static NeverDestroyed<EmptyInspectorClient> dummyInspectorClient; - pageClients.inspectorClient = &dummyInspectorClient.get(); +#if USE(APPKIT) + void uppercaseWord() final { } + void lowercaseWord() final { } + void capitalizeWord() final { } +#endif - static NeverDestroyed<EmptyFrameLoaderClient> dummyFrameLoaderClient; - pageClients.loaderClientForMainFrame = &dummyFrameLoaderClient.get(); - - static NeverDestroyed<EmptyProgressTrackerClient> dummyProgressTrackerClient; - pageClients.progressTrackerClient = &dummyProgressTrackerClient.get(); -} +#if USE(AUTOMATIC_TEXT_REPLACEMENT) + void showSubstitutionsPanel(bool) final { } + bool substitutionsPanelIsShowing() final { return false; } + void toggleSmartInsertDelete() final { } + bool isAutomaticQuoteSubstitutionEnabled() final { return false; } + void toggleAutomaticQuoteSubstitution() final { } + bool isAutomaticLinkDetectionEnabled() final { return false; } + void toggleAutomaticLinkDetection() final { } + bool isAutomaticDashSubstitutionEnabled() final { return false; } + void toggleAutomaticDashSubstitution() final { } + bool isAutomaticTextReplacementEnabled() final { return false; } + void toggleAutomaticTextReplacement() final { } + bool isAutomaticSpellingCorrectionEnabled() final { return false; } + void toggleAutomaticSpellingCorrection() final { } +#endif + +#if PLATFORM(GTK) + bool shouldShowUnicodeMenu() final { return false; } +#endif + + TextCheckerClient* textChecker() final { return &m_textCheckerClient; } + + void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) final { } + void updateSpellingUIWithMisspelledWord(const String&) final { } + void showSpellingUI(bool) final { } + bool spellingUIIsShowing() final { return false; } + + void willSetInputMethodState() final { } + void setInputMethodState(bool) final { } + + class EmptyTextCheckerClient final : public TextCheckerClient { + bool shouldEraseMarkersAfterChangeSelection(TextCheckingType) const final { return true; } + void ignoreWordInSpellDocument(const String&) final { } + void learnWord(const String&) final { } + void checkSpellingOfString(StringView, int*, int*) final { } + String getAutoCorrectSuggestionForMisspelledWord(const String&) final { return { }; } + void checkGrammarOfString(StringView, Vector<GrammarDetail>&, int*, int*) final { } + +#if USE(UNIFIED_TEXT_CHECKING) + Vector<TextCheckingResult> checkTextOfParagraph(StringView, TextCheckingTypeMask, const VisibleSelection&) final { return Vector<TextCheckingResult>(); } +#endif + + void getGuessesForWord(const String&, const String&, const VisibleSelection&, Vector<String>&) final { } + void requestCheckingOfString(TextCheckingRequest&, const VisibleSelection&) final; + }; + + EmptyTextCheckerClient m_textCheckerClient; +}; + +class EmptyFrameLoaderClient final : public FrameLoaderClient { + void frameLoaderDestroyed() final { } + + bool hasWebView() const final { return true; } // mainly for assertions + + void makeRepresentation(DocumentLoader*) final { } +#if PLATFORM(IOS) + bool forceLayoutOnRestoreFromPageCache() final { return false; } +#endif + void forceLayoutForNonHTML() final { } + + void setCopiesOnScroll() final { } + + void detachedFromParent2() final { } + void detachedFromParent3() final { } + + void convertMainResourceLoadToDownload(DocumentLoader*, SessionID, const ResourceRequest&, const ResourceResponse&) final { } + + void assignIdentifierToInitialRequest(unsigned long, DocumentLoader*, const ResourceRequest&) final { } + bool shouldUseCredentialStorage(DocumentLoader*, unsigned long) final { return false; } + void dispatchWillSendRequest(DocumentLoader*, unsigned long, ResourceRequest&, const ResourceResponse&) final { } + void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) final { } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + bool canAuthenticateAgainstProtectionSpace(DocumentLoader*, unsigned long, const ProtectionSpace&) final { return false; } +#endif + +#if PLATFORM(IOS) + RetainPtr<CFDictionaryRef> connectionProperties(DocumentLoader*, unsigned long) final { return nullptr; } +#endif + + void dispatchDidReceiveResponse(DocumentLoader*, unsigned long, const ResourceResponse&) final { } + void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long, int) final { } + void dispatchDidFinishLoading(DocumentLoader*, unsigned long) final { } +#if ENABLE(DATA_DETECTION) + void dispatchDidFinishDataDetection(NSArray *) final { } +#endif + void dispatchDidFailLoading(DocumentLoader*, unsigned long, const ResourceError&) final { } + bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int) final { return false; } + + void dispatchDidDispatchOnloadEvents() final { } + void dispatchDidReceiveServerRedirectForProvisionalLoad() final { } + void dispatchDidCancelClientRedirect() final { } + void dispatchWillPerformClientRedirect(const URL&, double, double) final { } + void dispatchDidChangeLocationWithinPage() final { } + void dispatchDidPushStateWithinPage() final { } + void dispatchDidReplaceStateWithinPage() final { } + void dispatchDidPopStateWithinPage() final { } + void dispatchWillClose() final { } + void dispatchDidReceiveIcon() final { } + void dispatchDidStartProvisionalLoad() final { } + void dispatchDidReceiveTitle(const StringWithDirection&) final { } + void dispatchDidCommitLoad(std::optional<HasInsecureContent>) final { } + void dispatchDidFailProvisionalLoad(const ResourceError&) final { } + void dispatchDidFailLoad(const ResourceError&) final { } + void dispatchDidFinishDocumentLoad() final { } + void dispatchDidFinishLoad() final { } + void dispatchDidReachLayoutMilestone(LayoutMilestones) final { } + + Frame* dispatchCreatePage(const NavigationAction&) final { return nullptr; } + void dispatchShow() final { } + + void dispatchDecidePolicyForResponse(const ResourceResponse&, const ResourceRequest&, FramePolicyFunction) final { } + void dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, FormState*, const String&, FramePolicyFunction) final; + void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, FormState*, FramePolicyFunction) final; + void cancelPolicyCheck() final { } + + void dispatchUnableToImplementPolicy(const ResourceError&) final { } + + void dispatchWillSendSubmitEvent(Ref<FormState>&&) final; + void dispatchWillSubmitForm(FormState&, FramePolicyFunction) final; + + void revertToProvisionalState(DocumentLoader*) final { } + void setMainDocumentError(DocumentLoader*, const ResourceError&) final { } + + void setMainFrameDocumentReady(bool) final { } + + void startDownload(const ResourceRequest&, const String&) final { } + + void willChangeTitle(DocumentLoader*) final { } + void didChangeTitle(DocumentLoader*) final { } + + void willReplaceMultipartContent() final { } + void didReplaceMultipartContent() final { } + + void committedLoad(DocumentLoader*, const char*, int) final { } + void finishedLoading(DocumentLoader*) final { } + + ResourceError cancelledError(const ResourceRequest&) final { return { ResourceError::Type::Cancellation }; } + ResourceError blockedError(const ResourceRequest&) final { return { }; } + ResourceError blockedByContentBlockerError(const ResourceRequest&) final { return { }; } + ResourceError cannotShowURLError(const ResourceRequest&) final { return { }; } + ResourceError interruptedForPolicyChangeError(const ResourceRequest&) final { return { }; } +#if ENABLE(CONTENT_FILTERING) + ResourceError blockedByContentFilterError(const ResourceRequest&) final { return { }; } +#endif + + ResourceError cannotShowMIMETypeError(const ResourceResponse&) final { return { }; } + ResourceError fileDoesNotExistError(const ResourceResponse&) final { return { }; } + ResourceError pluginWillHandleLoadError(const ResourceResponse&) final { return { }; } + + bool shouldFallBack(const ResourceError&) final { return false; } + + bool canHandleRequest(const ResourceRequest&) const final { return false; } + bool canShowMIMEType(const String&) const final { return false; } + bool canShowMIMETypeAsHTML(const String&) const final { return false; } + bool representationExistsForURLScheme(const String&) const final { return false; } + String generatedMIMETypeForURLScheme(const String&) const final { return emptyString(); } + + void frameLoadCompleted() final { } + void restoreViewState() final { } + void provisionalLoadStarted() final { } + void didFinishLoad() final { } + void prepareForDataSourceReplacement() final { } + + Ref<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&) final; + void updateCachedDocumentLoader(DocumentLoader&) final { } + void setTitle(const StringWithDirection&, const URL&) final { } + + String userAgent(const URL&) final { return emptyString(); } + + void savePlatformDataToCachedFrame(CachedFrame*) final { } + void transitionToCommittedFromCachedFrame(CachedFrame*) final { } +#if PLATFORM(IOS) + void didRestoreFrameHierarchyForCachedFrame() final { } +#endif + void transitionToCommittedForNewPage() final { } + + void didSaveToPageCache() final { } + void didRestoreFromPageCache() final { } + + void dispatchDidBecomeFrameset(bool) final { } + + void updateGlobalHistory() final { } + void updateGlobalHistoryRedirectLinks() final { } + bool shouldGoToHistoryItem(HistoryItem*) const final { return false; } + void updateGlobalHistoryItemForPage() final { } + void saveViewStateToItem(HistoryItem&) final { } + bool canCachePage() const final { return false; } + void didDisplayInsecureContent() final { } + void didRunInsecureContent(SecurityOrigin&, const URL&) final { } + void didDetectXSS(const URL&, bool) final { } + RefPtr<Frame> createFrame(const URL&, const String&, HTMLFrameOwnerElement&, const String&, bool, int, int) final; + RefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement&, const URL&, const Vector<String>&, const Vector<String>&, const String&, bool) final; + void recreatePlugin(Widget*) final; + RefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement&, const URL&, const Vector<String>&, const Vector<String>&) final; + + ObjectContentType objectContentType(const URL&, const String&) final { return ObjectContentType::None; } + String overrideMediaType() const final { return { }; } + + void redirectDataToPlugin(Widget&) final { } + void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld&) final { } + + void registerForIconNotification(bool) final { } + +#if PLATFORM(COCOA) + RemoteAXObjectRef accessibilityRemoteObject() final { return nullptr; } + NSCachedURLResponse *willCacheResponse(DocumentLoader*, unsigned long, NSCachedURLResponse *response) const final { return response; } +#endif + +#if PLATFORM(WIN) && USE(CFURLCONNECTION) + bool shouldCacheResponse(DocumentLoader*, unsigned long, const ResourceResponse&, const unsigned char*, unsigned long long) final { return true; } +#endif + + Ref<FrameNetworkingContext> createNetworkingContext() final; + +#if ENABLE(REQUEST_AUTOCOMPLETE) + void didRequestAutocomplete(Ref<FormState>&&) final { } +#endif + + bool isEmptyFrameLoaderClient() final { return true; } + void prefetchDNS(const String&) final { } + +#if USE(QUICK_LOOK) + RefPtr<QuickLookHandleClient> createQuickLookHandleClient(const String&, const String&) final { return nullptr; } +#endif +}; + +class EmptyFrameNetworkingContext final : public FrameNetworkingContext { +public: + static Ref<EmptyFrameNetworkingContext> create() { return adoptRef(*new EmptyFrameNetworkingContext); } + +private: + EmptyFrameNetworkingContext(); + + bool shouldClearReferrerOnHTTPSToHTTPRedirect() const { return true; } + NetworkStorageSession& storageSession() const final { return NetworkStorageSession::defaultStorageSession(); } + +#if PLATFORM(COCOA) + bool localFileContentSniffingEnabled() const { return false; } + SchedulePairHashSet* scheduledRunLoopPairs() const { return nullptr; } + RetainPtr<CFDataRef> sourceApplicationAuditData() const { return nullptr; }; +#endif + +#if PLATFORM(COCOA) || PLATFORM(WIN) + ResourceError blockedError(const ResourceRequest&) const final { return { }; } +#endif +}; + +class EmptyInspectorClient final : public InspectorClient { + void inspectedPageDestroyed() final { } + Inspector::FrontendChannel* openLocalFrontend(InspectorController*) final { return nullptr; } + void bringFrontendToFront() final { } + void highlight() final { } + void hideHighlight() final { } +}; + +#if ENABLE(APPLE_PAY) + +class EmptyPaymentCoordinatorClient final : public PaymentCoordinatorClient { + bool supportsVersion(unsigned) final { return false; } + bool canMakePayments() final { return false; } + void canMakePaymentsWithActiveCard(const String&, const String&, std::function<void(bool)> completionHandler) final { callOnMainThread([completionHandler] { completionHandler(false); }); } + void openPaymentSetup(const String&, const String&, std::function<void(bool)> completionHandler) final { callOnMainThread([completionHandler] { completionHandler(false); }); } + bool showPaymentUI(const URL&, const Vector<URL>&, const PaymentRequest&) final { return false; } + void completeMerchantValidation(const PaymentMerchantSession&) final { } + void completeShippingMethodSelection(PaymentAuthorizationStatus, std::optional<PaymentRequest::TotalAndLineItems>) final { } + void completeShippingContactSelection(PaymentAuthorizationStatus, const Vector<PaymentRequest::ShippingMethod>&, std::optional<PaymentRequest::TotalAndLineItems>) final { } + void completePaymentMethodSelection(std::optional<WebCore::PaymentRequest::TotalAndLineItems>) final { } + void completePaymentSession(PaymentAuthorizationStatus) final { } + void abortPaymentSession() final { } + void paymentCoordinatorDestroyed() final { } +}; + +#endif + +class EmptyPluginInfoProvider final : public PluginInfoProvider { + void refreshPlugins() final { }; + void getPluginInfo(Page&, Vector<PluginInfo>&) final { } + void getWebVisiblePluginInfo(Page&, Vector<PluginInfo>&) final { } +}; class EmptyPopupMenu : public PopupMenu { public: - virtual void show(const IntRect&, FrameView*, int) { } - virtual void hide() { } - virtual void updateFromElement() { } - virtual void disconnectClient() { } + EmptyPopupMenu() = default; +private: + void show(const IntRect&, FrameView*, int) final { } + void hide() final { } + void updateFromElement() final { } + void disconnectClient() final { } +}; + +class EmptyProgressTrackerClient final : public ProgressTrackerClient { + void willChangeEstimatedProgress() final { } + void didChangeEstimatedProgress() final { } + void progressStarted(Frame&) final { } + void progressEstimateChanged(Frame&) final { } + void progressFinished(Frame&) final { } }; class EmptySearchPopupMenu : public SearchPopupMenu { public: - virtual PopupMenu* popupMenu() { return m_popup.get(); } - virtual void saveRecentSearches(const AtomicString&, const Vector<String>&) { } - virtual void loadRecentSearches(const AtomicString&, Vector<String>&) { } - virtual bool enabled() { return false; } + EmptySearchPopupMenu() + : m_popup(adoptRef(*new EmptyPopupMenu)) + { + } private: - RefPtr<EmptyPopupMenu> m_popup; + PopupMenu* popupMenu() final { return m_popup.ptr(); } + void saveRecentSearches(const AtomicString&, const Vector<RecentSearch>&) final { } + void loadRecentSearches(const AtomicString&, Vector<RecentSearch>&) final { } + bool enabled() final { return false; } + + Ref<EmptyPopupMenu> m_popup; +}; + +class EmptyStorageNamespaceProvider final : public StorageNamespaceProvider { + struct EmptyStorageArea : public StorageArea { + unsigned length() final { return 0; } + String key(unsigned) final { return { }; } + String item(const String&) final { return { }; } + void setItem(Frame*, const String&, const String&, bool&) final { } + void removeItem(Frame*, const String&) final { } + void clear(Frame*) final { } + bool contains(const String&) final { return false; } + bool canAccessStorage(Frame*) final { return false; } + StorageType storageType() const final { return StorageType::Local; } + size_t memoryBytesUsedByCache() final { return 0; } + SecurityOriginData securityOrigin() const final { return { }; } + }; + + struct EmptyStorageNamespace final : public StorageNamespace { + RefPtr<StorageArea> storageArea(const SecurityOriginData&) final { return adoptRef(*new EmptyStorageArea); } + RefPtr<StorageNamespace> copy(Page*) final { return adoptRef(*new EmptyStorageNamespace); } + }; + + RefPtr<StorageNamespace> createSessionStorageNamespace(Page&, unsigned) final; + RefPtr<StorageNamespace> createLocalStorageNamespace(unsigned) final; + RefPtr<StorageNamespace> createEphemeralLocalStorageNamespace(Page&, unsigned) final; + RefPtr<StorageNamespace> createTransientLocalStorageNamespace(SecurityOrigin&, unsigned) final; +}; + +class EmptyUserContentProvider final : public UserContentProvider { + void forEachUserScript(const std::function<void(DOMWrapperWorld&, const UserScript&)>&) const final { } + void forEachUserStyleSheet(const std::function<void(const UserStyleSheet&)>&) const final { } +#if ENABLE(USER_MESSAGE_HANDLERS) + void forEachUserMessageHandler(const std::function<void(const UserMessageHandlerDescriptor&)>&) const final { } +#endif +#if ENABLE(CONTENT_EXTENSIONS) + ContentExtensions::ContentExtensionsBackend& userContentExtensionBackend() final { static NeverDestroyed<ContentExtensions::ContentExtensionsBackend> backend; return backend.get(); }; +#endif }; -PassRefPtr<PopupMenu> EmptyChromeClient::createPopupMenu(PopupMenuClient*) const +class EmptyVisitedLinkStore final : public VisitedLinkStore { + bool isLinkVisited(Page&, LinkHash, const URL&, const AtomicString&) final { return false; } + void addVisitedLink(Page&, LinkHash) final { } +}; + +RefPtr<PopupMenu> EmptyChromeClient::createPopupMenu(PopupMenuClient&) const { - return adoptRef(new EmptyPopupMenu()); + return adoptRef(*new EmptyPopupMenu); } -PassRefPtr<SearchPopupMenu> EmptyChromeClient::createSearchPopupMenu(PopupMenuClient*) const +RefPtr<SearchPopupMenu> EmptyChromeClient::createSearchPopupMenu(PopupMenuClient&) const { - return adoptRef(new EmptySearchPopupMenu()); + return adoptRef(*new EmptySearchPopupMenu); } #if ENABLE(INPUT_TYPE_COLOR) -PassOwnPtr<ColorChooser> EmptyChromeClient::createColorChooser(ColorChooserClient*, const Color&) -{ - return nullptr; -} -#endif -#if ENABLE(DATE_AND_TIME_INPUT_TYPES) && !PLATFORM(IOS) -PassRefPtr<DateTimeChooser> EmptyChromeClient::openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) +std::unique_ptr<ColorChooser> EmptyChromeClient::createColorChooser(ColorChooserClient&, const Color&) { return nullptr; } + #endif -void EmptyChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser>) +void EmptyChromeClient::runOpenPanel(Frame&, FileChooser&) { } -void EmptyFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String&, FramePolicyFunction) +void EmptyFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, FormState*, const String&, FramePolicyFunction) { } -void EmptyFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, FramePolicyFunction) +void EmptyFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, FormState*, FramePolicyFunction) { } -void EmptyFrameLoaderClient::dispatchWillSendSubmitEvent(PassRefPtr<FormState>) +void EmptyFrameLoaderClient::dispatchWillSendSubmitEvent(Ref<FormState>&&) { } -void EmptyFrameLoaderClient::dispatchWillSubmitForm(PassRefPtr<FormState>, FramePolicyFunction) +void EmptyFrameLoaderClient::dispatchWillSubmitForm(FormState&, FramePolicyFunction) { } -PassRefPtr<DocumentLoader> EmptyFrameLoaderClient::createDocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData) +Ref<DocumentLoader> EmptyFrameLoaderClient::createDocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData) { return DocumentLoader::create(request, substituteData); } -PassRefPtr<Frame> EmptyFrameLoaderClient::createFrame(const URL&, const String&, HTMLFrameOwnerElement*, const String&, bool, int, int) +RefPtr<Frame> EmptyFrameLoaderClient::createFrame(const URL&, const String&, HTMLFrameOwnerElement&, const String&, bool, int, int) { - return 0; + return nullptr; } -PassRefPtr<Widget> EmptyFrameLoaderClient::createPlugin(const IntSize&, HTMLPlugInElement*, const URL&, const Vector<String>&, const Vector<String>&, const String&, bool) +RefPtr<Widget> EmptyFrameLoaderClient::createPlugin(const IntSize&, HTMLPlugInElement&, const URL&, const Vector<String>&, const Vector<String>&, const String&, bool) { - return 0; + return nullptr; } void EmptyFrameLoaderClient::recreatePlugin(Widget*) { } -PassRefPtr<Widget> EmptyFrameLoaderClient::createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const URL&, const Vector<String>&, const Vector<String>&) +RefPtr<Widget> EmptyFrameLoaderClient::createJavaAppletWidget(const IntSize&, HTMLAppletElement&, const URL&, const Vector<String>&, const Vector<String>&) { - return 0; + return nullptr; } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -PassRefPtr<Widget> EmptyFrameLoaderClient::createMediaPlayerProxyPlugin(const IntSize&, HTMLMediaElement*, const URL&, const Vector<String>&, const Vector<String>&, const String&) +inline EmptyFrameNetworkingContext::EmptyFrameNetworkingContext() + : FrameNetworkingContext { nullptr } { - return 0; } -#endif -PassRefPtr<FrameNetworkingContext> EmptyFrameLoaderClient::createNetworkingContext() +Ref<FrameNetworkingContext> EmptyFrameLoaderClient::createNetworkingContext() { - return PassRefPtr<FrameNetworkingContext>(); + return EmptyFrameNetworkingContext::create(); } -void EmptyTextCheckerClient::requestCheckingOfString(PassRefPtr<TextCheckingRequest>) +void EmptyEditorClient::EmptyTextCheckerClient::requestCheckingOfString(TextCheckingRequest&, const VisibleSelection&) { } -void EmptyEditorClient::registerUndoStep(PassRefPtr<UndoStep>) +void EmptyEditorClient::registerUndoStep(UndoStep&) { } -void EmptyEditorClient::registerRedoStep(PassRefPtr<UndoStep>) +void EmptyEditorClient::registerRedoStep(UndoStep&) { } -#if ENABLE(CONTEXT_MENUS) -#if USE(CROSS_PLATFORM_CONTEXT_MENUS) -PassOwnPtr<ContextMenu> EmptyContextMenuClient::customizeMenu(PassOwnPtr<ContextMenu>) +RefPtr<StorageNamespace> EmptyStorageNamespaceProvider::createSessionStorageNamespace(Page&, unsigned) { - return nullptr; + return adoptRef(*new EmptyStorageNamespace); +} + +RefPtr<StorageNamespace> EmptyStorageNamespaceProvider::createLocalStorageNamespace(unsigned) +{ + return adoptRef(*new EmptyStorageNamespace); +} + +RefPtr<StorageNamespace> EmptyStorageNamespaceProvider::createEphemeralLocalStorageNamespace(Page&, unsigned) +{ + return adoptRef(*new EmptyStorageNamespace); } + +RefPtr<StorageNamespace> EmptyStorageNamespaceProvider::createTransientLocalStorageNamespace(SecurityOrigin&, unsigned) +{ + return adoptRef(*new EmptyStorageNamespace); +} + +void fillWithEmptyClients(PageConfiguration& pageConfiguration) +{ + static NeverDestroyed<EmptyChromeClient> dummyChromeClient; + pageConfiguration.chromeClient = &dummyChromeClient.get(); + +#if ENABLE(APPLE_PAY) + static NeverDestroyed<EmptyPaymentCoordinatorClient> dummyPaymentCoordinatorClient; + pageConfiguration.paymentCoordinatorClient = &dummyPaymentCoordinatorClient.get(); #endif + +#if ENABLE(CONTEXT_MENUS) + static NeverDestroyed<EmptyContextMenuClient> dummyContextMenuClient; + pageConfiguration.contextMenuClient = &dummyContextMenuClient.get(); #endif +#if ENABLE(DRAG_SUPPORT) + static NeverDestroyed<EmptyDragClient> dummyDragClient; + pageConfiguration.dragClient = &dummyDragClient.get(); +#endif + + static NeverDestroyed<EmptyInspectorClient> dummyInspectorClient; + pageConfiguration.inspectorClient = &dummyInspectorClient.get(); + + static NeverDestroyed<EmptyFrameLoaderClient> dummyFrameLoaderClient; + pageConfiguration.loaderClientForMainFrame = &dummyFrameLoaderClient.get(); + + static NeverDestroyed<EmptyProgressTrackerClient> dummyProgressTrackerClient; + pageConfiguration.progressTrackerClient = &dummyProgressTrackerClient.get(); + + pageConfiguration.backForwardClient = adoptRef(*new EmptyBackForwardClient); + pageConfiguration.diagnosticLoggingClient = std::make_unique<EmptyDiagnosticLoggingClient>(); + + pageConfiguration.applicationCacheStorage = ApplicationCacheStorage::create({ }, { }); + pageConfiguration.databaseProvider = adoptRef(*new EmptyDatabaseProvider); + pageConfiguration.pluginInfoProvider = adoptRef(*new EmptyPluginInfoProvider); + pageConfiguration.storageNamespaceProvider = adoptRef(*new EmptyStorageNamespaceProvider); + pageConfiguration.userContentProvider = adoptRef(*new EmptyUserContentProvider); + pageConfiguration.visitedLinkStore = adoptRef(*new EmptyVisitedLinkStore); +} + +UniqueRef<EditorClient> createEmptyEditorClient() +{ + return makeUniqueRef<EmptyEditorClient>(); +} + +DiagnosticLoggingClient& emptyDiagnosticLoggingClient() +{ + static NeverDestroyed<EmptyDiagnosticLoggingClient> client; + return client; +} + } diff --git a/Source/WebCore/loader/EmptyClients.h b/Source/WebCore/loader/EmptyClients.h index 199c9cc4a..e2aa60ed7 100644 --- a/Source/WebCore/loader/EmptyClients.h +++ b/Source/WebCore/loader/EmptyClients.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Eric Seidel (eric@webkit.org) - * Copyright (C) 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2012 Samsung Electronics. All rights reserved. * @@ -13,10 +13,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -26,617 +26,188 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EmptyClients_h -#define EmptyClients_h +#pragma once #include "ChromeClient.h" -#include "ContextMenuClient.h" -#include "DeviceMotionClient.h" -#include "DeviceOrientationClient.h" -#include "DragClient.h" -#include "EditorClient.h" -#include "TextCheckerClient.h" -#include "FloatRect.h" -#include "FocusDirection.h" -#include "FrameLoaderClient.h" -#include "InspectorClient.h" -#include "Page.h" -#include "ProgressTrackerClient.h" -#include "ResourceError.h" -#include <wtf/text/StringView.h> +#include <wtf/UniqueRef.h> -/* - This file holds empty Client stubs for use by WebCore. - Viewless element needs to create a dummy Page->Frame->FrameView tree for use in parsing or executing JavaScript. - This tree depends heavily on Clients (usually provided by WebKit classes). - - This file was first created for SVGImage as it had no way to access the current Page (nor should it, - since Images are not tied to a page). - See http://bugs.webkit.org/show_bug.cgi?id=5971 for the original discussion about this file. - - Ideally, whenever you change a Client class, you should add a stub here. - Brittle, yes. Unfortunate, yes. Hopefully temporary. -*/ +// Empty client classes for use by WebCore. +// +// First created for SVGImage as it had no way to access the current Page (nor should it, since Images are not tied to a page). +// See http://bugs.webkit.org/show_bug.cgi?id=5971 for the original discussion about this file. namespace WebCore { -class GraphicsContext3D; +class DiagnosticLoggingClient; +class EditorClient; +class PageConfiguration; class EmptyChromeClient : public ChromeClient { WTF_MAKE_FAST_ALLOCATED; -public: - virtual ~EmptyChromeClient() { } - virtual void chromeDestroyed() override { } - - virtual void setWindowRect(const FloatRect&) override { } - virtual FloatRect windowRect() override { return FloatRect(); } - - virtual FloatRect pageRect() override { return FloatRect(); } - - virtual void focus() override { } - virtual void unfocus() override { } - - virtual bool canTakeFocus(FocusDirection) override { return false; } - virtual void takeFocus(FocusDirection) override { } - - virtual void focusedElementChanged(Element*) override { } - virtual void focusedFrameChanged(Frame*) override { } - - virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) override { return 0; } - virtual void show() override { } - - virtual bool canRunModal() override { return false; } - virtual void runModal() override { } - - virtual void setToolbarsVisible(bool) override { } - virtual bool toolbarsVisible() override { return false; } - virtual void setStatusbarVisible(bool) override { } - virtual bool statusbarVisible() override { return false; } + void chromeDestroyed() override { } - virtual void setScrollbarsVisible(bool) override { } - virtual bool scrollbarsVisible() override { return false; } + void setWindowRect(const FloatRect&) final { } + FloatRect windowRect() final { return FloatRect(); } - virtual void setMenubarVisible(bool) override { } - virtual bool menubarVisible() override { return false; } + FloatRect pageRect() final { return FloatRect(); } - virtual void setResizable(bool) override { } + void focus() final { } + void unfocus() final { } - virtual void addMessageToConsole(MessageSource, MessageLevel, const String&, unsigned, unsigned, const String&) override { } + bool canTakeFocus(FocusDirection) final { return false; } + void takeFocus(FocusDirection) final { } - virtual bool canRunBeforeUnloadConfirmPanel() override { return false; } - virtual bool runBeforeUnloadConfirmPanel(const String&, Frame*) override { return true; } + void focusedElementChanged(Element*) final { } + void focusedFrameChanged(Frame*) final { } - virtual void closeWindowSoon() override { } + Page* createWindow(Frame&, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) final { return nullptr; } + void show() final { } - virtual void runJavaScriptAlert(Frame*, const String&) override { } - virtual bool runJavaScriptConfirm(Frame*, const String&) override { return false; } - virtual bool runJavaScriptPrompt(Frame*, const String&, const String&, String&) override { return false; } - virtual bool shouldInterruptJavaScript() override { return false; } + bool canRunModal() final { return false; } + void runModal() final { } - virtual bool selectItemWritingDirectionIsNatural() override { return false; } - virtual bool selectItemAlignmentFollowsMenuWritingDirection() override { return false; } - virtual bool hasOpenedPopup() const override { return false; } - virtual PassRefPtr<PopupMenu> createPopupMenu(PopupMenuClient*) const override; - virtual PassRefPtr<SearchPopupMenu> createSearchPopupMenu(PopupMenuClient*) const override; + void setToolbarsVisible(bool) final { } + bool toolbarsVisible() final { return false; } - virtual void setStatusbarText(const String&) override { } + void setStatusbarVisible(bool) final { } + bool statusbarVisible() final { return false; } - virtual KeyboardUIMode keyboardUIMode() override { return KeyboardAccessDefault; } + void setScrollbarsVisible(bool) final { } + bool scrollbarsVisible() final { return false; } - virtual IntRect windowResizerRect() const override { return IntRect(); } + void setMenubarVisible(bool) final { } + bool menubarVisible() final { return false; } - virtual void invalidateRootView(const IntRect&, bool) override { } - virtual void invalidateContentsAndRootView(const IntRect&, bool) override { } - virtual void invalidateContentsForSlowScroll(const IntRect&, bool) override { } - virtual void scroll(const IntSize&, const IntRect&, const IntRect&) override { } -#if USE(TILED_BACKING_STORE) - virtual void delegatedScrollRequested(const IntPoint&) { } -#endif -#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) - virtual void scheduleAnimation() { } -#endif + void setResizable(bool) final { } - virtual IntPoint screenToRootView(const IntPoint& p) const override { return p; } - virtual IntRect rootViewToScreen(const IntRect& r) const override { return r; } - virtual PlatformPageClient platformPageClient() const override { return 0; } - virtual void contentsSizeChanged(Frame*, const IntSize&) const override { } + void addMessageToConsole(MessageSource, MessageLevel, const String&, unsigned, unsigned, const String&) final { } - virtual void scrollbarsModeDidChange() const override { } - virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned) override { } + bool canRunBeforeUnloadConfirmPanel() final { return false; } + bool runBeforeUnloadConfirmPanel(const String&, Frame&) final { return true; } - virtual void setToolTip(const String&, TextDirection) override { } + void closeWindowSoon() final { } - virtual void print(Frame*) override { } + void runJavaScriptAlert(Frame&, const String&) final { } + bool runJavaScriptConfirm(Frame&, const String&) final { return false; } + bool runJavaScriptPrompt(Frame&, const String&, const String&, String&) final { return false; } -#if ENABLE(SQL_DATABASE) - virtual void exceededDatabaseQuota(Frame*, const String&, DatabaseDetails) override { } -#endif + bool selectItemWritingDirectionIsNatural() final { return false; } + bool selectItemAlignmentFollowsMenuWritingDirection() final { return false; } + bool hasOpenedPopup() const final { return false; } + RefPtr<PopupMenu> createPopupMenu(PopupMenuClient&) const final; + RefPtr<SearchPopupMenu> createSearchPopupMenu(PopupMenuClient&) const final; - virtual void reachedMaxAppCacheSize(int64_t) override { } - virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t) override { } + void setStatusbarText(const String&) final { } -#if ENABLE(DIRECTORY_UPLOAD) - virtual void enumerateChosenDirectory(FileChooser*) { } -#endif + KeyboardUIMode keyboardUIMode() final { return KeyboardAccessDefault; } -#if ENABLE(INPUT_TYPE_COLOR) - virtual PassOwnPtr<ColorChooser> createColorChooser(ColorChooserClient*, const Color&) override; -#endif + void invalidateRootView(const IntRect&) final { } + void invalidateContentsAndRootView(const IntRect&) override { } + void invalidateContentsForSlowScroll(const IntRect&) final { } + void scroll(const IntSize&, const IntRect&, const IntRect&) final { } -#if ENABLE(DATE_AND_TIME_INPUT_TYPES) && !PLATFORM(IOS) - virtual PassRefPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) override; +#if USE(COORDINATED_GRAPHICS) + void delegatedScrollRequested(const IntPoint&) final { } #endif - virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) override; - virtual void loadIconForFiles(const Vector<String>&, FileIconLoader*) override { } - - virtual void formStateDidChange(const Node*) override { } - - virtual void elementDidFocus(const Node*) override { } - virtual void elementDidBlur(const Node*) override { } - -#if !PLATFORM(IOS) - virtual void setCursor(const Cursor&) override { } - virtual void setCursorHiddenUntilMouseMoves(bool) override { } +#if !USE(REQUEST_ANIMATION_FRAME_TIMER) + void scheduleAnimation() final { } #endif - virtual void scrollRectIntoView(const IntRect&) const override { } - -#if USE(ACCELERATED_COMPOSITING) - virtual void attachRootGraphicsLayer(Frame*, GraphicsLayer*) override { } - virtual void setNeedsOneShotDrawingSynchronization() override { } - virtual void scheduleCompositingLayerFlush() override { } -#endif - -#if PLATFORM(WIN) - virtual void setLastSetCursorToCurrentCursor() override { } - virtual void AXStartFrameLoad() override { } - virtual void AXFinishFrameLoad() override { } -#endif + IntPoint screenToRootView(const IntPoint& p) const final { return p; } + IntRect rootViewToScreen(const IntRect& r) const final { return r; } #if PLATFORM(IOS) -#if ENABLE(TOUCH_EVENTS) - virtual void didPreventDefaultForEvent() override { } + IntPoint accessibilityScreenToRootView(const IntPoint& p) const final { return p; }; + IntRect rootViewToAccessibilityScreen(const IntRect& r) const final { return r; }; #endif - virtual void didReceiveMobileDocType() override { } - virtual void setNeedsScrollNotifications(Frame*, bool) override { } - virtual void observedContentChange(Frame*) override { } - virtual void clearContentChangeObservers(Frame*) override { } - virtual void notifyRevealedSelectionByScrollingFrame(Frame*) override { } - virtual void didLayout(LayoutType) override { } - virtual void didStartOverflowScroll() override { } - virtual void didEndOverflowScroll() override { } - - virtual void suppressFormNotifications() override { } - virtual void restoreFormNotifications() override { } - - virtual void addOrUpdateScrollingLayer(Node*, PlatformLayer*, PlatformLayer*, const IntSize&, bool, bool) override { } - virtual void removeScrollingLayer(Node*, PlatformLayer*, PlatformLayer*) override { } - - virtual void webAppOrientationsUpdated() override { }; -#endif // PLATFORM(IOS) -#if PLATFORM(IOS) - virtual bool isStopping() override { return false; } -#endif + PlatformPageClient platformPageClient() const final { return 0; } + void contentsSizeChanged(Frame&, const IntSize&) const final { } -#if ENABLE(TOUCH_EVENTS) - virtual void needTouchEvents(bool) override { } -#endif - - virtual void numWheelEventHandlersChanged(unsigned) override { } - - virtual bool isEmptyChromeClient() const override { return true; } + void scrollbarsModeDidChange() const final { } + void mouseDidMoveOverElement(const HitTestResult&, unsigned) final { } - virtual void didAssociateFormControls(const Vector<RefPtr<Element>>&) override { } - virtual bool shouldNotifyOnFormChanges() override { return false; } -}; + void setToolTip(const String&, TextDirection) final { } -// FIXME (bug 116233): Get rid of EmptyFrameLoaderClient. It is a travesty. + void print(Frame&) final { } -class EmptyFrameLoaderClient : public FrameLoaderClient { - WTF_MAKE_NONCOPYABLE(EmptyFrameLoaderClient); WTF_MAKE_FAST_ALLOCATED; -public: - EmptyFrameLoaderClient() { } - virtual ~EmptyFrameLoaderClient() { } - virtual void frameLoaderDestroyed() override { } + void exceededDatabaseQuota(Frame&, const String&, DatabaseDetails) final { } - virtual bool hasWebView() const override { return true; } // mainly for assertions + void reachedMaxAppCacheSize(int64_t) final { } + void reachedApplicationCacheOriginQuota(SecurityOrigin&, int64_t) final { } - virtual void makeRepresentation(DocumentLoader*) override { } - virtual void forceLayout() override { } -#if PLATFORM(IOS) - virtual void forceLayoutWithoutRecalculatingStyles() override { } +#if ENABLE(INPUT_TYPE_COLOR) + std::unique_ptr<ColorChooser> createColorChooser(ColorChooserClient&, const Color&) final; #endif - virtual void forceLayoutForNonHTML() override { } - - virtual void setCopiesOnScroll() override { } - - virtual void detachedFromParent2() override { } - virtual void detachedFromParent3() override { } - - virtual void convertMainResourceLoadToDownload(DocumentLoader*, const ResourceRequest&, const ResourceResponse&) override { } - virtual void assignIdentifierToInitialRequest(unsigned long, DocumentLoader*, const ResourceRequest&) override { } - virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long) override { return false; } - virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long, ResourceRequest&, const ResourceResponse&) override { } - virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) override { } - virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) override { } -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) - virtual bool canAuthenticateAgainstProtectionSpace(DocumentLoader*, unsigned long, const ProtectionSpace&) override { return false; } -#endif + void runOpenPanel(Frame&, FileChooser&) final; + void loadIconForFiles(const Vector<String>&, FileIconLoader&) final { } -#if PLATFORM(IOS) - virtual RetainPtr<CFDictionaryRef> connectionProperties(DocumentLoader*, unsigned long) override { return nullptr; } -#endif + void elementDidFocus(Element&) final { } + void elementDidBlur(Element&) final { } - virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long, const ResourceResponse&) override { } - virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long, int) override { } - virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long) override { } - virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long, const ResourceError&) override { } - virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int) override { return false; } - - virtual void dispatchDidHandleOnloadEvents() override { } - virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() override { } - virtual void dispatchDidCancelClientRedirect() override { } - virtual void dispatchWillPerformClientRedirect(const URL&, double, double) override { } - virtual void dispatchDidChangeLocationWithinPage() override { } - virtual void dispatchDidPushStateWithinPage() override { } - virtual void dispatchDidReplaceStateWithinPage() override { } - virtual void dispatchDidPopStateWithinPage() override { } - virtual void dispatchWillClose() override { } - virtual void dispatchDidReceiveIcon() override { } - virtual void dispatchDidStartProvisionalLoad() override { } - virtual void dispatchDidReceiveTitle(const StringWithDirection&) override { } - virtual void dispatchDidChangeIcons(IconType) override { } - virtual void dispatchDidCommitLoad() override { } - virtual void dispatchDidFailProvisionalLoad(const ResourceError&) override { } - virtual void dispatchDidFailLoad(const ResourceError&) override { } - virtual void dispatchDidFinishDocumentLoad() override { } - virtual void dispatchDidFinishLoad() override { } - virtual void dispatchDidLayout(LayoutMilestones) override { } - - virtual Frame* dispatchCreatePage(const NavigationAction&) override { return 0; } - virtual void dispatchShow() override { } - - virtual void dispatchDecidePolicyForResponse(const ResourceResponse&, const ResourceRequest&, FramePolicyFunction) override { } - virtual void dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String&, FramePolicyFunction) override; - virtual void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, FramePolicyFunction) override; - virtual void cancelPolicyCheck() override { } - - virtual void dispatchUnableToImplementPolicy(const ResourceError&) override { } - - virtual void dispatchWillSendSubmitEvent(PassRefPtr<FormState>) override; - virtual void dispatchWillSubmitForm(PassRefPtr<FormState>, FramePolicyFunction) override; - - virtual void revertToProvisionalState(DocumentLoader*) override { } - virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) override { } - - virtual void setMainFrameDocumentReady(bool) override { } - - virtual void startDownload(const ResourceRequest&, const String& suggestedName = String()) override { UNUSED_PARAM(suggestedName); } - - virtual void willChangeTitle(DocumentLoader*) override { } - virtual void didChangeTitle(DocumentLoader*) override { } - - virtual void committedLoad(DocumentLoader*, const char*, int) override { } - virtual void finishedLoading(DocumentLoader*) override { } - - virtual ResourceError cancelledError(const ResourceRequest&) override { ResourceError error("", 0, "", ""); error.setIsCancellation(true); return error; } - virtual ResourceError blockedError(const ResourceRequest&) override { return ResourceError("", 0, "", ""); } - virtual ResourceError cannotShowURLError(const ResourceRequest&) override { return ResourceError("", 0, "", ""); } - virtual ResourceError interruptedForPolicyChangeError(const ResourceRequest&) override { return ResourceError("", 0, "", ""); } - - virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) override { return ResourceError("", 0, "", ""); } - virtual ResourceError fileDoesNotExistError(const ResourceResponse&) override { return ResourceError("", 0, "", ""); } - virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) override { return ResourceError("", 0, "", ""); } - - virtual bool shouldFallBack(const ResourceError&) override { return false; } - - virtual bool canHandleRequest(const ResourceRequest&) const override { return false; } - virtual bool canShowMIMEType(const String&) const override { return false; } - virtual bool canShowMIMETypeAsHTML(const String&) const override { return false; } - virtual bool representationExistsForURLScheme(const String&) const override { return false; } - virtual String generatedMIMETypeForURLScheme(const String&) const override { return ""; } - - virtual void frameLoadCompleted() override { } - virtual void restoreViewState() override { } - virtual void provisionalLoadStarted() override { } - virtual void didFinishLoad() override { } - virtual void prepareForDataSourceReplacement() override { } - - virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&) override; - virtual void setTitle(const StringWithDirection&, const URL&) override { } - - virtual String userAgent(const URL&) override { return ""; } - - virtual void savePlatformDataToCachedFrame(CachedFrame*) override { } - virtual void transitionToCommittedFromCachedFrame(CachedFrame*) override { } -#if PLATFORM(IOS) - virtual void didRestoreFrameHierarchyForCachedFrame() override { } -#endif - virtual void transitionToCommittedForNewPage() override { } - - virtual void didSaveToPageCache() override { } - virtual void didRestoreFromPageCache() override { } - - virtual void dispatchDidBecomeFrameset(bool) override { } - - virtual void updateGlobalHistory() override { } - virtual void updateGlobalHistoryRedirectLinks() override { } - virtual bool shouldGoToHistoryItem(HistoryItem*) const override { return false; } - virtual void updateGlobalHistoryItemForPage() override { } - virtual void saveViewStateToItem(HistoryItem*) override { } - virtual bool canCachePage() const override { return false; } - virtual void didDisplayInsecureContent() override { } - virtual void didRunInsecureContent(SecurityOrigin*, const URL&) override { } - virtual void didDetectXSS(const URL&, bool) override { } - virtual PassRefPtr<Frame> createFrame(const URL&, const String&, HTMLFrameOwnerElement*, const String&, bool, int, int) override; - virtual PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const URL&, const Vector<String>&, const Vector<String>&, const String&, bool) override; - virtual void recreatePlugin(Widget*) override; - virtual PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const URL&, const Vector<String>&, const Vector<String>&) override; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - virtual PassRefPtr<Widget> createMediaPlayerProxyPlugin(const IntSize&, HTMLMediaElement*, const URL&, const Vector<String>&, const Vector<String>&, const String&) override; - virtual void hideMediaPlayerProxyPlugin(Widget*) override { } - virtual void showMediaPlayerProxyPlugin(Widget*) override { } +#if !PLATFORM(IOS) + void setCursor(const Cursor&) final { } + void setCursorHiddenUntilMouseMoves(bool) final { } #endif - virtual ObjectContentType objectContentType(const URL&, const String&, bool) override { return ObjectContentType(); } - virtual String overrideMediaType() const override { return String(); } - - virtual void redirectDataToPlugin(Widget*) override { } - virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld&) override { } + void scrollRectIntoView(const IntRect&) const final { } - virtual void registerForIconNotification(bool) override { } + void attachRootGraphicsLayer(Frame&, GraphicsLayer*) final { } + void attachViewOverlayGraphicsLayer(Frame&, GraphicsLayer*) final { } + void setNeedsOneShotDrawingSynchronization() final { } + void scheduleCompositingLayerFlush() final { } -#if PLATFORM(MAC) - virtual RemoteAXObjectRef accessibilityRemoteObject() override { return 0; } - virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long, NSCachedURLResponse* response) const override { return response; } -#endif -#if PLATFORM(WIN) && USE(CFNETWORK) - // FIXME: Windows should use willCacheResponse - <https://bugs.webkit.org/show_bug.cgi?id=57257>. - virtual bool shouldCacheResponse(DocumentLoader*, unsigned long, const ResourceResponse&, const unsigned char*, unsigned long long) override { return true; } +#if PLATFORM(WIN) + void setLastSetCursorToCurrentCursor() final { } + void AXStartFrameLoad() final { } + void AXFinishFrameLoad() final { } #endif - virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext() override; - - virtual bool isEmptyFrameLoaderClient() override { return true; } -}; - -class EmptyTextCheckerClient : public TextCheckerClient { -public: - virtual bool shouldEraseMarkersAfterChangeSelection(TextCheckingType) const override { return true; } - virtual void ignoreWordInSpellDocument(const String&) override { } - virtual void learnWord(const String&) override { } - virtual void checkSpellingOfString(const UChar*, int, int*, int*) override { } - virtual String getAutoCorrectSuggestionForMisspelledWord(const String&) override { return String(); } - virtual void checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*) override { } - -#if USE(UNIFIED_TEXT_CHECKING) - virtual Vector<TextCheckingResult> checkTextOfParagraph(StringView, TextCheckingTypeMask) override { return Vector<TextCheckingResult>(); } +#if ENABLE(IOS_TOUCH_EVENTS) + void didPreventDefaultForEvent() final { } #endif - virtual void getGuessesForWord(const String&, const String&, Vector<String>&) override { } - virtual void requestCheckingOfString(PassRefPtr<TextCheckingRequest>) override; -}; - -class EmptyEditorClient : public EditorClient { - WTF_MAKE_NONCOPYABLE(EmptyEditorClient); WTF_MAKE_FAST_ALLOCATED; -public: - EmptyEditorClient() { } - virtual ~EmptyEditorClient() { } - virtual void pageDestroyed() override { } - - virtual bool shouldDeleteRange(Range*) override { return false; } - virtual bool smartInsertDeleteEnabled() override { return false; } - virtual bool isSelectTrailingWhitespaceEnabled() override { return false; } - virtual bool isContinuousSpellCheckingEnabled() override { return false; } - virtual void toggleContinuousSpellChecking() override { } - virtual bool isGrammarCheckingEnabled() override { return false; } - virtual void toggleGrammarChecking() override { } - virtual int spellCheckerDocumentTag() override { return -1; } - - - virtual bool shouldBeginEditing(Range*) override { return false; } - virtual bool shouldEndEditing(Range*) override { return false; } - virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction) override { return false; } - virtual bool shouldInsertText(const String&, Range*, EditorInsertAction) override { return false; } - virtual bool shouldChangeSelectedRange(Range*, Range*, EAffinity, bool) override { return false; } - - virtual bool shouldApplyStyle(StyleProperties*, Range*) override { return false; } - virtual bool shouldMoveRangeAfterDelete(Range*, Range*) override { return false; } - - virtual void didBeginEditing() override { } - virtual void respondToChangedContents() override { } - virtual void respondToChangedSelection(Frame*) override { } - virtual void didEndEditing() override { } - virtual void willWriteSelectionToPasteboard(Range*) override { } - virtual void didWriteSelectionToPasteboard() override { } - virtual void getClientPasteboardDataForRange(Range*, Vector<String>&, Vector<RefPtr<SharedBuffer>>&) override { } - - virtual void registerUndoStep(PassRefPtr<UndoStep>) override; - virtual void registerRedoStep(PassRefPtr<UndoStep>) override; - virtual void clearUndoRedoOperations() override { } - - virtual bool canCopyCut(Frame*, bool defaultValue) const override { return defaultValue; } - virtual bool canPaste(Frame*, bool defaultValue) const override { return defaultValue; } - virtual bool canUndo() const override { return false; } - virtual bool canRedo() const override { return false; } - - virtual void undo() override { } - virtual void redo() override { } - - virtual void handleKeyboardEvent(KeyboardEvent*) override { } - virtual void handleInputMethodKeydown(KeyboardEvent*) override { } - - virtual void textFieldDidBeginEditing(Element*) override { } - virtual void textFieldDidEndEditing(Element*) override { } - virtual void textDidChangeInTextField(Element*) override { } - virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) override { return false; } - virtual void textWillBeDeletedInTextField(Element*) override { } - virtual void textDidChangeInTextArea(Element*) override { } - #if PLATFORM(IOS) - virtual void suppressSelectionNotifications() override { } - virtual void restoreSelectionNotifications() override { } - virtual void startDelayingAndCoalescingContentChangeNotifications() override { } - virtual void stopDelayingAndCoalescingContentChangeNotifications() override { } - virtual void writeDataToPasteboard(NSDictionary*) override { } - virtual NSArray* supportedPasteboardTypesForCurrentSelection() override { return nullptr; } - virtual NSArray* readDataFromPasteboard(NSString*, int) override { return nullptr; } - virtual bool hasRichlyEditableSelection() override { return false; } - virtual int getPasteboardItemsCount() override { return 0; } - virtual DocumentFragment* documentFragmentFromDelegate(int) override { return nullptr; } - virtual bool performsTwoStepPaste(DocumentFragment*) override { return false; } - virtual int pasteboardChangeCount() override { return 0; } -#endif - -#if PLATFORM(MAC) - virtual NSString* userVisibleString(NSURL*) override { return 0; } - virtual DocumentFragment* documentFragmentFromAttributedString(NSAttributedString*, Vector<RefPtr<ArchiveResource>>&) override { return 0; }; - virtual void setInsertionPasteboard(const String&) override { }; - virtual NSURL *canonicalizeURL(NSURL*) override { return 0; } - virtual NSURL *canonicalizeURLString(NSString*) override { return 0; } -#endif - -#if USE(APPKIT) - virtual void uppercaseWord() override { } - virtual void lowercaseWord() override { } - virtual void capitalizeWord() override { } -#endif - -#if USE(AUTOMATIC_TEXT_REPLACEMENT) - virtual void showSubstitutionsPanel(bool) override { } - virtual bool substitutionsPanelIsShowing() override { return false; } - virtual void toggleSmartInsertDelete() override { } - virtual bool isAutomaticQuoteSubstitutionEnabled() override { return false; } - virtual void toggleAutomaticQuoteSubstitution() override { } - virtual bool isAutomaticLinkDetectionEnabled() override { return false; } - virtual void toggleAutomaticLinkDetection() override { } - virtual bool isAutomaticDashSubstitutionEnabled() override { return false; } - virtual void toggleAutomaticDashSubstitution() override { } - virtual bool isAutomaticTextReplacementEnabled() override { return false; } - virtual void toggleAutomaticTextReplacement() override { } - virtual bool isAutomaticSpellingCorrectionEnabled() override { return false; } - virtual void toggleAutomaticSpellingCorrection() override { } -#endif - -#if ENABLE(DELETION_UI) - virtual bool shouldShowDeleteInterface(HTMLElement*) override { return false; } -#endif - -#if PLATFORM(GTK) - virtual bool shouldShowUnicodeMenu() override { return false; } -#endif - virtual TextCheckerClient* textChecker() override { return &m_textCheckerClient; } - - virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) override { } - virtual void updateSpellingUIWithMisspelledWord(const String&) override { } - virtual void showSpellingUI(bool) override { } - virtual bool spellingUIIsShowing() override { return false; } - - virtual void willSetInputMethodState() override { } - virtual void setInputMethodState(bool) override { } - -private: - EmptyTextCheckerClient m_textCheckerClient; -}; - -#if ENABLE(CONTEXT_MENUS) -class EmptyContextMenuClient : public ContextMenuClient { - WTF_MAKE_NONCOPYABLE(EmptyContextMenuClient); WTF_MAKE_FAST_ALLOCATED; -public: - EmptyContextMenuClient() { } - virtual ~EmptyContextMenuClient() { } - virtual void contextMenuDestroyed() override { } - -#if USE(CROSS_PLATFORM_CONTEXT_MENUS) - virtual PassOwnPtr<ContextMenu> customizeMenu(PassOwnPtr<ContextMenu>) override; -#else - virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) override { return 0; } -#endif - virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) override { } - - virtual void downloadURL(const URL&) override { } - virtual void searchWithGoogle(const Frame*) override { } - virtual void lookUpInDictionary(Frame*) override { } - virtual bool isSpeaking() override { return false; } - virtual void speak(const String&) override { } - virtual void stopSpeaking() override { } + void didReceiveMobileDocType(bool) final { } + void setNeedsScrollNotifications(Frame&, bool) final { } + void observedContentChange(Frame&) final { } + void clearContentChangeObservers(Frame&) final { } + void notifyRevealedSelectionByScrollingFrame(Frame&) final { } + void didLayout(LayoutType) final { } + void didStartOverflowScroll() final { } + void didEndOverflowScroll() final { } + + void suppressFormNotifications() final { } + void restoreFormNotifications() final { } + + void addOrUpdateScrollingLayer(Node*, PlatformLayer*, PlatformLayer*, const IntSize&, bool, bool) final { } + void removeScrollingLayer(Node*, PlatformLayer*, PlatformLayer*) final { } + + void webAppOrientationsUpdated() final { }; + void showPlaybackTargetPicker(bool) final { }; +#endif // PLATFORM(IOS) -#if PLATFORM(MAC) - virtual void searchWithSpotlight() override { } +#if ENABLE(ORIENTATION_EVENTS) + int deviceOrientation() const final { return 0; } #endif -#if USE(ACCESSIBILITY_CONTEXT_MENUS) - virtual void showContextMenu() override { } +#if PLATFORM(IOS) + bool isStopping() final { return false; } #endif -}; -#endif // ENABLE(CONTEXT_MENUS) - -#if ENABLE(DRAG_SUPPORT) -class EmptyDragClient : public DragClient { - WTF_MAKE_NONCOPYABLE(EmptyDragClient); WTF_MAKE_FAST_ALLOCATED; -public: - EmptyDragClient() { } - virtual ~EmptyDragClient() {} - virtual void willPerformDragDestinationAction(DragDestinationAction, DragData&) override { } - virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard&) override { } - virtual DragDestinationAction actionMaskForDrag(DragData&) override { return DragDestinationActionNone; } - virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&) override { return DragSourceActionNone; } - virtual void startDrag(DragImageRef, const IntPoint&, const IntPoint&, Clipboard&, Frame&, bool) override { } - virtual void dragControllerDestroyed() override { } -}; -#endif // ENABLE(DRAG_SUPPORT) - -class EmptyInspectorClient : public InspectorClient { - WTF_MAKE_NONCOPYABLE(EmptyInspectorClient); WTF_MAKE_FAST_ALLOCATED; -public: - EmptyInspectorClient() { } - virtual ~EmptyInspectorClient() { } - virtual void inspectorDestroyed() override { } + void wheelEventHandlersChanged(bool) final { } - virtual InspectorFrontendChannel* openInspectorFrontend(InspectorController*) override { return 0; } - virtual void closeInspectorFrontend() override { } - virtual void bringFrontendToFront() override { } + bool isEmptyChromeClient() const final { return true; } - virtual void highlight() override { } - virtual void hideHighlight() override { } + void didAssociateFormControls(const Vector<RefPtr<Element>>&) final { } + bool shouldNotifyOnFormChanges() final { return false; } }; -class EmptyDeviceClient : public DeviceClient { -public: - virtual void startUpdating() override { } - virtual void stopUpdating() override { } -}; - -class EmptyDeviceMotionClient : public DeviceMotionClient { -public: - virtual void setController(DeviceMotionController*) override { } - virtual DeviceMotionData* lastMotion() const override { return 0; } - virtual void deviceMotionControllerDestroyed() override { } -}; - -class EmptyDeviceOrientationClient : public DeviceOrientationClient { -public: - virtual void setController(DeviceOrientationController*) override { } - virtual DeviceOrientationData* lastOrientation() const override { return 0; } - virtual void deviceOrientationControllerDestroyed() override { } -}; - -class EmptyProgressTrackerClient : public ProgressTrackerClient { - virtual void willChangeEstimatedProgress() override { } - virtual void didChangeEstimatedProgress() override { } - - virtual void progressStarted(Frame&) override { } - virtual void progressEstimateChanged(Frame&) override { } - virtual void progressFinished(Frame&) override { } -}; - -void fillWithEmptyClients(Page::PageClients&); +void fillWithEmptyClients(PageConfiguration&); +UniqueRef<EditorClient> createEmptyEditorClient(); +DiagnosticLoggingClient& emptyDiagnosticLoggingClient(); } - -#endif // EmptyClients_h diff --git a/Source/WebCore/loader/FTPDirectoryParser.cpp b/Source/WebCore/loader/FTPDirectoryParser.cpp index 9a5b6114b..c9326c72e 100644 --- a/Source/WebCore/loader/FTPDirectoryParser.cpp +++ b/Source/WebCore/loader/FTPDirectoryParser.cpp @@ -505,7 +505,7 @@ FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& res * So its rounded up to the next block, so what, its better * than not showing the size at all. */ - uint64_t size = strtoul(tokens[1], NULL, 10) * 512; + uint64_t size = strtoull(tokens[1], 0, 10) * 512; result.fileSize = String::number(size); } @@ -1152,7 +1152,7 @@ FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& res if (!state.now) { - time_t now = time(NULL); + time_t now = time(nullptr); state.now = now * 1000000.0; // FIXME: This code has the year 2038 bug @@ -1617,7 +1617,7 @@ FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& res result.modifiedTime.tm_min = atoi(p+3); if (!state.now) { - time_t now = time(NULL); + time_t now = time(nullptr); state.now = now * 1000000.0; // FIXME: This code has the year 2038 bug diff --git a/Source/WebCore/loader/FTPDirectoryParser.h b/Source/WebCore/loader/FTPDirectoryParser.h index 642b3c3b9..325b37a6f 100644 --- a/Source/WebCore/loader/FTPDirectoryParser.h +++ b/Source/WebCore/loader/FTPDirectoryParser.h @@ -68,8 +68,7 @@ // This was originally Mozilla code, titled ParseFTPList.h // Original version of this file can currently be found at: http://mxr.mozilla.org/mozilla1.8/source/netwerk/streamconv/converters/ParseFTPList.h -#ifndef FTPDirectoryParser_h -#define FTPDirectoryParser_h +#pragma once #include <wtf/text/WTFString.h> @@ -127,9 +126,9 @@ struct ListResult { valid = false; type = FTPJunkEntry; - filename = 0; + filename = nullptr; filenameLength = 0; - linkname = 0; + linkname = nullptr; linknameLength = 0; fileSize.truncate(0); caseSensitive = false; @@ -153,5 +152,3 @@ struct ListResult FTPEntryType parseOneFTPLine(const char* inputLine, ListState&, ListResult&); } // namespace WebCore - -#endif // FTPDirectoryParser_h diff --git a/Source/WebCore/loader/FetchOptions.h b/Source/WebCore/loader/FetchOptions.h new file mode 100644 index 000000000..b733461c6 --- /dev/null +++ b/Source/WebCore/loader/FetchOptions.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be 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. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "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 CANON INC. AND ITS 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. + */ + +#pragma once + +namespace WebCore { + +struct FetchOptions { + enum class Type { EmptyString, Audio, Font, Image, Script, Style, Track, Video }; + Type type { Type::EmptyString }; + + enum class Destination { EmptyString, Document, Sharedworker, Subresource, Unknown, Worker }; + Destination destination { Destination::EmptyString }; + + enum class Mode { Navigate, SameOrigin, NoCors, Cors }; + Mode mode { Mode::NoCors }; + + enum class Credentials { Omit, SameOrigin, Include }; + Credentials credentials { Credentials::Omit }; + + enum class Cache { Default, NoStore, Reload, NoCache, ForceCache, OnlyIfCached }; + Cache cache { Cache::Default }; + + enum class Redirect { Follow, Error, Manual }; + Redirect redirect { Redirect::Follow }; + + enum class ReferrerPolicy { EmptyString, NoReferrer, NoReferrerWhenDowngrade, Origin, OriginWhenCrossOrigin, UnsafeUrl }; + ReferrerPolicy referrerPolicy { ReferrerPolicy::EmptyString }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/FormState.cpp b/Source/WebCore/loader/FormState.cpp index e214aaa7b..5e896e7f8 100644 --- a/Source/WebCore/loader/FormState.cpp +++ b/Source/WebCore/loader/FormState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -34,17 +34,17 @@ namespace WebCore { -inline FormState::FormState(PassRefPtr<HTMLFormElement> form, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Document> sourceDocument, FormSubmissionTrigger formSubmissionTrigger) +inline FormState::FormState(HTMLFormElement& form, StringPairVector&& textFieldValues, Document& sourceDocument, FormSubmissionTrigger formSubmissionTrigger) : m_form(form) + , m_textFieldValues(WTFMove(textFieldValues)) , m_sourceDocument(sourceDocument) , m_formSubmissionTrigger(formSubmissionTrigger) { - m_textFieldValues.swap(textFieldValuesToAdopt); } -PassRefPtr<FormState> FormState::create(PassRefPtr<HTMLFormElement> form, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Document> sourceDocument, FormSubmissionTrigger formSubmissionTrigger) +Ref<FormState> FormState::create(HTMLFormElement& form, StringPairVector&& textFieldValues, Document& sourceDocument, FormSubmissionTrigger formSubmissionTrigger) { - return adoptRef(new FormState(form, textFieldValuesToAdopt, sourceDocument, formSubmissionTrigger)); + return adoptRef(*new FormState(form, WTFMove(textFieldValues), sourceDocument, formSubmissionTrigger)); } } diff --git a/Source/WebCore/loader/FormState.h b/Source/WebCore/loader/FormState.h index 6f55943fe..2dedd09be 100644 --- a/Source/WebCore/loader/FormState.h +++ b/Source/WebCore/loader/FormState.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -26,42 +26,35 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FormState_h -#define FormState_h +#pragma once -#include <wtf/RefCounted.h> #include <wtf/text/WTFString.h> namespace WebCore { - class Document; - class HTMLFormElement; +class Document; +class HTMLFormElement; - enum FormSubmissionTrigger { - SubmittedByJavaScript, - NotSubmittedByJavaScript - }; - - typedef Vector<std::pair<String, String>> StringPairVector; +enum FormSubmissionTrigger { SubmittedByJavaScript, NotSubmittedByJavaScript }; - class FormState : public RefCounted<FormState> { - public: - static PassRefPtr<FormState> create(PassRefPtr<HTMLFormElement>, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Document>, FormSubmissionTrigger); +using StringPairVector = Vector<std::pair<String, String>>; - HTMLFormElement* form() const { return m_form.get(); } - const StringPairVector& textFieldValues() const { return m_textFieldValues; } - Document* sourceDocument() const { return m_sourceDocument.get(); } - FormSubmissionTrigger formSubmissionTrigger() const { return m_formSubmissionTrigger; } +class FormState : public RefCounted<FormState> { +public: + static Ref<FormState> create(HTMLFormElement&, StringPairVector&& textFieldValues, Document&, FormSubmissionTrigger); - private: - FormState(PassRefPtr<HTMLFormElement>, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Document>, FormSubmissionTrigger); + HTMLFormElement& form() const { return m_form; } + const StringPairVector& textFieldValues() const { return m_textFieldValues; } + Document& sourceDocument() const { return m_sourceDocument; } + FormSubmissionTrigger formSubmissionTrigger() const { return m_formSubmissionTrigger; } - RefPtr<HTMLFormElement> m_form; - StringPairVector m_textFieldValues; - RefPtr<Document> m_sourceDocument; - FormSubmissionTrigger m_formSubmissionTrigger; - }; +private: + FormState(HTMLFormElement&, StringPairVector&& textFieldValues, Document&, FormSubmissionTrigger); -} + Ref<HTMLFormElement> m_form; + StringPairVector m_textFieldValues; + Ref<Document> m_sourceDocument; + FormSubmissionTrigger m_formSubmissionTrigger; +}; -#endif // FormState_h +} // namespace WebCore diff --git a/Source/WebCore/loader/FormSubmission.cpp b/Source/WebCore/loader/FormSubmission.cpp index 4d1a6e85b..1d742ea33 100644 --- a/Source/WebCore/loader/FormSubmission.cpp +++ b/Source/WebCore/loader/FormSubmission.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -31,6 +32,7 @@ #include "config.h" #include "FormSubmission.h" +#include "ContentSecurityPolicy.h" #include "DOMFormData.h" #include "Document.h" #include "Event.h" @@ -45,9 +47,9 @@ #include "HTMLInputElement.h" #include "HTMLNames.h" #include "HTMLParserIdioms.h" +#include "NoEventDispatchAssertion.h" #include "TextEncoding.h" #include <wtf/CurrentTime.h> -#include <wtf/RandomNumber.h> namespace WebCore { @@ -65,7 +67,7 @@ static void appendMailtoPostFormDataToURL(URL& url, const FormData& data, const { String body = data.flattenToString(); - if (equalIgnoringCase(encodingType, "text/plain")) { + if (equalLettersIgnoringASCIICase(encodingType, "text/plain")) { // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20. body = decodeURLEscapeSequences(body.replaceWithLiteral('&', "\r\n").replace('+', ' ') + "\r\n"); } @@ -76,10 +78,10 @@ static void appendMailtoPostFormDataToURL(URL& url, const FormData& data, const body = String(bodyData.data(), bodyData.size()).replaceWithLiteral('+', "%20"); String query = url.query(); - if (!query.isEmpty()) - query.append('&'); - query.append(body); - url.setQuery(query); + if (query.isEmpty()) + url.setQuery(body); + else + url.setQuery(query + '&' + body); } void FormSubmission::Attributes::parseAction(const String& action) @@ -90,11 +92,11 @@ void FormSubmission::Attributes::parseAction(const String& action) String FormSubmission::Attributes::parseEncodingType(const String& type) { - if (equalIgnoringCase(type, "multipart/form-data")) - return "multipart/form-data"; - if (equalIgnoringCase(type, "text/plain")) - return "text/plain"; - return "application/x-www-form-urlencoded"; + if (equalLettersIgnoringASCIICase(type, "multipart/form-data")) + return ASCIILiteral("multipart/form-data"); + if (equalLettersIgnoringASCIICase(type, "text/plain")) + return ASCIILiteral("text/plain"); + return ASCIILiteral("application/x-www-form-urlencoded"); } void FormSubmission::Attributes::updateEncodingType(const String& type) @@ -105,7 +107,7 @@ void FormSubmission::Attributes::updateEncodingType(const String& type) FormSubmission::Method FormSubmission::Attributes::parseMethodType(const String& type) { - return equalIgnoringCase(type, "post") ? FormSubmission::PostMethod : FormSubmission::GetMethod; + return equalLettersIgnoringASCIICase(type, "post") ? FormSubmission::Method::Post : FormSubmission::Method::Get; } void FormSubmission::Attributes::updateMethodType(const String& type) @@ -113,65 +115,70 @@ void FormSubmission::Attributes::updateMethodType(const String& type) m_method = parseMethodType(type); } -void FormSubmission::Attributes::copyFrom(const Attributes& other) -{ - m_method = other.m_method; - m_isMultiPartForm = other.m_isMultiPartForm; - - m_action = other.m_action; - m_target = other.m_target; - m_encodingType = other.m_encodingType; - m_acceptCharset = other.m_acceptCharset; -} - -inline FormSubmission::FormSubmission(Method method, const URL& action, const String& target, const String& contentType, PassRefPtr<FormState> state, PassRefPtr<FormData> data, const String& boundary, bool lockHistory, PassRefPtr<Event> event) +inline FormSubmission::FormSubmission(Method method, const URL& action, const String& target, const String& contentType, Ref<FormState>&& state, Ref<FormData>&& data, const String& boundary, LockHistory lockHistory, Event* event) : m_method(method) , m_action(action) , m_target(target) , m_contentType(contentType) - , m_formState(state) - , m_formData(data) + , m_formState(WTFMove(state)) + , m_formData(WTFMove(data)) , m_boundary(boundary) , m_lockHistory(lockHistory) , m_event(event) { } -PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const Attributes& attributes, PassRefPtr<Event> event, bool lockHistory, FormSubmissionTrigger trigger) +static TextEncoding encodingFromAcceptCharset(const String& acceptCharset, Document& document) { - ASSERT(form); + String normalizedAcceptCharset = acceptCharset; + normalizedAcceptCharset.replace(',', ' '); + + Vector<String> charsets; + normalizedAcceptCharset.split(' ', charsets); + + for (auto& charset : charsets) { + TextEncoding encoding(charset); + if (encoding.isValid()) + return encoding; + } - HTMLFormControlElement* submitButton = 0; + return document.textEncoding(); +} + +Ref<FormSubmission> FormSubmission::create(HTMLFormElement& form, const Attributes& attributes, Event* event, LockHistory lockHistory, FormSubmissionTrigger trigger) +{ + HTMLFormControlElement* submitButton = nullptr; if (event && event->target()) { for (Node* node = event->target()->toNode(); node; node = node->parentNode()) { - if (node->isElementNode() && toElement(node)->isFormControlElement()) { - submitButton = toHTMLFormControlElement(node); + if (is<HTMLFormControlElement>(*node)) { + submitButton = downcast<HTMLFormControlElement>(node); break; } } } - FormSubmission::Attributes copiedAttributes; - copiedAttributes.copyFrom(attributes); + auto copiedAttributes = attributes; if (submitButton) { - String attributeValue; - if (!(attributeValue = submitButton->getAttribute(formactionAttr)).isNull()) + AtomicString attributeValue; + if (!(attributeValue = submitButton->attributeWithoutSynchronization(formactionAttr)).isNull()) copiedAttributes.parseAction(attributeValue); - if (!(attributeValue = submitButton->getAttribute(formenctypeAttr)).isNull()) + if (!(attributeValue = submitButton->attributeWithoutSynchronization(formenctypeAttr)).isNull()) copiedAttributes.updateEncodingType(attributeValue); - if (!(attributeValue = submitButton->getAttribute(formmethodAttr)).isNull()) + if (!(attributeValue = submitButton->attributeWithoutSynchronization(formmethodAttr)).isNull()) copiedAttributes.updateMethodType(attributeValue); - if (!(attributeValue = submitButton->getAttribute(formtargetAttr)).isNull()) + if (!(attributeValue = submitButton->attributeWithoutSynchronization(formtargetAttr)).isNull()) copiedAttributes.setTarget(attributeValue); } - Document& document = form->document(); - URL actionURL = document.completeURL(copiedAttributes.action().isEmpty() ? document.url().string() : copiedAttributes.action()); + auto& document = form.document(); + auto actionURL = document.completeURL(copiedAttributes.action().isEmpty() ? document.url().string() : copiedAttributes.action()); bool isMailtoForm = actionURL.protocolIs("mailto"); bool isMultiPartForm = false; - String encodingType = copiedAttributes.encodingType(); + auto encodingType = copiedAttributes.encodingType(); + + document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(actionURL, ContentSecurityPolicy::InsecureRequestType::FormSubmission); - if (copiedAttributes.method() == PostMethod) { + if (copiedAttributes.method() == Method::Post) { isMultiPartForm = copiedAttributes.isMultiPartForm(); if (isMultiPartForm && isMailtoForm) { encodingType = "application/x-www-form-urlencoded"; @@ -179,24 +186,27 @@ PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const A } } - TextEncoding dataEncoding = isMailtoForm ? UTF8Encoding() : FormDataBuilder::encodingFromAcceptCharset(copiedAttributes.acceptCharset(), document); - RefPtr<DOMFormData> domFormData = DOMFormData::create(dataEncoding.encodingForFormSubmission()); - Vector<std::pair<String, String>> formValues; + auto dataEncoding = isMailtoForm ? UTF8Encoding() : encodingFromAcceptCharset(copiedAttributes.acceptCharset(), document); + auto domFormData = DOMFormData::create(dataEncoding.encodingForFormSubmission()); + StringPairVector formValues; bool containsPasswordData = false; - for (unsigned i = 0; i < form->associatedElements().size(); ++i) { - FormAssociatedElement& control = *form->associatedElements()[i]; - HTMLElement& element = control.asHTMLElement(); - if (!element.isDisabledFormControl()) - control.appendFormData(*domFormData, isMultiPartForm); - if (isHTMLInputElement(element)) { - HTMLInputElement& input = toHTMLInputElement(element); - if (input.isTextField()) { - formValues.append(std::pair<String, String>(input.name().string(), input.value())); - input.addSearchResult(); + { + NoEventDispatchAssertion noEventDispatchAssertion; + + for (auto& control : form.associatedElements()) { + auto& element = control->asHTMLElement(); + if (!element.isDisabledFormControl()) + control->appendFormData(domFormData, isMultiPartForm); + if (is<HTMLInputElement>(element)) { + auto& input = downcast<HTMLInputElement>(element); + if (input.isTextField()) { + formValues.append({ input.name().string(), input.value() }); + input.addSearchResult(); + } + if (input.isPasswordField() && !input.value().isEmpty()) + containsPasswordData = true; } - if (input.isPasswordField() && !input.value().isEmpty()) - containsPasswordData = true; } } @@ -204,11 +214,11 @@ PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const A String boundary; if (isMultiPartForm) { - formData = FormData::createMultiPart(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding(), &document); + formData = FormData::createMultiPart(domFormData, domFormData->encoding(), &document); boundary = formData->boundary().data(); } else { - formData = FormData::create(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding(), attributes.method() == GetMethod ? FormData::FormURLEncoded : FormData::parseEncodingType(encodingType)); - if (copiedAttributes.method() == PostMethod && isMailtoForm) { + formData = FormData::create(domFormData, domFormData->encoding(), attributes.method() == Method::Get ? FormData::FormURLEncoded : FormData::parseEncodingType(encodingType)); + if (copiedAttributes.method() == Method::Post && isMailtoForm) { // Convert the form data into a string that we put into the URL. appendMailtoPostFormDataToURL(actionURL, *formData, encodingType); formData = FormData::create(); @@ -217,14 +227,17 @@ PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const A formData->setIdentifier(generateFormDataIdentifier()); formData->setContainsPasswordData(containsPasswordData); + String targetOrBaseTarget = copiedAttributes.target().isEmpty() ? document.baseTarget() : copiedAttributes.target(); - RefPtr<FormState> formState = FormState::create(form, formValues, &document, trigger); - return adoptRef(new FormSubmission(copiedAttributes.method(), actionURL, targetOrBaseTarget, encodingType, formState.release(), formData.release(), boundary, lockHistory, event)); + + auto formState = FormState::create(form, WTFMove(formValues), document, trigger); + + return adoptRef(*new FormSubmission(copiedAttributes.method(), actionURL, targetOrBaseTarget, encodingType, WTFMove(formState), formData.releaseNonNull(), boundary, lockHistory, event)); } URL FormSubmission::requestURL() const { - if (m_method == FormSubmission::PostMethod) + if (m_method == Method::Post) return m_action; URL requestURL(m_action); @@ -240,9 +253,9 @@ void FormSubmission::populateFrameLoadRequest(FrameLoadRequest& frameRequest) if (!m_referrer.isEmpty()) frameRequest.resourceRequest().setHTTPReferrer(m_referrer); - if (m_method == FormSubmission::PostMethod) { + if (m_method == Method::Post) { frameRequest.resourceRequest().setHTTPMethod("POST"); - frameRequest.resourceRequest().setHTTPBody(m_formData); + frameRequest.resourceRequest().setHTTPBody(m_formData.copyRef()); // construct some user headers if necessary if (m_boundary.isEmpty()) @@ -253,6 +266,7 @@ void FormSubmission::populateFrameLoadRequest(FrameLoadRequest& frameRequest) frameRequest.resourceRequest().setURL(requestURL()); FrameLoader::addHTTPOriginIfNeeded(frameRequest.resourceRequest(), m_origin); + FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(frameRequest.resourceRequest()); } } diff --git a/Source/WebCore/loader/FormSubmission.h b/Source/WebCore/loader/FormSubmission.h index c2f83d53f..252412c0c 100644 --- a/Source/WebCore/loader/FormSubmission.h +++ b/Source/WebCore/loader/FormSubmission.h @@ -28,39 +28,29 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FormSubmission_h -#define FormSubmission_h +#pragma once #include "FormState.h" +#include "FrameLoaderTypes.h" #include "URL.h" namespace WebCore { -class Document; class Event; class FormData; + struct FrameLoadRequest; -class HTMLFormElement; -class TextEncoding; class FormSubmission : public RefCounted<FormSubmission> { public: - enum Method { GetMethod, PostMethod }; + enum class Method { Get, Post }; class Attributes { - WTF_MAKE_NONCOPYABLE(Attributes); public: - Attributes() - : m_method(GetMethod) - , m_isMultiPartForm(false) - , m_encodingType("application/x-www-form-urlencoded") - { - } - Method method() const { return m_method; } static Method parseMethodType(const String&); void updateMethodType(const String&); - static String methodString(Method method) { return method == PostMethod ? "post" : "get"; } + static ASCIILiteral methodString(Method method) { return ASCIILiteral { method == Method::Post ? "post" : "get" }; } const String& action() const { return m_action; } void parseAction(const String&); @@ -76,57 +66,51 @@ public: const String& acceptCharset() const { return m_acceptCharset; } void setAcceptCharset(const String& value) { m_acceptCharset = value; } - void copyFrom(const Attributes&); - private: - Method m_method; - bool m_isMultiPartForm; - + Method m_method { Method::Get }; + bool m_isMultiPartForm { false }; String m_action; String m_target; - String m_encodingType; + String m_encodingType { ASCIILiteral { "application/x-www-form-urlencoded" } }; String m_acceptCharset; }; - static PassRefPtr<FormSubmission> create(HTMLFormElement*, const Attributes&, PassRefPtr<Event> event, bool lockHistory, FormSubmissionTrigger); + static Ref<FormSubmission> create(HTMLFormElement&, const Attributes&, Event*, LockHistory, FormSubmissionTrigger); void populateFrameLoadRequest(FrameLoadRequest&); - URL requestURL() const; Method method() const { return m_method; } const URL& action() const { return m_action; } const String& target() const { return m_target; } - void clearTarget() { m_target = String(); } const String& contentType() const { return m_contentType; } - FormState* state() const { return m_formState.get(); } - FormData* data() const { return m_formData.get(); } + FormState& state() const { return m_formState; } + FormData& data() const { return m_formData; } const String boundary() const { return m_boundary; } - bool lockHistory() const { return m_lockHistory; } + LockHistory lockHistory() const { return m_lockHistory; } Event* event() const { return m_event.get(); } - const String& referrer() const { return m_referrer; } - void setReferrer(const String& referrer) { m_referrer = referrer; } const String& origin() const { return m_origin; } + + void clearTarget() { m_target = { }; } + void setReferrer(const String& referrer) { m_referrer = referrer; } void setOrigin(const String& origin) { m_origin = origin; } private: - FormSubmission(Method, const URL& action, const String& target, const String& contentType, PassRefPtr<FormState>, PassRefPtr<FormData>, const String& boundary, bool lockHistory, PassRefPtr<Event>); + FormSubmission(Method, const URL& action, const String& target, const String& contentType, Ref<FormState>&&, Ref<FormData>&&, const String& boundary, LockHistory, Event*); // FIXME: Hold an instance of Attributes instead of individual members. Method m_method; URL m_action; String m_target; String m_contentType; - RefPtr<FormState> m_formState; - RefPtr<FormData> m_formData; + Ref<FormState> m_formState; + Ref<FormData> m_formData; String m_boundary; - bool m_lockHistory; + LockHistory m_lockHistory; RefPtr<Event> m_event; String m_referrer; String m_origin; }; -} - -#endif // FormSubmission_h +} // namespace WebCore diff --git a/Source/WebCore/loader/FrameLoadRequest.cpp b/Source/WebCore/loader/FrameLoadRequest.cpp index 014678823..8a1e7514c 100644 --- a/Source/WebCore/loader/FrameLoadRequest.cpp +++ b/Source/WebCore/loader/FrameLoadRequest.cpp @@ -36,12 +36,18 @@ namespace WebCore { -FrameLoadRequest::FrameLoadRequest(Frame* frame, const ResourceRequest& resourceRequest, const SubstituteData& substituteData) - : m_requester(frame->document()->securityOrigin()) +FrameLoadRequest::FrameLoadRequest(Frame* frame, const ResourceRequest& resourceRequest, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, const SubstituteData& substituteData) + : m_requester(&frame->document()->securityOrigin()) , m_resourceRequest(resourceRequest) - , m_lockHistory(false) , m_shouldCheckNewWindowPolicy(false) , m_substituteData(substituteData) + , m_lockHistory(LockHistory::No) + , m_lockBackForwardList(LockBackForwardList::No) + , m_shouldSendReferrer(MaybeSendReferrer) + , m_allowNavigationToInvalidURL(AllowNavigationToInvalidURL::Yes) + , m_newFrameOpenerPolicy(NewFrameOpenerPolicy::Allow) + , m_shouldReplaceDocumentIfJavaScriptURL(ReplaceDocumentIfJavaScriptURL) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) { } diff --git a/Source/WebCore/loader/FrameLoadRequest.h b/Source/WebCore/loader/FrameLoadRequest.h index 1b7789986..1cbebbb82 100644 --- a/Source/WebCore/loader/FrameLoadRequest.h +++ b/Source/WebCore/loader/FrameLoadRequest.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,9 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameLoadRequest_h -#define FrameLoadRequest_h +#pragma once +#include "FrameLoaderTypes.h" #include "ResourceRequest.h" #include "SecurityOrigin.h" #include "SubstituteData.h" @@ -35,31 +35,75 @@ class Frame; struct FrameLoadRequest { public: - explicit FrameLoadRequest(SecurityOrigin* requester) - : m_requester(requester) - , m_lockHistory(false) - , m_shouldCheckNewWindowPolicy(false) + FrameLoadRequest(SecurityOrigin& requester, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy newFrameOpenerPolicy, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : m_requester(&requester) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_shouldSendReferrer(shouldSendReferrer) + , m_allowNavigationToInvalidURL(allowNavigationToInvalidURL) + , m_newFrameOpenerPolicy(newFrameOpenerPolicy) + , m_shouldReplaceDocumentIfJavaScriptURL(ReplaceDocumentIfJavaScriptURL) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) { } - FrameLoadRequest(SecurityOrigin* requester, const ResourceRequest& resourceRequest) - : m_requester(requester) + FrameLoadRequest(SecurityOrigin& requester, const ResourceRequest& resourceRequest, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy newFrameOpenerPolicy, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : m_requester(&requester) , m_resourceRequest(resourceRequest) - , m_lockHistory(false) - , m_shouldCheckNewWindowPolicy(false) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_shouldSendReferrer(shouldSendReferrer) + , m_allowNavigationToInvalidURL(allowNavigationToInvalidURL) + , m_newFrameOpenerPolicy(newFrameOpenerPolicy) + , m_shouldReplaceDocumentIfJavaScriptURL(ReplaceDocumentIfJavaScriptURL) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) { } - FrameLoadRequest(SecurityOrigin* requester, const ResourceRequest& resourceRequest, const String& frameName) - : m_requester(requester) + FrameLoadRequest(SecurityOrigin& requester, const ResourceRequest& resourceRequest, const String& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy newFrameOpenerPolicy, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : m_requester(&requester) , m_resourceRequest(resourceRequest) , m_frameName(frameName) - , m_lockHistory(false) - , m_shouldCheckNewWindowPolicy(false) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_shouldSendReferrer(shouldSendReferrer) + , m_allowNavigationToInvalidURL(allowNavigationToInvalidURL) + , m_newFrameOpenerPolicy(newFrameOpenerPolicy) + , m_shouldReplaceDocumentIfJavaScriptURL(ReplaceDocumentIfJavaScriptURL) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) { } - FrameLoadRequest(Frame*, const ResourceRequest&, const SubstituteData& = SubstituteData()); + FrameLoadRequest(SecurityOrigin& requester, const ResourceRequest& resourceRequest, const String& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy newFrameOpenerPolicy, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : m_requester(&requester) + , m_resourceRequest(resourceRequest) + , m_frameName(frameName) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_shouldSendReferrer(shouldSendReferrer) + , m_allowNavigationToInvalidURL(allowNavigationToInvalidURL) + , m_newFrameOpenerPolicy(newFrameOpenerPolicy) + , m_shouldReplaceDocumentIfJavaScriptURL(shouldReplaceDocumentIfJavaScriptURL) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) + { + } + + FrameLoadRequest(SecurityOrigin& requester, const ResourceRequest& resourceRequest, const String& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy newFrameOpenerPolicy, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, const AtomicString& downloadAttribute) + : m_requester(&requester) + , m_resourceRequest(resourceRequest) + , m_frameName(frameName) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_shouldSendReferrer(shouldSendReferrer) + , m_allowNavigationToInvalidURL(allowNavigationToInvalidURL) + , m_newFrameOpenerPolicy(newFrameOpenerPolicy) + , m_shouldReplaceDocumentIfJavaScriptURL(shouldReplaceDocumentIfJavaScriptURL) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) + , m_downloadAttribute(downloadAttribute) + { + } + + WEBCORE_EXPORT FrameLoadRequest(Frame*, const ResourceRequest&, ShouldOpenExternalURLsPolicy, const SubstituteData& = SubstituteData()); bool isEmpty() const { return m_resourceRequest.isEmpty(); } @@ -71,9 +115,6 @@ public: const String& frameName() const { return m_frameName; } void setFrameName(const String& frameName) { m_frameName = frameName; } - void setLockHistory(bool lockHistory) { m_lockHistory = lockHistory; } - bool lockHistory() const { return m_lockHistory; } - void setShouldCheckNewWindowPolicy(bool checkPolicy) { m_shouldCheckNewWindowPolicy = checkPolicy; } bool shouldCheckNewWindowPolicy() const { return m_shouldCheckNewWindowPolicy; } @@ -81,15 +122,36 @@ public: void setSubstituteData(const SubstituteData& data) { m_substituteData = data; } bool hasSubstituteData() { return m_substituteData.isValid(); } + LockHistory lockHistory() const { return m_lockHistory; } + LockBackForwardList lockBackForwardList() const { return m_lockBackForwardList; } + ShouldSendReferrer shouldSendReferrer() const { return m_shouldSendReferrer; } + AllowNavigationToInvalidURL allowNavigationToInvalidURL() const { return m_allowNavigationToInvalidURL; } + NewFrameOpenerPolicy newFrameOpenerPolicy() const { return m_newFrameOpenerPolicy; } + + // The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the + // corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed. + ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL() const { return m_shouldReplaceDocumentIfJavaScriptURL; } + + void setShouldOpenExternalURLsPolicy(ShouldOpenExternalURLsPolicy policy) { m_shouldOpenExternalURLsPolicy = policy; } + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy() const { return m_shouldOpenExternalURLsPolicy; } + + const AtomicString& downloadAttribute() const { return m_downloadAttribute; } + private: RefPtr<SecurityOrigin> m_requester; ResourceRequest m_resourceRequest; String m_frameName; - bool m_lockHistory; - bool m_shouldCheckNewWindowPolicy; + bool m_shouldCheckNewWindowPolicy { false }; SubstituteData m_substituteData; -}; -} + LockHistory m_lockHistory; + LockBackForwardList m_lockBackForwardList; + ShouldSendReferrer m_shouldSendReferrer; + AllowNavigationToInvalidURL m_allowNavigationToInvalidURL; + NewFrameOpenerPolicy m_newFrameOpenerPolicy; + ShouldReplaceDocumentIfJavaScriptURL m_shouldReplaceDocumentIfJavaScriptURL; + ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow }; + AtomicString m_downloadAttribute; +}; -#endif // FrameLoadRequest_h +} // namespace WebCore diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp index c3cc3c559..f5a0a3ed1 100644 --- a/Source/WebCore/loader/FrameLoader.cpp +++ b/Source/WebCore/loader/FrameLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008 Alp Toker <alp@atoker.com> @@ -12,13 +12,13 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * documentation and/or other materials provided with the distribution. + * 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. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -43,13 +43,13 @@ #include "CachedResourceLoader.h" #include "Chrome.h" #include "ChromeClient.h" -#include "Console.h" +#include "ContentFilter.h" #include "ContentSecurityPolicy.h" -#include "DOMImplementation.h" #include "DOMWindow.h" #include "DatabaseManager.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" #include "Document.h" -#include "DocumentLoadTiming.h" #include "DocumentLoader.h" #include "Editor.h" #include "EditorClient.h" @@ -65,37 +65,47 @@ #include "FrameNetworkingContext.h" #include "FrameTree.h" #include "FrameView.h" -#include "HTMLAnchorElement.h" +#include "GCController.h" #include "HTMLFormElement.h" #include "HTMLInputElement.h" #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "HTMLParserIdioms.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" #include "HistoryController.h" #include "HistoryItem.h" #include "IconController.h" +#include "IgnoreOpensDuringUnloadCountIncrementer.h" #include "InspectorController.h" #include "InspectorInstrumentation.h" +#include "LinkLoader.h" +#include "LoadTiming.h" #include "LoaderStrategy.h" #include "Logging.h" -#include "MIMETypeRegistry.h" #include "MainFrame.h" #include "MemoryCache.h" +#include "MemoryRelease.h" +#include "NoEventDispatchAssertion.h" #include "Page.h" -#include "PageActivityAssertionToken.h" #include "PageCache.h" #include "PageTransitionEvent.h" +#include "PerformanceLogging.h" #include "PlatformStrategies.h" #include "PluginData.h" -#include "PluginDatabase.h" #include "PluginDocument.h" #include "PolicyChecker.h" #include "ProgressTracker.h" +#include "PublicSuffix.h" #include "ResourceHandle.h" +#include "ResourceLoadInfo.h" +#include "ResourceLoadObserver.h" #include "ResourceRequest.h" -#include "SchemeRegistry.h" -#include "ScriptCallStack.h" +#include "SVGDocument.h" +#include "SVGLocatable.h" +#include "SVGNames.h" +#include "SVGViewElement.h" +#include "SVGViewSpec.h" #include "ScriptController.h" #include "ScriptSourceCode.h" #include "ScrollAnimator.h" @@ -106,6 +116,7 @@ #include "Settings.h" #include "SubframeLoader.h" #include "TextResourceDecoder.h" +#include "UserContentController.h" #include "WindowFeatures.h" #include "XMLDocumentParser.h" #include <wtf/CurrentTime.h> @@ -114,61 +125,44 @@ #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> -#if ENABLE(SHARED_WORKERS) -#include "SharedWorkerRepository.h" -#endif - -#if ENABLE(SVG) -#include "SVGDocument.h" -#include "SVGLocatable.h" -#include "SVGNames.h" -#include "SVGPreserveAspectRatio.h" -#include "SVGSVGElement.h" -#include "SVGViewElement.h" -#include "SVGViewSpec.h" -#endif - #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) #include "Archive.h" #endif +#if ENABLE(DATA_DETECTION) +#include "DataDetection.h" +#endif + #if PLATFORM(IOS) #include "DocumentType.h" -#include "MemoryPressureHandler.h" #include "ResourceLoader.h" -#include "RuntimeApplicationChecksIOS.h" -#include "SystemMemory.h" +#include "RuntimeApplicationChecks.h" #include "WKContentObservation.h" #endif +#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - FrameLoader::" fmt, this, ##__VA_ARGS__) + namespace WebCore { using namespace HTMLNames; - -#if ENABLE(SVG) using namespace SVGNames; -#endif static const char defaultAcceptHeader[] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; -#if PLATFORM(IOS) -const int memoryLevelThresholdToPrunePageCache = 20; -#endif - bool isBackForwardLoadType(FrameLoadType type) { switch (type) { - case FrameLoadTypeStandard: - case FrameLoadTypeReload: - case FrameLoadTypeReloadFromOrigin: - case FrameLoadTypeSame: - case FrameLoadTypeRedirectWithLockedBackForwardList: - case FrameLoadTypeReplace: - return false; - case FrameLoadTypeBack: - case FrameLoadTypeForward: - case FrameLoadTypeIndexedBackForward: - return true; + case FrameLoadType::Standard: + case FrameLoadType::Reload: + case FrameLoadType::ReloadFromOrigin: + case FrameLoadType::Same: + case FrameLoadType::RedirectWithLockedBackForwardList: + case FrameLoadType::Replace: + return false; + case FrameLoadType::Back: + case FrameLoadType::Forward: + case FrameLoadType::IndexedBackForward: + return true; } ASSERT_NOT_REACHED(); return false; @@ -180,12 +174,31 @@ bool isBackForwardLoadType(FrameLoadType type) // non-member lets us exclude it from the header file, thus keeping FrameLoader.h's // API simpler. // -static bool isDocumentSandboxed(Frame* frame, SandboxFlags mask) +static bool isDocumentSandboxed(Frame& frame, SandboxFlags mask) { - return frame->document() && frame->document()->isSandboxed(mask); + return frame.document() && frame.document()->isSandboxed(mask); } +struct ForbidPromptsScope { + ForbidPromptsScope(Page* page) : m_page(page) + { + if (!m_page) + return; + m_page->forbidPrompts(); + } + + ~ForbidPromptsScope() + { + if (!m_page) + return; + m_page->allowPrompts(); + } + + Page* m_page; +}; + class FrameLoader::FrameProgressTracker { + WTF_MAKE_FAST_ALLOCATED; public: explicit FrameProgressTracker(Frame& frame) : m_frame(frame) @@ -230,23 +243,20 @@ FrameLoader::FrameLoader(Frame& frame, FrameLoaderClient& client) , m_icon(std::make_unique<IconController>(frame)) , m_mixedContentChecker(frame) , m_state(FrameStateProvisional) - , m_loadType(FrameLoadTypeStandard) - , m_delegateIsHandlingProvisionalLoadError(false) + , m_loadType(FrameLoadType::Standard) , m_quickRedirectComing(false) , m_sentRedirectNotification(false) , m_inStopAllLoaders(false) , m_isExecutingJavaScriptFormAction(false) , m_didCallImplicitClose(true) , m_wasUnloadEventEmitted(false) - , m_pageDismissalEventBeingDispatched(NoDismissal) , m_isComplete(false) , m_needsClear(false) - , m_checkTimer(this, &FrameLoader::checkTimerFired) + , m_checkTimer(*this, &FrameLoader::checkTimerFired) , m_shouldCallCheckCompleted(false) , m_shouldCallCheckLoadComplete(false) , m_opener(nullptr) , m_loadingFromCachedPage(false) - , m_suppressOpenerInNewFrame(false) , m_currentNavigationHasShownBeforeUnloadConfirmPanel(false) , m_loadsSynchronously(false) , m_forcedSandboxFlags(SandboxNone) @@ -257,9 +267,8 @@ FrameLoader::~FrameLoader() { setOpener(nullptr); - HashSet<Frame*>::iterator end = m_openedFrames.end(); - for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it) - (*it)->loader().m_opener = 0; + for (auto& frame : m_openedFrames) + frame->loader().m_opener = nullptr; m_client.frameLoaderDestroyed(); @@ -270,7 +279,7 @@ FrameLoader::~FrameLoader() void FrameLoader::init() { // This somewhat odd set of steps gives the frame an initial empty document. - setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()).get()); + setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()).ptr()); setProvisionalDocumentLoader(m_policyDocumentLoader.get()); m_provisionalDocumentLoader->startLoadingMainResource(); @@ -289,8 +298,8 @@ void FrameLoader::initForSynthesizedDocument(const URL&) // FrameLoader::checkCompleted() will overwrite the URL of the document to be activeDocumentLoader()->documentURL(). RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()); - loader->setFrame(&m_frame); - loader->setResponse(ResourceResponse(URL(), ASCIILiteral("text/html"), 0, String(), String())); + loader->attachToFrame(m_frame); + loader->setResponse(ResourceResponse(URL(), ASCIILiteral("text/html"), 0, String())); loader->setCommitted(true); setDocumentLoader(loader.get()); @@ -305,6 +314,7 @@ void FrameLoader::initForSynthesizedDocument(const URL&) m_needsClear = true; m_networkingContext = m_client.createNetworkingContext(); + m_progressTracker = std::make_unique<FrameProgressTracker>(m_frame); } #endif @@ -324,60 +334,50 @@ void FrameLoader::setDefersLoading(bool defers) } } -void FrameLoader::changeLocation(SecurityOrigin* securityOrigin, const URL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool refresh) +void FrameLoader::changeLocation(const FrameLoadRequest& request) { - urlSelected(FrameLoadRequest(securityOrigin, ResourceRequest(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy), "_self"), - 0, lockHistory, lockBackForwardList, MaybeSendReferrer, ReplaceDocumentIfJavaScriptURL); + urlSelected(request, nullptr); } -void FrameLoader::urlSelected(const URL& url, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ShouldSendReferrer shouldSendReferrer) +void FrameLoader::urlSelected(const URL& url, const String& passedTarget, Event* triggeringEvent, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, std::optional<NewFrameOpenerPolicy> openerPolicy, const AtomicString& downloadAttribute) { - urlSelected(FrameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), passedTarget), - triggeringEvent, lockHistory, lockBackForwardList, shouldSendReferrer, DoNotReplaceDocumentIfJavaScriptURL); + NewFrameOpenerPolicy newFrameOpenerPolicy = openerPolicy.value_or(shouldSendReferrer == NeverSendReferrer ? NewFrameOpenerPolicy::Suppress : NewFrameOpenerPolicy::Allow); + urlSelected(FrameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), passedTarget, lockHistory, lockBackForwardList, shouldSendReferrer, AllowNavigationToInvalidURL::Yes, newFrameOpenerPolicy, DoNotReplaceDocumentIfJavaScriptURL, shouldOpenExternalURLsPolicy, downloadAttribute), triggeringEvent); } -// The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the -// corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed. -void FrameLoader::urlSelected(const FrameLoadRequest& passedRequest, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ShouldSendReferrer shouldSendReferrer, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL) +void FrameLoader::urlSelected(const FrameLoadRequest& passedRequest, Event* triggeringEvent) { - ASSERT(!m_suppressOpenerInNewFrame); - Ref<Frame> protect(m_frame); FrameLoadRequest frameRequest(passedRequest); - if (m_frame.script().executeIfJavaScriptURL(frameRequest.resourceRequest().url(), shouldReplaceDocumentIfJavaScriptURL)) + if (m_frame.script().executeIfJavaScriptURL(frameRequest.resourceRequest().url(), frameRequest.shouldReplaceDocumentIfJavaScriptURL())) return; if (frameRequest.frameName().isEmpty()) frameRequest.setFrameName(m_frame.document()->baseTarget()); - if (shouldSendReferrer == NeverSendReferrer) - m_suppressOpenerInNewFrame = true; addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); + m_frame.document()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(frameRequest.resourceRequest(), ContentSecurityPolicy::InsecureRequestType::Navigation); - loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0, shouldSendReferrer); - - m_suppressOpenerInNewFrame = false; + loadFrameRequest(frameRequest, triggeringEvent, nullptr); } -void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) +void FrameLoader::submitForm(Ref<FormSubmission>&& submission) { - ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod); + ASSERT(submission->method() == FormSubmission::Method::Post || submission->method() == FormSubmission::Method::Get); // FIXME: Find a good spot for these. - ASSERT(submission->data()); - ASSERT(submission->state()); - ASSERT(!submission->state()->sourceDocument()->frame() || submission->state()->sourceDocument()->frame() == &m_frame); - + ASSERT(!submission->state().sourceDocument().frame() || submission->state().sourceDocument().frame() == &m_frame); + if (!m_frame.page()) return; - + if (submission->action().isEmpty()) return; - if (isDocumentSandboxed(&m_frame, SandboxForms)) { + if (isDocumentSandboxed(m_frame, SandboxForms)) { // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. - m_frame.document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked form submission to '" + submission->action().stringCenterEllipsizedToLength() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set."); + m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked form submission to '" + submission->action().stringCenterEllipsizedToLength() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set."); return; } @@ -391,12 +391,12 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) return; } - Frame* targetFrame = findFrameForNavigation(submission->target(), submission->state()->sourceDocument()); + Frame* targetFrame = findFrameForNavigation(submission->target(), &submission->state().sourceDocument()); if (!targetFrame) { - if (!DOMWindow::allowPopUp(&m_frame) && !ScriptController::processingUserGesture()) + if (!DOMWindow::allowPopUp(m_frame) && !ScriptController::processingUserGesture()) return; - // FIXME: targetFrame can be 0 for two distinct reasons: + // FIXME: targetFrame can be null for two distinct reasons: // 1. The frame was not found by name, so we should try opening a new window. // 2. The frame was found, but navigating it was not allowed, e.g. by HTML5 sandbox or by origin checks. // Continuing form submission makes no sense in the latter case. @@ -413,7 +413,7 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) // We do not want to submit more than one form from the same page, nor do we want to submit a single // form more than once. This flag prevents these from happening; not sure how other browsers prevent this. - // The flag is reset in each time we start handle a new mouse or key down event, and + // The flag is reset in each time we start dispatching a new mouse or key down event, and // also in setView since this part may get reused for a page from the back/forward cache. // The form multi-submit logic here is only needed when we are submitting a form that affects this frame. @@ -427,11 +427,11 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) m_submittedFormURL = submission->requestURL(); } - submission->data()->generateFiles(m_frame.document()); + submission->data().generateFiles(m_frame.document()); submission->setReferrer(outgoingReferrer()); submission->setOrigin(outgoingOrigin()); - targetFrame->navigationScheduler().scheduleFormSubmission(submission); + targetFrame->navigationScheduler().scheduleFormSubmission(WTFMove(submission)); } void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy) @@ -439,55 +439,8 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy) if (m_frame.document() && m_frame.document()->parser()) m_frame.document()->parser()->stopParsing(); - if (unloadEventPolicy != UnloadEventPolicyNone) { - if (m_frame.document()) { - if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) { - Element* currentFocusedElement = m_frame.document()->focusedElement(); - if (currentFocusedElement && currentFocusedElement->toInputElement()) - currentFocusedElement->toInputElement()->endEditing(); - if (m_pageDismissalEventBeingDispatched == NoDismissal) { - if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) { - m_pageDismissalEventBeingDispatched = PageHideDismissal; - m_frame.document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame.document()->inPageCache()), m_frame.document()); - } - - // FIXME: update Page Visibility state here. - // https://bugs.webkit.org/show_bug.cgi?id=116770 - - if (!m_frame.document()->inPageCache()) { - RefPtr<Event> unloadEvent(Event::create(eventNames().unloadEvent, false, false)); - // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed - // while dispatching the event, so protect it to prevent writing the end - // time into freed memory. - RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader; - m_pageDismissalEventBeingDispatched = UnloadDismissal; - if (documentLoader && !documentLoader->timing()->unloadEventStart() && !documentLoader->timing()->unloadEventEnd()) { - DocumentLoadTiming* timing = documentLoader->timing(); - ASSERT(timing->navigationStart()); - timing->markUnloadEventStart(); - m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document()); - timing->markUnloadEventEnd(); - } else - m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document()); - } - } - m_pageDismissalEventBeingDispatched = NoDismissal; - if (m_frame.document()) - m_frame.document()->updateStyleIfNeeded(); - m_wasUnloadEventEmitted = true; - } - } - - // Dispatching the unload event could have made m_frame.document() null. - if (m_frame.document() && !m_frame.document()->inPageCache()) { - // Don't remove event listeners from a transitional empty document (see bug 28716 for more information). - bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader - && m_frame.document()->isSecureTransitionTo(m_provisionalDocumentLoader->url()); - - if (!keepEventListeners) - m_frame.document()->removeAllEventListeners(); - } - } + if (unloadEventPolicy != UnloadEventPolicyNone) + dispatchUnloadEvents(unloadEventPolicy); m_isComplete = true; // to avoid calling completed() in finishedParsing() m_didCallImplicitClose = true; // don't want that one either @@ -497,15 +450,13 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy) m_frame.document()->setParsing(false); } - if (Document* doc = m_frame.document()) { + if (auto* document = m_frame.document()) { // FIXME: HTML5 doesn't tell us to set the state to complete when aborting, but we do anyway to match legacy behavior. // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10537 - doc->setReadyState(Document::Complete); + document->setReadyState(Document::Complete); -#if ENABLE(SQL_DATABASE) // FIXME: Should the DatabaseManager watch for something like ActiveDOMObject::stop() rather than being special-cased here? - DatabaseManager::manager().stopDatabases(doc, 0); -#endif + DatabaseManager::singleton().stopDatabases(*document, nullptr); } // FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache. @@ -533,18 +484,28 @@ void FrameLoader::willTransitionToCommitted() if (m_frame.editor().hasComposition()) { // The text was already present in DOM, so it's better to confirm than to cancel the composition. m_frame.editor().confirmComposition(); - if (EditorClient* editorClient = m_frame.editor().client()) + if (EditorClient* editorClient = m_frame.editor().client()) { editorClient->respondToChangedSelection(&m_frame); + editorClient->discardedComposition(&m_frame); + } } } bool FrameLoader::closeURL() { history().saveDocumentState(); - - // Should only send the pagehide event here if the current document exists and has not been placed in the page cache. + Document* currentDocument = m_frame.document(); - stopLoading(currentDocument && !currentDocument->inPageCache() ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly); + UnloadEventPolicy unloadEventPolicy; + if (m_frame.page() && m_frame.page()->chrome().client().isSVGImageChromeClient()) { + // If this is the SVGDocument of an SVGImage, no need to dispatch events or recalcStyle. + unloadEventPolicy = UnloadEventPolicyNone; + } else { + // Should only send the pagehide event here if the current document exists and has not been placed in the page cache. + unloadEventPolicy = currentDocument && currentDocument->pageCacheState() == Document::NotInPageCache ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly; + } + + stopLoading(unloadEventPolicy); m_frame.editor().clearUndoRedoOperations(); return true; @@ -606,6 +567,17 @@ void FrameLoader::cancelAndClear() m_frame.script().updatePlatformScriptObjects(); } +static inline bool shouldClearWindowName(const Frame& frame, const Document& newDocument) +{ + if (!frame.isMainFrame()) + return false; + + if (frame.loader().opener()) + return false; + + return !newDocument.securityOrigin().isSameOriginAs(frame.document()->securityOrigin()); +} + void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView) { m_frame.editor().clear(); @@ -614,33 +586,43 @@ void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool return; m_needsClear = false; - if (!m_frame.document()->inPageCache()) { + if (m_frame.document()->pageCacheState() != Document::InPageCache) { m_frame.document()->cancelParsing(); m_frame.document()->stopActiveDOMObjects(); - if (m_frame.document()->hasLivingRenderTree()) { - m_frame.document()->prepareForDestruction(); - m_frame.document()->removeFocusedNodeOfSubtree(m_frame.document()); - } + bool hadLivingRenderTree = m_frame.document()->hasLivingRenderTree(); + m_frame.document()->prepareForDestruction(); + if (hadLivingRenderTree) + m_frame.document()->removeFocusedNodeOfSubtree(*m_frame.document()); } // Do this after detaching the document so that the unload event works. if (clearWindowProperties) { - InspectorInstrumentation::frameWindowDiscarded(&m_frame, m_frame.document()->domWindow()); - m_frame.document()->domWindow()->resetUnlessSuspendedForPageCache(); - m_frame.script().clearWindowShell(newDocument->domWindow(), m_frame.document()->inPageCache()); + InspectorInstrumentation::frameWindowDiscarded(m_frame, m_frame.document()->domWindow()); + m_frame.document()->domWindow()->resetUnlessSuspendedForDocumentSuspension(); + m_frame.script().clearWindowShellsNotMatchingDOMWindow(newDocument->domWindow(), m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache); + + if (shouldClearWindowName(m_frame, *newDocument)) + m_frame.tree().setName(nullAtom); } m_frame.selection().prepareForDestruction(); - m_frame.eventHandler().clear(); + + // We may call this code during object destruction, so need to make sure eventHandler is present. + if (auto eventHandler = m_frame.eventHandlerPtr()) + eventHandler->clear(); + if (clearFrameView && m_frame.view()) m_frame.view()->clear(); // Do not drop the document before the ScriptController and view are cleared // as some destructors might still try to access the document. - m_frame.setDocument(0); + m_frame.setDocument(nullptr); subframeLoader().clear(); + if (clearWindowProperties) + m_frame.script().setDOMWindowForWindowShell(newDocument->domWindow()); + if (clearScriptObjects) m_frame.script().clearScriptObjects(); @@ -658,36 +640,35 @@ void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool void FrameLoader::receivedFirstData() { - dispatchDidCommitLoad(); + dispatchDidCommitLoad(std::nullopt); dispatchDidClearWindowObjectsInAllWorlds(); dispatchGlobalObjectAvailableInAllWorlds(); if (m_documentLoader) { - StringWithDirection ptitle = m_documentLoader->title(); - // If we have a title let the WebView know about it. - if (!ptitle.isNull()) - m_client.dispatchDidReceiveTitle(ptitle); + auto& title = m_documentLoader->title(); + if (!title.string.isNull()) + m_client.dispatchDidReceiveTitle(title); } if (!m_documentLoader) return; - if (m_frame.document()->isViewSource()) - return; + + ASSERT(m_frame.document()); + auto& document = *m_frame.document(); + + ASSERT(m_frame.document()); + LinkLoader::loadLinksFromHeader(m_documentLoader->response().httpHeaderField(HTTPHeaderName::Link), m_frame.document()->url(), *m_frame.document()); double delay; - String url; - if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url)) + String urlString; + if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField(HTTPHeaderName::Refresh), delay, urlString)) return; - if (url.isEmpty()) - url = m_frame.document()->url().string(); - else - url = m_frame.document()->completeURL(url).string(); - - if (!protocolIsJavaScript(url)) - m_frame.navigationScheduler().scheduleRedirect(delay, url); + auto completedURL = urlString.isEmpty() ? document.url() : document.completeURL(urlString); + if (!protocolIsJavaScript(completedURL)) + m_frame.navigationScheduler().scheduleRedirect(document, delay, completedURL); else { - String message = "Refused to refresh " + m_frame.document()->url().stringCenterEllipsizedToLength() + " to a javascript: URL"; - m_frame.document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); + auto message = "Refused to refresh " + document.url().stringCenterEllipsizedToLength() + " to a javascript: URL"; + m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); } } @@ -704,8 +685,8 @@ void FrameLoader::didBeginDocument(bool dispatch) m_frame.document()->setReadyState(Document::Loading); if (m_pendingStateObject) { - m_frame.document()->statePopped(m_pendingStateObject.get()); - m_pendingStateObject.clear(); + m_frame.document()->statePopped(*m_pendingStateObject); + m_pendingStateObject = nullptr; } if (dispatch) @@ -715,31 +696,17 @@ void FrameLoader::didBeginDocument(bool dispatch) m_frame.document()->initContentSecurityPolicy(); const Settings& settings = m_frame.settings(); - m_frame.document()->cachedResourceLoader()->setImagesEnabled(settings.areImagesEnabled()); - m_frame.document()->cachedResourceLoader()->setAutoLoadImages(settings.loadsImagesAutomatically()); + m_frame.document()->cachedResourceLoader().setImagesEnabled(settings.areImagesEnabled()); + m_frame.document()->cachedResourceLoader().setAutoLoadImages(settings.loadsImagesAutomatically()); if (m_documentLoader) { - String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control"); + String dnsPrefetchControl = m_documentLoader->response().httpHeaderField(HTTPHeaderName::XDNSPrefetchControl); if (!dnsPrefetchControl.isEmpty()) m_frame.document()->parseDNSPrefetchControlHeader(dnsPrefetchControl); - String policyValue = m_documentLoader->response().httpHeaderField("Content-Security-Policy"); - if (!policyValue.isEmpty()) - m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::Enforce); - - policyValue = m_documentLoader->response().httpHeaderField("Content-Security-Policy-Report-Only"); - if (!policyValue.isEmpty()) - m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::Report); - - policyValue = m_documentLoader->response().httpHeaderField("X-WebKit-CSP"); - if (!policyValue.isEmpty()) - m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::PrefixedEnforce); + m_frame.document()->contentSecurityPolicy()->didReceiveHeaders(ContentSecurityPolicyResponseHeaders(m_documentLoader->response()), ContentSecurityPolicy::ReportParsingErrors::No); - policyValue = m_documentLoader->response().httpHeaderField("X-WebKit-CSP-Report-Only"); - if (!policyValue.isEmpty()) - m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::PrefixedReport); - - String headerContentLanguage = m_documentLoader->response().httpHeaderField("Content-Language"); + String headerContentLanguage = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ContentLanguage); if (!headerContentLanguage.isEmpty()) { size_t commaIndex = headerContentLanguage.find(','); headerContentLanguage.truncate(commaIndex); // notFound == -1 == don't truncate @@ -766,6 +733,8 @@ void FrameLoader::finishedParsing() m_client.dispatchDidFinishDocumentLoad(); + scrollToFragmentWithParentBoundary(m_frame.document()->url()); + checkCompleted(); if (!m_frame.view()) @@ -774,7 +743,6 @@ void FrameLoader::finishedParsing() // Check if the scrollbars are really needed for the content. // If not, remove them, relayout, and repaint. m_frame.view()->restoreScrollbar(); - scrollToFragmentWithParentBoundary(m_frame.document()->url()); } void FrameLoader::loadDone() @@ -802,32 +770,47 @@ bool FrameLoader::allAncestorsAreComplete() const void FrameLoader::checkCompleted() { - Ref<Frame> protect(m_frame); m_shouldCallCheckCompleted = false; // Have we completed before? if (m_isComplete) return; + // FIXME: It would be better if resource loads were kicked off after render tree update (or didn't complete synchronously). + // https://bugs.webkit.org/show_bug.cgi?id=171729 + if (m_frame.document()->inRenderTreeUpdate()) { + scheduleCheckCompleted(); + return; + } + // Are we still parsing? if (m_frame.document()->parsing()) return; // Still waiting for images/scripts? - if (m_frame.document()->cachedResourceLoader()->requestCount()) + if (m_frame.document()->cachedResourceLoader().requestCount()) return; // Still waiting for elements that don't go through a FrameLoader? if (m_frame.document()->isDelayingLoadEvent()) return; + auto* scriptableParser = m_frame.document()->scriptableDocumentParser(); + if (scriptableParser && scriptableParser->hasScriptsWaitingForStylesheets()) + return; + // Any frame that hasn't completed yet? if (!allChildrenAreComplete()) return; + // Important not to protect earlier in this function, because earlier parts + // of this function can be called in the frame's destructor, and it's not legal + // to ref an object while it's being destroyed. + Ref<Frame> protect(m_frame); + // OK, completed. m_isComplete = true; - m_requestedHistoryItem = 0; + m_requestedHistoryItem = nullptr; m_frame.document()->setReadyState(Document::Complete); #if PLATFORM(IOS) @@ -851,7 +834,7 @@ void FrameLoader::checkCompleted() checkLoadComplete(); } -void FrameLoader::checkTimerFired(Timer<FrameLoader>&) +void FrameLoader::checkTimerFired() { Ref<Frame> protect(m_frame); @@ -904,110 +887,78 @@ void FrameLoader::loadURLIntoChildFrame(const URL& url, const String& referer, F ASSERT(childFrame); #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree().uniqueName(), url); - if (subframeArchive) { - childFrame->loader().loadArchive(subframeArchive.release()); - return; + if (auto activeLoader = activeDocumentLoader()) { + if (auto subframeArchive = activeLoader->popArchiveForSubframe(childFrame->tree().uniqueName(), url)) { + childFrame->loader().loadArchive(RefPtr<Archive> { subframeArchive }.releaseNonNull()); + return; + } } -#endif // ENABLE(WEB_ARCHIVE) +#endif - HistoryItem* parentItem = history().currentItem(); // If we're moving in the back/forward list, we might want to replace the content // of this child frame with whatever was there at that point. - if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType()) - && !m_frame.document()->loadEventFinished()) { - HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree().uniqueName()); - if (childItem) { + auto* parentItem = history().currentItem(); + if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType()) && !m_frame.document()->loadEventFinished()) { + if (auto* childItem = parentItem->childItemWithTarget(childFrame->tree().uniqueName())) { childFrame->loader().m_requestedHistoryItem = childItem; - childFrame->loader().loadDifferentDocumentItem(childItem, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem); + childFrame->loader().loadDifferentDocumentItem(*childItem, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem); return; } } - childFrame->loader().loadURL(url, referer, "_self", false, FrameLoadTypeRedirectWithLockedBackForwardList, 0, 0); + FrameLoadRequest frameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), "_self", LockHistory::No, LockBackForwardList::Yes, ShouldSendReferrer::MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, ReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy::ShouldNotAllow); + childFrame->loader().loadURL(frameLoadRequest, referer, FrameLoadType::RedirectWithLockedBackForwardList, 0, 0); } #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) -void FrameLoader::loadArchive(PassRefPtr<Archive> archive) + +void FrameLoader::loadArchive(Ref<Archive>&& archive) { ArchiveResource* mainResource = archive->mainResource(); ASSERT(mainResource); if (!mainResource) return; - - SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), URL()); + + ResourceResponse response(URL(), mainResource->mimeType(), mainResource->data().size(), mainResource->textEncoding()); + SubstituteData substituteData(&mainResource->data(), URL(), response, SubstituteData::SessionHistoryVisibility::Hidden); ResourceRequest request(mainResource->url()); -#if PLATFORM(MAC) - request.applyWebArchiveHackForMail(); -#endif - RefPtr<DocumentLoader> documentLoader = m_client.createDocumentLoader(request, substituteData); - documentLoader->setArchive(archive.get()); - load(documentLoader.get()); + auto documentLoader = m_client.createDocumentLoader(request, substituteData); + documentLoader->setArchive(WTFMove(archive)); + load(documentLoader.ptr()); } -#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - -ObjectContentType FrameLoader::defaultObjectContentType(const URL& url, const String& mimeTypeIn, bool shouldPreferPlugInsForImages) -{ - String mimeType = mimeTypeIn; - - if (mimeType.isEmpty()) - mimeType = mimeTypeFromURL(url); - -#if !PLATFORM(MAC) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does EFL - if (mimeType.isEmpty()) { - String decodedPath = decodeURLEscapeSequences(url.path()); - mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(decodedPath.substring(decodedPath.reverseFind('.') + 1)); - } -#endif - - if (mimeType.isEmpty()) - return ObjectContentFrame; // Go ahead and hope that we can display the content. - -#if !PLATFORM(MAC) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does EFL - bool plugInSupportsMIMEType = PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType); -#else - bool plugInSupportsMIMEType = false; -#endif - if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) - return shouldPreferPlugInsForImages && plugInSupportsMIMEType ? WebCore::ObjectContentNetscapePlugin : WebCore::ObjectContentImage; - - if (plugInSupportsMIMEType) - return WebCore::ObjectContentNetscapePlugin; - - if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType)) - return WebCore::ObjectContentFrame; - - return WebCore::ObjectContentNone; -} +#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) String FrameLoader::outgoingReferrer() const { // See http://www.whatwg.org/specs/web-apps/current-work/#fetching-resources // for why we walk the parent chain for srcdoc documents. Frame* frame = &m_frame; - while (frame->document()->isSrcdocDocument()) { + while (frame && frame->document()->isSrcdocDocument()) { frame = frame->tree().parent(); // Srcdoc documents cannot be top-level documents, by definition, // because they need to be contained in iframes with the srcdoc. ASSERT(frame); } + if (!frame) + return emptyString(); return frame->loader().m_outgoingReferrer; } String FrameLoader::outgoingOrigin() const { - return m_frame.document()->securityOrigin()->toString(); + return m_frame.document()->securityOrigin().toString(); } -bool FrameLoader::checkIfFormActionAllowedByCSP(const URL& url) const +bool FrameLoader::checkIfFormActionAllowedByCSP(const URL& url, bool didReceiveRedirectResponse) const { if (m_submittedFormURL.isEmpty()) return true; - return m_frame.document()->contentSecurityPolicy()->allowFormAction(url); + auto redirectResponseReceived = didReceiveRedirectResponse ? ContentSecurityPolicy::RedirectResponseReceived::Yes : ContentSecurityPolicy::RedirectResponseReceived::No; + return m_frame.document()->contentSecurityPolicy()->allowFormAction(url, redirectResponseReceived); } Frame* FrameLoader::opener() @@ -1034,9 +985,9 @@ void FrameLoader::setOpener(Frame* opener) void FrameLoader::handleFallbackContent() { HTMLFrameOwnerElement* owner = m_frame.ownerElement(); - if (!owner || !owner->hasTagName(objectTag)) + if (!is<HTMLObjectElement>(owner)) return; - toHTMLObjectElement(owner)->renderFallbackContent(); + downcast<HTMLObjectElement>(*owner).renderFallbackContent(); } void FrameLoader::provisionalLoadStarted() @@ -1045,6 +996,11 @@ void FrameLoader::provisionalLoadStarted() m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); m_frame.navigationScheduler().cancel(true); m_client.provisionalLoadStarted(); + + if (m_frame.isMainFrame()) { + if (auto* page = m_frame.page()) + page->didStartProvisionalLoad(); + } } void FrameLoader::resetMultipleFormSubmissionProtection() @@ -1068,7 +1024,7 @@ void FrameLoader::setFirstPartyForCookies(const URL& url) // This does the same kind of work that didOpenURL does, except it relies on the fact // that a higher level already checked that the URLs match and the scrolling is the right thing to do. -void FrameLoader::loadInSameDocument(const URL& url, PassRefPtr<SerializedScriptValue> stateObject, bool isNewNavigation) +void FrameLoader::loadInSameDocument(const URL& url, SerializedScriptValue* stateObject, bool isNewNavigation) { // If we have a state object, we cannot also be a new navigation. ASSERT(!stateObject || (stateObject && !isNewNavigation)); @@ -1121,7 +1077,7 @@ void FrameLoader::loadInSameDocument(const URL& url, PassRefPtr<SerializedScript m_client.dispatchDidNavigateWithinPage(); - m_frame.document()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue()); + m_frame.document()->statePopped(stateObject ? Ref<SerializedScriptValue> { *stateObject } : SerializedScriptValue::nullValue()); m_client.dispatchDidPopStateWithinPage(); if (hashChange) { @@ -1149,45 +1105,25 @@ void FrameLoader::completed() parent->loader().checkCompleted(); if (m_frame.view()) - m_frame.view()->maintainScrollPositionAtAnchor(0); - m_activityAssertion = nullptr; + m_frame.view()->maintainScrollPositionAtAnchor(nullptr); } void FrameLoader::started() { - if (m_frame.page()) - m_activityAssertion = m_frame.page()->createActivityToken(); for (Frame* frame = &m_frame; frame; frame = frame->tree().parent()) frame->loader().m_isComplete = false; } -void FrameLoader::prepareForHistoryNavigation() -{ - // If there is no currentItem, but we still want to engage in - // history navigation we need to manufacture one, and update - // the state machine of this frame to impersonate having - // loaded it. - RefPtr<HistoryItem> currentItem = history().currentItem(); - if (!currentItem) { - currentItem = HistoryItem::create(); - currentItem->setLastVisitWasFailure(true); - history().setCurrentItem(currentItem.get()); - m_frame.page()->backForward().setCurrentItem(currentItem.get()); - - ASSERT(stateMachine()->isDisplayingInitialEmptyDocument()); - stateMachine()->advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); - stateMachine()->advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); - } -} - void FrameLoader::prepareForLoadStart() { + RELEASE_LOG_IF_ALLOWED("prepareForLoadStart: Starting frame load (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame()); + m_progressTracker->progressStarted(); m_client.dispatchDidStartProvisionalLoad(); if (AXObjectCache::accessibilityEnabled()) { if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) { - AXObjectCache::AXLoadingEvent loadingEvent = loadType() == FrameLoadTypeReload ? AXObjectCache::AXLoadingReloaded : AXObjectCache::AXLoadingStarted; + AXObjectCache::AXLoadingEvent loadingEvent = loadType() == FrameLoadType::Reload ? AXObjectCache::AXLoadingReloaded : AXObjectCache::AXLoadingStarted; cache->frameLoadingEventNotification(&m_frame, loadingEvent); } } @@ -1198,15 +1134,14 @@ void FrameLoader::setupForReplace() m_client.revertToProvisionalState(m_documentLoader.get()); setState(FrameStateProvisional); m_provisionalDocumentLoader = m_documentLoader; - m_documentLoader = 0; + m_documentLoader = nullptr; detachChildren(); } -void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList, - PassRefPtr<Event> event, PassRefPtr<FormState> formState, ShouldSendReferrer shouldSendReferrer) -{ +void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, Event* event, FormState* formState) +{ // Protect frame from getting blown away inside dispatchBeforeLoadEvent in loadWithDocumentLoader. - Ref<Frame> protect(m_frame); + auto protectFrame = makeRef(m_frame); URL url = request.resourceRequest().url(); @@ -1221,25 +1156,25 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis argsReferrer = outgoingReferrer(); String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), url, argsReferrer); - if (shouldSendReferrer == NeverSendReferrer) + if (request.shouldSendReferrer() == NeverSendReferrer) referrer = String(); - + FrameLoadType loadType; if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) - loadType = FrameLoadTypeReload; - else if (lockBackForwardList) - loadType = FrameLoadTypeRedirectWithLockedBackForwardList; + loadType = FrameLoadType::Reload; + else if (request.lockBackForwardList() == LockBackForwardList::Yes) + loadType = FrameLoadType::RedirectWithLockedBackForwardList; else - loadType = FrameLoadTypeStandard; + loadType = FrameLoadType::Standard; if (request.resourceRequest().httpMethod() == "POST") - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); + loadPostRequest(request, referrer, loadType, event, formState); else - loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); + loadURL(request, referrer, loadType, event, formState); // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual // load if frame names have changed. - Frame* sourceFrame = formState ? formState->sourceDocument()->frame() : &m_frame; + Frame* sourceFrame = formState ? formState->sourceDocument().frame() : &m_frame; if (!sourceFrame) sourceFrame = &m_frame; Frame* targetFrame = sourceFrame->loader().findFrameForNavigation(request.frameName()); @@ -1249,48 +1184,67 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis } } -void FrameLoader::loadURL(const URL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, - PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) +static ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicyToApply(Frame& sourceFrame, ShouldOpenExternalURLsPolicy propagatedPolicy) +{ + if (!sourceFrame.isMainFrame()) + return ShouldOpenExternalURLsPolicy::ShouldNotAllow; + if (ScriptController::processingUserGesture()) + return ShouldOpenExternalURLsPolicy::ShouldAllow; + return propagatedPolicy; +} + +bool FrameLoader::isNavigationAllowed() const +{ + return m_pageDismissalEventBeingDispatched == PageDismissalType::None && NavigationDisabler::isNavigationAllowed(); +} + +void FrameLoader::loadURL(const FrameLoadRequest& frameLoadRequest, const String& referrer, FrameLoadType newLoadType, Event* event, FormState* formState) { if (m_inStopAllLoaders) return; Ref<Frame> protect(m_frame); - RefPtr<FormState> formState = prpFormState; + String frameName = frameLoadRequest.frameName(); + AllowNavigationToInvalidURL allowNavigationToInvalidURL = frameLoadRequest.allowNavigationToInvalidURL(); + NewFrameOpenerPolicy openerPolicy = frameLoadRequest.newFrameOpenerPolicy(); + LockHistory lockHistory = frameLoadRequest.lockHistory(); bool isFormSubmission = formState; - + + const URL& newURL = frameLoadRequest.resourceRequest().url(); ResourceRequest request(newURL); if (!referrer.isEmpty()) { request.setHTTPReferrer(referrer); RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer); addHTTPOriginIfNeeded(request, referrerOrigin->toString()); } -#if ENABLE(CACHE_PARTITIONING) if (&m_frame.tree().top() != &m_frame) - request.setCachePartition(m_frame.tree().top().document()->securityOrigin()->cachePartition()); -#endif + request.setDomainForCachePartition(m_frame.tree().top().document()->securityOrigin().domainForCachePartition()); + addExtraFieldsToRequest(request, newLoadType, true); - if (newLoadType == FrameLoadTypeReload || newLoadType == FrameLoadTypeReloadFromOrigin) + if (newLoadType == FrameLoadType::Reload || newLoadType == FrameLoadType::ReloadFromOrigin) request.setCachePolicy(ReloadIgnoringCacheData); - ASSERT(newLoadType != FrameLoadTypeSame); + ASSERT(newLoadType != FrameLoadType::Same); // The search for a target frame is done earlier in the case of form submission. Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName); if (targetFrame && targetFrame != &m_frame) { - targetFrame->loader().loadURL(newURL, referrer, "_self", lockHistory, newLoadType, event, formState.release()); + FrameLoadRequest newFrameLoadRequest(frameLoadRequest); + newFrameLoadRequest.setFrameName("_self"); + targetFrame->loader().loadURL(newFrameLoadRequest, referrer, newLoadType, event, formState); return; } - if (m_pageDismissalEventBeingDispatched != NoDismissal) + if (!isNavigationAllowed()) return; - NavigationAction action(request, newLoadType, isFormSubmission, event); + NavigationAction action(request, newLoadType, isFormSubmission, event, frameLoadRequest.shouldOpenExternalURLsPolicy(), frameLoadRequest.downloadAttribute()); if (!targetFrame && !frameName.isEmpty()) { - policyChecker().checkNewWindowPolicy(action, request, formState.release(), frameName, [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { - continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue); + action = action.copyWithShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(m_frame, frameLoadRequest.shouldOpenExternalURLsPolicy())); + policyChecker().checkNewWindowPolicy(action, request, formState, frameName, [this, allowNavigationToInvalidURL, openerPolicy] (const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { + continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, allowNavigationToInvalidURL, openerPolicy); }); return; } @@ -1308,7 +1262,7 @@ void FrameLoader::loadURL(const URL& newURL, const String& referrer, const Strin oldDocumentLoader->setLastCheckedRequest(ResourceRequest()); policyChecker().stopCheck(); policyChecker().setLoadType(newLoadType); - policyChecker().checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(), [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) { + policyChecker().checkNavigationPolicy(request, false /* didReceiveRedirectResponse */, oldDocumentLoader.get(), formState, [this] (const ResourceRequest& request, FormState*, bool shouldContinue) { continueFragmentScrollAfterNavigationPolicy(request, shouldContinue); }); return; @@ -1316,16 +1270,16 @@ void FrameLoader::loadURL(const URL& newURL, const String& referrer, const Strin // must grab this now, since this load may stop the previous load and clear this flag bool isRedirect = m_quickRedirectComing; - loadWithNavigationAction(request, action, lockHistory, newLoadType, formState.release()); + loadWithNavigationAction(request, action, lockHistory, newLoadType, formState, allowNavigationToInvalidURL); if (isRedirect) { m_quickRedirectComing = false; if (m_provisionalDocumentLoader) m_provisionalDocumentLoader->setIsClientRedirect(true); - } else if (sameURL && newLoadType != FrameLoadTypeReload && newLoadType != FrameLoadTypeReloadFromOrigin) { + } else if (sameURL && newLoadType != FrameLoadType::Reload && newLoadType != FrameLoadType::ReloadFromOrigin) { // Example of this case are sites that reload the same URL with a different cookie // driving the generated content, or a master frame with links that drive a target // frame, where the user has clicked on the same link repeatedly. - m_loadType = FrameLoadTypeSame; + m_loadType = FrameLoadType::Same; } } @@ -1333,10 +1287,12 @@ SubstituteData FrameLoader::defaultSubstituteDataForURL(const URL& url) { if (!shouldTreatURLAsSrcdocDocument(url)) return SubstituteData(); - String srcdoc = m_frame.ownerElement()->fastGetAttribute(srcdocAttr); + String srcdoc = m_frame.ownerElement()->attributeWithoutSynchronization(srcdocAttr); ASSERT(!srcdoc.isNull()); CString encodedSrcdoc = srcdoc.utf8(); - return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), "text/html", "UTF-8", URL()); + + ResourceResponse response(URL(), ASCIILiteral("text/html"), encodedSrcdoc.length(), ASCIILiteral("UTF-8")); + return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), URL(), response, SubstituteData::SessionHistoryVisibility::Hidden); } void FrameLoader::load(const FrameLoadRequest& passedRequest) @@ -1358,8 +1314,9 @@ void FrameLoader::load(const FrameLoadRequest& passedRequest) } if (request.shouldCheckNewWindowPolicy()) { - policyChecker().checkNewWindowPolicy(NavigationAction(request.resourceRequest(), NavigationTypeOther), request.resourceRequest(), nullptr, request.frameName(), [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { - continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue); + NavigationAction action(request.resourceRequest(), NavigationType::Other, passedRequest.shouldOpenExternalURLsPolicy()); + policyChecker().checkNewWindowPolicy(action, request.resourceRequest(), nullptr, request.frameName(), [this] (const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { + continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress); }); return; @@ -1368,23 +1325,25 @@ void FrameLoader::load(const FrameLoadRequest& passedRequest) if (!request.hasSubstituteData()) request.setSubstituteData(defaultSubstituteDataForURL(request.resourceRequest().url())); - RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData()); - if (request.lockHistory() && m_documentLoader) - loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory()); - load(loader.get()); + Ref<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData()); + applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, request.shouldOpenExternalURLsPolicy()); + + load(loader.ptr()); } -void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState) +void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, LockHistory lockHistory, FrameLoadType type, FormState* formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL) { - RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url())); - if (lockHistory && m_documentLoader) + Ref<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url())); + applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, action.shouldOpenExternalURLsPolicy()); + + if (lockHistory == LockHistory::Yes && m_documentLoader) loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory()); loader->setTriggeringAction(action); if (m_documentLoader) loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - loadWithDocumentLoader(loader.get(), type, formState); + loadWithDocumentLoader(loader.ptr(), type, formState, allowNavigationToInvalidURL); } void FrameLoader::load(DocumentLoader* newDocumentLoader) @@ -1395,11 +1354,13 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader) if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) { r.setCachePolicy(ReloadIgnoringCacheData); - type = FrameLoadTypeSame; - } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->unreachableURL()) && m_loadType == FrameLoadTypeReload) - type = FrameLoadTypeReload; + type = FrameLoadType::Same; + } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->unreachableURL()) && m_loadType == FrameLoadType::Reload) + type = FrameLoadType::Reload; + else if (m_loadType == FrameLoadType::RedirectWithLockedBackForwardList && !newDocumentLoader->unreachableURL().isEmpty() && newDocumentLoader->substituteData().isValid()) + type = FrameLoadType::RedirectWithLockedBackForwardList; else - type = FrameLoadTypeStandard; + type = FrameLoadType::Standard; if (m_documentLoader) newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding()); @@ -1408,7 +1369,7 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader) // visiting in the history list, we treat it as a reload so the history list // is appropriately maintained. // - // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ... + // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadType::Reload" ... // shouldn't a more explicit type of reload be defined, that means roughly // "load without affecting history" ? if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) { @@ -1417,14 +1378,55 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader) // changed and updateForBackForwardNavigation() will not be called when loading is committed. history().saveDocumentAndScrollState(); - ASSERT(type == FrameLoadTypeStandard); - type = FrameLoadTypeReload; + ASSERT(type == FrameLoadType::Standard); + type = FrameLoadType::Reload; } - loadWithDocumentLoader(newDocumentLoader, type, 0); + loadWithDocumentLoader(newDocumentLoader, type, 0, AllowNavigationToInvalidURL::Yes); +} + +static void logNavigation(MainFrame& frame, const URL& destinationURL, FrameLoadType type) +{ + if (!frame.page()) + return; + + String navigationDescription; + switch (type) { + case FrameLoadType::Standard: + navigationDescription = ASCIILiteral("standard"); + break; + case FrameLoadType::Back: + navigationDescription = ASCIILiteral("back"); + break; + case FrameLoadType::Forward: + navigationDescription = ASCIILiteral("forward"); + break; + case FrameLoadType::IndexedBackForward: + navigationDescription = ASCIILiteral("indexedBackForward"); + break; + case FrameLoadType::Reload: + navigationDescription = ASCIILiteral("reload"); + break; + case FrameLoadType::Same: + navigationDescription = ASCIILiteral("same"); + break; + case FrameLoadType::ReloadFromOrigin: + navigationDescription = ASCIILiteral("reloadFromOrigin"); + break; + case FrameLoadType::Replace: + case FrameLoadType::RedirectWithLockedBackForwardList: + // Not logging those for now. + return; + } + frame.page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::navigationKey(), navigationDescription, ShouldSample::No); +#if ENABLE(PUBLIC_SUFFIX_LIST) + String domain = topPrivatelyControlledDomain(destinationURL.host()); + if (!domain.isEmpty()) + frame.page()->diagnosticLoggingClient().logDiagnosticMessageWithEnhancedPrivacy(DiagnosticLoggingKeys::domainVisitedKey(), domain, ShouldSample::No); +#endif } -void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState) +void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, FormState* formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL) { // Retain because dispatchBeforeLoadEvent may release the last reference to it. Ref<Frame> protect(m_frame); @@ -1436,17 +1438,23 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t ASSERT(m_frame.view()); - if (m_pageDismissalEventBeingDispatched != NoDismissal) + if (!isNavigationAllowed()) return; if (m_frame.document()) m_previousURL = m_frame.document()->url(); + const URL& newURL = loader->request().url(); + + // Log main frame navigation types. + if (m_frame.isMainFrame()) { + logNavigation(static_cast<MainFrame&>(m_frame), newURL, type); + static_cast<MainFrame&>(m_frame).performanceLogging().didReachPointOfInterest(PerformanceLogging::MainFrameLoadStarted); + } + policyChecker().setLoadType(type); - RefPtr<FormState> formState = prpFormState; bool isFormSubmission = formState; - const URL& newURL = loader->request().url(); const String& httpMethod = loader->request().httpMethod(); if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) { @@ -1456,7 +1464,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t oldDocumentLoader->setTriggeringAction(action); oldDocumentLoader->setLastCheckedRequest(ResourceRequest()); policyChecker().stopCheck(); - policyChecker().checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState, [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) { + policyChecker().checkNavigationPolicy(loader->request(), false /* didReceiveRedirectResponse */, oldDocumentLoader.get(), formState, [this] (const ResourceRequest& request, FormState*, bool shouldContinue) { continueFragmentScrollAfterNavigationPolicy(request, shouldContinue); }); return; @@ -1478,13 +1486,13 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t // a new URL, the parent frame shouldn't learn the URL. if (!m_stateMachine.committedFirstRealDocumentLoad() && !ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) { - continueLoadAfterNavigationPolicy(loader->request(), formState, false); + continueLoadAfterNavigationPolicy(loader->request(), formState, false, allowNavigationToInvalidURL); return; } } - policyChecker().checkNavigationPolicy(loader->request(), loader, formState, [this](const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue) { - continueLoadAfterNavigationPolicy(request, formState, shouldContinue); + policyChecker().checkNavigationPolicy(loader->request(), false /* didReceiveRedirectResponse */, loader, formState, [this, allowNavigationToInvalidURL] (const ResourceRequest& request, FormState* formState, bool shouldContinue) { + continueLoadAfterNavigationPolicy(request, formState, shouldContinue, allowNavigationToInvalidURL); }); } @@ -1494,7 +1502,16 @@ void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url) if (!frame) return; - frame->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url); + frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to load local resource: " + url); +} + +void FrameLoader::reportBlockedPortFailed(Frame* frame, const String& url) +{ + ASSERT(!url.isEmpty()); + if (!frame) + return; + + frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to use restricted network port: " + url); } const ResourceRequest& FrameLoader::initialRequest() const @@ -1507,7 +1524,7 @@ bool FrameLoader::willLoadMediaElementURL(URL& url) #if PLATFORM(IOS) // MobileStore depends on the iOS 4.0 era client delegate method because webView:resource:willSendRequest:redirectResponse:fromDataSource // doesn't let them tell when a load request is coming from a media element. See <rdar://problem/8266916> for more details. - if (applicationIsMobileStore()) + if (IOSApplication::isMobileStore()) return m_client.shouldLoadMediaElementURL(url); #endif @@ -1516,7 +1533,7 @@ bool FrameLoader::willLoadMediaElementURL(URL& url) unsigned long identifier; ResourceError error; requestFromDelegate(request, identifier, error); - notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, ResourceResponse(url, String(), -1, String(), String()), 0, -1, -1, error); + notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, ResourceResponse(url, String(), -1, String()), 0, -1, -1, error); url = request.url(); @@ -1538,13 +1555,10 @@ bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader) // case handles well-formed URLs that can't be loaded, and the latter // case handles malformed URLs and unknown schemes. Loading alternate content // at other times behaves like a standard load. - DocumentLoader* compareDocumentLoader = 0; if (policyChecker().delegateIsDecidingNavigationPolicy() || policyChecker().delegateIsHandlingUnimplementablePolicy()) - compareDocumentLoader = m_policyDocumentLoader.get(); - else if (m_delegateIsHandlingProvisionalLoadError) - compareDocumentLoader = m_provisionalDocumentLoader.get(); + return m_policyDocumentLoader && unreachableURL == m_policyDocumentLoader->request().url(); - return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url(); + return unreachableURL == m_provisionalLoadErrorBeingHandledURL; } void FrameLoader::reloadWithOverrideEncoding(const String& encoding) @@ -1561,28 +1575,17 @@ void FrameLoader::reloadWithOverrideEncoding(const String& encoding) // We should ask the user for confirmation in this case. request.setCachePolicy(ReturnCacheDataElseLoad); - RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url())); - setPolicyDocumentLoader(loader.get()); - - loader->setOverrideEncoding(encoding); - - loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); -} + Ref<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url())); + applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, m_documentLoader->shouldOpenExternalURLsPolicyToPropagate()); -void FrameLoader::reloadWithOverrideURL(const URL& overrideUrl, bool endToEndReload) -{ - if (!m_documentLoader) - return; + setPolicyDocumentLoader(loader.ptr()); - if (overrideUrl.isEmpty()) - return; + loader->setOverrideEncoding(encoding); - ResourceRequest request = m_documentLoader->request(); - request.setURL(overrideUrl); - reloadWithRequest(request, endToEndReload); + loadWithDocumentLoader(loader.ptr(), FrameLoadType::Reload, 0, AllowNavigationToInvalidURL::Yes); } -void FrameLoader::reload(bool endToEndReload) +void FrameLoader::reload(bool endToEndReload, bool contentBlockersEnabled) { if (!m_documentLoader) return; @@ -1598,17 +1601,13 @@ void FrameLoader::reload(bool endToEndReload) if (!unreachableURL.isEmpty()) initialRequest.setURL(unreachableURL); - reloadWithRequest(initialRequest, endToEndReload); -} - -void FrameLoader::reloadWithRequest(const ResourceRequest& initialRequest, bool endToEndReload) -{ - ASSERT(m_documentLoader); - // Create a new document loader for the reload, this will become m_documentLoader eventually, // but first it has to be the "policy" document loader, and then the "provisional" document loader. - RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url())); + Ref<DocumentLoader> loader = m_client.createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url())); + applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, m_documentLoader->shouldOpenExternalURLsPolicyToPropagate()); + loader->setUserContentExtensionsEnabled(contentBlockersEnabled); + ResourceRequest& request = loader->request(); // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment. @@ -1616,17 +1615,17 @@ void FrameLoader::reloadWithRequest(const ResourceRequest& initialRequest, bool // If we're about to re-post, set up action so the application can warn the user. if (request.httpMethod() == "POST") - loader->setTriggeringAction(NavigationAction(request, NavigationTypeFormResubmitted)); + loader->setTriggeringAction(NavigationAction(request, NavigationType::FormResubmitted)); loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload, 0); + loadWithDocumentLoader(loader.ptr(), endToEndReload ? FrameLoadType::ReloadFromOrigin : FrameLoadType::Reload, 0, AllowNavigationToInvalidURL::Yes); } void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItemPolicy) { - ASSERT(!m_frame.document() || !m_frame.document()->inPageCache()); - if (m_pageDismissalEventBeingDispatched != NoDismissal) + ASSERT(!m_frame.document() || m_frame.document()->pageCacheState() != Document::InPageCache); + if (!isNavigationAllowed()) return; // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. @@ -1644,7 +1643,7 @@ void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItem // If no new load is in progress, we should clear the provisional item from history // before we call stopLoading. if (clearProvisionalItemPolicy == ShouldClearProvisionalItem) - history().setProvisionalItem(0); + history().setProvisionalItem(nullptr); for (RefPtr<Frame> child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) child->loader().stopAllLoaders(clearProvisionalItemPolicy); @@ -1653,7 +1652,7 @@ void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItem if (m_documentLoader) m_documentLoader->stopLoading(); - setProvisionalDocumentLoader(0); + setProvisionalDocumentLoader(nullptr); m_checkTimer.stop(); @@ -1662,6 +1661,9 @@ void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItem void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete) { + // Calling stopAllLoaders can cause the frame to be deallocated, including the frame loader. + Ref<Frame> protectedFrame(m_frame); + stopAllLoaders(); #if PLATFORM(IOS) @@ -1731,7 +1733,7 @@ void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) return; if (loader) - loader->setFrame(&m_frame); + loader->attachToFrame(m_frame); if (m_policyDocumentLoader && m_policyDocumentLoader != m_provisionalDocumentLoader && m_policyDocumentLoader != m_documentLoader) @@ -1752,7 +1754,8 @@ void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) } void FrameLoader::setState(FrameState newState) -{ +{ + FrameState oldState = m_state; m_state = newState; if (newState == FrameStateProvisional) @@ -1761,12 +1764,14 @@ void FrameLoader::setState(FrameState newState) frameLoadCompleted(); if (m_documentLoader) m_documentLoader->stopRecordingResponses(); + if (m_frame.isMainFrame() && oldState != newState) + static_cast<MainFrame&>(m_frame).didCompleteLoad(); } } void FrameLoader::clearProvisionalLoad() { - setProvisionalDocumentLoader(0); + setProvisionalDocumentLoader(nullptr); m_progressTracker->progressCompleted(); setState(FrameStateComplete); } @@ -1777,42 +1782,24 @@ void FrameLoader::commitProvisionalLoad() Ref<Frame> protect(m_frame); std::unique_ptr<CachedPage> cachedPage; - if (m_loadingFromCachedPage) - cachedPage = pageCache()->take(history().provisionalItem()); + if (m_loadingFromCachedPage && history().provisionalItem()) + cachedPage = PageCache::singleton().take(*history().provisionalItem(), m_frame.page()); LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame.tree().uniqueName().string().utf8().data(), m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : "", pdl ? pdl->url().stringCenterEllipsizedToLength().utf8().data() : "<no provisional DocumentLoader>"); -#if PLATFORM(IOS) - // In the case where we are not navigating to a cached page, and the system is under (speculative) memory pressure, - // we can try to preemptively release some of the pages in the cache. - // FIXME: Right now the capacity is 1 on iOS devices with 256 MB of RAM, so this will always blow away the whole - // page cache. We could still preemptively prune the page cache while navigating to a cached page if capacity > 1. - // See <rdar://problem/11779846> for more details. - if (!cachedPage) { - if (memoryPressureHandler().hasReceivedMemoryPressure()) { - LOG(MemoryPressure, "Pruning page cache because under memory pressure at: %s", __PRETTY_FUNCTION__); - LOG(PageCache, "Pruning page cache to 0 due to memory pressure"); - // Don't cache any page if we are under memory pressure. - pageCache()->pruneToCapacityNow(0); - } else if (systemMemoryLevel() <= memoryLevelThresholdToPrunePageCache) { - LOG(MemoryPressure, "Pruning page cache because system memory level is %d at: %s", systemMemoryLevel(), __PRETTY_FUNCTION__); - LOG(PageCache, "Pruning page cache to %d due to low memory (level %d less or equal to %d threshold)", pageCache()->capacity() / 2, systemMemoryLevel(), memoryLevelThresholdToPrunePageCache); - pageCache()->pruneToCapacityNow(pageCache()->capacity() / 2); - } - } -#endif - willTransitionToCommitted(); - // Check to see if we need to cache the page we are navigating away from into the back/forward cache. - // We are doing this here because we know for sure that a new page is about to be loaded. - HistoryItem* item = history().currentItem(); - if (!m_frame.tree().parent() && pageCache()->canCache(m_frame.page()) && !item->isInPageCache()) - pageCache()->add(item, *m_frame.page()); + if (!m_frame.tree().parent() && history().currentItem()) { + // Check to see if we need to cache the page we are navigating away from into the back/forward cache. + // We are doing this here because we know for sure that a new page is about to be loaded. + PageCache::singleton().addIfCacheable(*history().currentItem(), m_frame.page()); + + WebCore::jettisonExpensiveObjectsOnTopLevelNavigation(); + } - if (m_loadType != FrameLoadTypeReplace) + if (m_loadType != FrameLoadType::Replace) closeOldDataSources(); if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument()) @@ -1822,8 +1809,8 @@ void FrameLoader::commitProvisionalLoad() if (pdl && m_documentLoader) { // Check if the destination page is allowed to access the previous page's timing information. - RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(pdl->request().url()); - m_documentLoader->timing()->setHasSameOriginAsPreviousDocument(securityOrigin->canRequest(m_previousURL)); + Ref<SecurityOrigin> securityOrigin(SecurityOrigin::create(pdl->request().url())); + m_documentLoader->timing().setHasSameOriginAsPreviousDocument(securityOrigin.get().canRequest(m_previousURL)); } // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's @@ -1839,21 +1826,44 @@ void FrameLoader::commitProvisionalLoad() // commit to happen before any changes to viewport arguments and dealing with this there is difficult. m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(true); #endif - prepareForCachedPageRestore(); + willRestoreFromCachedPage(); + + // Start request for the main resource and dispatch didReceiveResponse before the load is committed for + // consistency with all other loads. See https://bugs.webkit.org/show_bug.cgi?id=150927. + ResourceError mainResouceError; + unsigned long mainResourceIdentifier; + ResourceRequest mainResourceRequest(cachedPage->documentLoader()->request()); + requestFromDelegate(mainResourceRequest, mainResourceIdentifier, mainResouceError); + notifier().dispatchDidReceiveResponse(cachedPage->documentLoader(), mainResourceIdentifier, cachedPage->documentLoader()->response()); + + std::optional<HasInsecureContent> hasInsecureContent = cachedPage->cachedMainFrame()->hasInsecureContent(); + + { + // Do not dispatch DOM events as their JavaScript listeners could cause the page to be put + // into the page cache before we have finished restoring it from the page cache. + NoEventDispatchAssertion assertNoEventDispatch; + + // FIXME: This API should be turned around so that we ground CachedPage into the Page. + cachedPage->restore(*m_frame.page()); + } + + dispatchDidCommitLoad(hasInsecureContent); - // FIXME: This API should be turned around so that we ground CachedPage into the Page. - cachedPage->restore(*m_frame.page()); + didRestoreFromCachedPage(); - dispatchDidCommitLoad(); #if PLATFORM(IOS) m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(false); m_frame.page()->chrome().dispatchViewportPropertiesDidChange(m_frame.page()->viewportArguments()); #endif - // If we have a title let the WebView know about it. - StringWithDirection title = m_documentLoader->title(); - if (!title.isNull()) + + auto& title = m_documentLoader->title(); + if (!title.string.isNull()) m_client.dispatchDidReceiveTitle(title); + // Send remaining notifications for the main resource. + notifier().sendRemainingDelegateMessages(m_documentLoader.get(), mainResourceIdentifier, mainResourceRequest, ResourceResponse(), + nullptr, static_cast<int>(m_documentLoader->response().expectedContentLength()), 0, mainResouceError); + checkCompleted(); } else didOpenURL(); @@ -1861,28 +1871,26 @@ void FrameLoader::commitProvisionalLoad() LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame.tree().uniqueName().string().utf8().data(), m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : ""); - if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) + if (m_loadType == FrameLoadType::Standard && m_documentLoader->isClientRedirect()) history().updateForClientRedirect(); if (m_loadingFromCachedPage) { -#if PLATFORM(IOS) // Note, didReceiveDocType is expected to be called for cached pages. See <rdar://problem/5906758> for more details. - if (m_frame.document()->doctype() && m_frame.page()) - m_frame.page()->chrome().didReceiveDocType(&m_frame); -#endif - m_frame.document()->documentDidResumeFromPageCache(); + if (auto* page = m_frame.page()) + page->chrome().didReceiveDocType(m_frame); + m_frame.document()->resume(ActiveDOMObject::PageCache); // Force a layout to update view size and thereby update scrollbars. #if PLATFORM(IOS) - m_client.forceLayoutWithoutRecalculatingStyles(); + if (!m_client.forceLayoutOnRestoreFromPageCache()) + m_frame.view()->forceLayout(); #else m_frame.view()->forceLayout(); #endif - const ResponseVector& responses = m_documentLoader->responses(); - size_t count = responses.size(); - for (size_t i = 0; i < count; i++) { - const ResourceResponse& response = responses[i]; + // Main resource delegates were already sent, so we skip the first response here. + for (unsigned i = 1; i < m_documentLoader->responses().size(); ++i) { + const auto& response = m_documentLoader->responses()[i]; // FIXME: If the WebKit client changes or cancels the request, this is not respected. ResourceError error; unsigned long identifier; @@ -1924,82 +1932,77 @@ void FrameLoader::transitionToCommitted(CachedPage* cachedPage) if (pdl != m_provisionalDocumentLoader) return; - // Nothing else can interupt this commit - set the Provisional->Committed transition in stone if (m_documentLoader) m_documentLoader->stopLoadingSubresources(); if (m_documentLoader) m_documentLoader->stopLoadingPlugIns(); + // Setting our document loader invokes the unload event handler of our child frames. + // Script can do anything. If the script initiates a new load, we need to abandon the + // current load or the two will stomp each other. setDocumentLoader(m_provisionalDocumentLoader.get()); - setProvisionalDocumentLoader(0); - - if (pdl != m_documentLoader) { - ASSERT(m_state == FrameStateComplete); + if (pdl != m_provisionalDocumentLoader) return; - } + setProvisionalDocumentLoader(nullptr); + // Nothing else can interupt this commit - set the Provisional->Committed transition in stone setState(FrameStateCommittedPage); -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - if (m_frame.isMainFrame()) - m_frame.page()->chrome().client().needTouchEvents(false); -#endif - // Handle adding the URL to the back/forward list. DocumentLoader* dl = m_documentLoader.get(); switch (m_loadType) { - case FrameLoadTypeForward: - case FrameLoadTypeBack: - case FrameLoadTypeIndexedBackForward: - if (m_frame.page()) { - // If the first load within a frame is a navigation within a back/forward list that was attached - // without any of the items being loaded then we need to update the history in a similar manner as - // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>). - if (!m_stateMachine.committedFirstRealDocumentLoad() && m_frame.isMainFrame()) - history().updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList); - - history().updateForBackForwardNavigation(); - - // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object - if (history().currentItem() && !cachedPage) - m_pendingStateObject = history().currentItem()->stateObject(); - - // Create a document view for this document, or used the cached view. - if (cachedPage) { - DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); - ASSERT(cachedDocumentLoader); - cachedDocumentLoader->setFrame(&m_frame); - m_client.transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); - } else - m_client.transitionToCommittedForNewPage(); - } - break; - - case FrameLoadTypeReload: - case FrameLoadTypeReloadFromOrigin: - case FrameLoadTypeSame: - case FrameLoadTypeReplace: - history().updateForReload(); - m_client.transitionToCommittedForNewPage(); - break; - - case FrameLoadTypeStandard: - history().updateForStandardLoad(); - if (m_frame.view()) - m_frame.view()->setScrollbarsSuppressed(true); - m_client.transitionToCommittedForNewPage(); - break; - - case FrameLoadTypeRedirectWithLockedBackForwardList: - history().updateForRedirectWithLockedBackForwardList(); - m_client.transitionToCommittedForNewPage(); - break; - - // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is). - // An exception should be thrown if we're in the FrameLoadTypeUninitialized state. - default: - ASSERT_NOT_REACHED(); + case FrameLoadType::Forward: + case FrameLoadType::Back: + case FrameLoadType::IndexedBackForward: + if (m_frame.page()) { + // If the first load within a frame is a navigation within a back/forward list that was attached + // without any of the items being loaded then we need to update the history in a similar manner as + // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>). + if (!m_stateMachine.committedFirstRealDocumentLoad() && m_frame.isMainFrame()) + history().updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList); + + history().updateForBackForwardNavigation(); + + // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object + if (history().currentItem() && !cachedPage) + m_pendingStateObject = history().currentItem()->stateObject(); + + // Create a document view for this document, or used the cached view. + if (cachedPage) { + DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); + ASSERT(cachedDocumentLoader); + cachedDocumentLoader->attachToFrame(m_frame); + m_client.transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); + } else + m_client.transitionToCommittedForNewPage(); + } + break; + + case FrameLoadType::Reload: + case FrameLoadType::ReloadFromOrigin: + case FrameLoadType::Same: + case FrameLoadType::Replace: + history().updateForReload(); + m_client.transitionToCommittedForNewPage(); + break; + + case FrameLoadType::Standard: + history().updateForStandardLoad(); + if (m_frame.view()) + m_frame.view()->setScrollbarsSuppressed(true); + m_client.transitionToCommittedForNewPage(); + break; + + case FrameLoadType::RedirectWithLockedBackForwardList: + history().updateForRedirectWithLockedBackForwardList(); + m_client.transitionToCommittedForNewPage(); + break; + + // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is). + // An exception should be thrown if we're in the FrameLoadTypeUninitialized state. + default: + ASSERT_NOT_REACHED(); } m_documentLoader->writer().setMIMEType(dl->responseMIMEType()); @@ -2027,7 +2030,7 @@ void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgres m_sentRedirectNotification = false; } -void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDate, bool lockBackForwardList) +void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDate, LockBackForwardList lockBackForwardList) { m_client.dispatchWillPerformClientRedirect(url, seconds, fireDate); @@ -2039,7 +2042,7 @@ void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDa // load as part of the original navigation. If we don't have a document loader, we have // no "original" load on which to base a redirect, so we treat the redirect as a normal load. // Loads triggered by JavaScript form submissions never count as quick redirects. - m_quickRedirectComing = (lockBackForwardList || history().currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction; + m_quickRedirectComing = (lockBackForwardList == LockBackForwardList::Yes || history().currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction; } bool FrameLoader::shouldReload(const URL& currentURL, const URL& destinationURL) @@ -2066,7 +2069,7 @@ void FrameLoader::closeOldDataSources() m_client.setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers } -void FrameLoader::prepareForCachedPageRestore() +void FrameLoader::willRestoreFromCachedPage() { ASSERT(!m_frame.tree().parent()); ASSERT(m_frame.page()); @@ -2085,6 +2088,31 @@ void FrameLoader::prepareForCachedPageRestore() } } +void FrameLoader::didRestoreFromCachedPage() +{ + // Dispatching JavaScript events can cause frame destruction. + auto& mainFrame = m_frame.page()->mainFrame(); + Vector<Ref<Frame>> childFrames; + for (auto* child = mainFrame.tree().traverseNextInPostOrderWithWrap(true); child; child = child->tree().traverseNextInPostOrderWithWrap(false)) + childFrames.append(*child); + + for (auto& child : childFrames) { + if (!child->tree().isDescendantOf(&mainFrame)) + continue; + auto* document = child->document(); + if (!document) + continue; + + // FIXME: Update Page Visibility state here. + // https://bugs.webkit.org/show_bug.cgi?id=116770 + document->dispatchPageshowEvent(PageshowEventPersisted); + + auto* historyItem = child->loader().history().currentItem(); + if (historyItem && historyItem->stateObject()) + document->dispatchPopstateEvent(historyItem->stateObject()); + } +} + void FrameLoader::open(CachedFrameBase& cachedFrame) { m_isComplete = false; @@ -2105,7 +2133,7 @@ void FrameLoader::open(CachedFrameBase& cachedFrame) clear(document, true, true, cachedFrame.isMainFrame()); - document->setInPageCache(false); + document->setPageCacheState(Document::NotInPageCache); m_needsClear = true; m_isComplete = false; @@ -2118,13 +2146,15 @@ void FrameLoader::open(CachedFrameBase& cachedFrame) ASSERT(view); view->setWasScrolledByUser(false); - // Use the current ScrollView's frame rect. - if (m_frame.view()) - view->setFrameRect(m_frame.view()->frameRect()); + std::optional<IntRect> previousViewFrameRect = m_frame.view() ? m_frame.view()->frameRect() : std::optional<IntRect>(std::nullopt); m_frame.setView(view); + + // Use the previous ScrollView's frame rect. + if (previousViewFrameRect) + view->setFrameRect(previousViewFrameRect.value()); m_frame.setDocument(document); - document->domWindow()->resumeFromPageCache(); + document->domWindow()->resumeFromDocumentSuspension(); updateFirstPartyForCookies(); @@ -2139,12 +2169,12 @@ bool FrameLoader::isHostedByObjectElement() const bool FrameLoader::isReplacing() const { - return m_loadType == FrameLoadTypeReplace; + return m_loadType == FrameLoadType::Replace; } void FrameLoader::setReplacing() { - m_loadType = FrameLoadTypeReplace; + m_loadType = FrameLoadType::Replace; } bool FrameLoader::subframeIsLoading() const @@ -2177,10 +2207,15 @@ FrameLoadType FrameLoader::loadType() const CachePolicy FrameLoader::subresourceCachePolicy() const { + if (Page* page = m_frame.page()) { + if (page->isResourceCachingDisabled()) + return CachePolicyReload; + } + if (m_isComplete) return CachePolicyVerify; - if (m_loadType == FrameLoadTypeReloadFromOrigin) + if (m_loadType == FrameLoadType::ReloadFromOrigin) return CachePolicyReload; if (Frame* parentFrame = m_frame.tree().parent()) { @@ -2189,18 +2224,24 @@ CachePolicy FrameLoader::subresourceCachePolicy() const return parentCachePolicy; } - if (m_loadType == FrameLoadTypeReload) - return CachePolicyRevalidate; - - const ResourceRequest& request(documentLoader()->request()); -#if PLATFORM(MAC) - if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post") && ResourceRequest::useQuickLookResourceCachingQuirks()) + switch (m_loadType) { + case FrameLoadType::Reload: return CachePolicyRevalidate; -#endif - - if (request.cachePolicy() == ReturnCacheDataElseLoad) + case FrameLoadType::Back: + case FrameLoadType::Forward: + case FrameLoadType::IndexedBackForward: return CachePolicyHistoryBuffer; + case FrameLoadType::ReloadFromOrigin: + ASSERT_NOT_REACHED(); // Already handled above. + return CachePolicyReload; + case FrameLoadType::RedirectWithLockedBackForwardList: + case FrameLoadType::Replace: + case FrameLoadType::Same: + case FrameLoadType::Standard: + return CachePolicyVerify; + } + RELEASE_ASSERT_NOT_REACHED(); return CachePolicyVerify; } @@ -2210,7 +2251,11 @@ void FrameLoader::checkLoadCompleteForThisFrame() switch (m_state) { case FrameStateProvisional: { - if (m_delegateIsHandlingProvisionalLoadError) + // FIXME: Prohibiting any provisional load failures from being sent to clients + // while handling provisional load failures is too heavy. For example, the current + // load will fail to cancel another ongoing load. That might prevent clients' page + // load state being handled properly. + if (!m_provisionalLoadErrorBeingHandledURL.isEmpty()) return; RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; @@ -2232,9 +2277,13 @@ void FrameLoader::checkLoadCompleteForThisFrame() // Only reset if we aren't already going to a new provisional item. bool shouldReset = !history().provisionalItem(); if (!pdl->isLoadingInAPISense() || pdl->isStopping()) { - m_delegateIsHandlingProvisionalLoadError = true; + m_provisionalLoadErrorBeingHandledURL = m_provisionalDocumentLoader->url(); m_client.dispatchDidFailProvisionalLoad(error); - m_delegateIsHandlingProvisionalLoadError = false; +#if ENABLE(CONTENT_FILTERING) + if (auto contentFilter = pdl->contentFilter()) + contentFilter->handleProvisionalLoadFailure(error); +#endif + m_provisionalLoadErrorBeingHandledURL = { }; ASSERT(!pdl->isLoading()); @@ -2275,7 +2324,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() // If the user had a scroll point, scroll to it, overriding the anchor point if any. if (m_frame.page()) { - if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) + if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadType::Reload || m_loadType == FrameLoadType::ReloadFromOrigin) history().restoreScrollPositionAndViewState(); } @@ -2283,31 +2332,55 @@ void FrameLoader::checkLoadCompleteForThisFrame() return; m_progressTracker->progressCompleted(); - if (Page* page = m_frame.page()) { + Page* page = m_frame.page(); + if (page) { if (m_frame.isMainFrame()) - page->resetRelevantPaintedObjectCounter(); + page->didFinishLoad(); } const ResourceError& error = dl->mainDocumentError(); AXObjectCache::AXLoadingEvent loadingEvent; if (!error.isNull()) { + RELEASE_LOG_IF_ALLOWED("checkLoadCompleteForThisFrame: Finished frame load with error (frame = %p, main = %d, isTimeout = %d, isCancellation = %d, errorCode = %d)", &m_frame, m_frame.isMainFrame(), error.isTimeout(), error.isCancellation(), error.errorCode()); m_client.dispatchDidFailLoad(error); loadingEvent = AXObjectCache::AXLoadingFailed; } else { + RELEASE_LOG_IF_ALLOWED("checkLoadCompleteForThisFrame: Finished frame load (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame()); +#if ENABLE(DATA_DETECTION) + auto* document = m_frame.document(); + if (m_frame.settings().dataDetectorTypes() != DataDetectorTypeNone && document) { + if (auto* documentElement = document->documentElement()) { + RefPtr<Range> documentRange = makeRange(firstPositionInNode(documentElement), lastPositionInNode(documentElement)); + m_frame.setDataDetectionResults(DataDetection::detectContentInRange(documentRange, m_frame.settings().dataDetectorTypes(), m_client.dataDetectionContext())); + if (m_frame.isMainFrame()) + m_client.dispatchDidFinishDataDetection(m_frame.dataDetectionResults()); + } + } +#endif m_client.dispatchDidFinishLoad(); loadingEvent = AXObjectCache::AXLoadingFinished; } // Notify accessibility. - if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) - cache->frameLoadingEventNotification(&m_frame, loadingEvent); + if (auto* document = m_frame.document()) { + if (AXObjectCache* cache = document->existingAXObjectCache()) + cache->frameLoadingEventNotification(&m_frame, loadingEvent); + } + + // The above calls to dispatchDidFinishLoad() might have detached the Frame + // from its Page and also might have caused Page to be deleted. + // Don't assume 'page' is still available to use. + if (m_frame.isMainFrame() && m_frame.page()) { + ASSERT(&m_frame.page()->mainFrame() == &m_frame); + m_frame.page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageLoadedKey(), emptyString(), error.isNull() ? DiagnosticLoggingResultPass : DiagnosticLoggingResultFail, ShouldSample::Yes); + } return; } case FrameStateComplete: - m_loadType = FrameLoadTypeStandard; + m_loadType = FrameLoadType::Standard; frameLoadCompleted(); return; } @@ -2324,76 +2397,46 @@ void FrameLoader::continueLoadAfterWillSubmitForm() // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader, // so we need to null check it again. - if (!m_provisionalDocumentLoader) + if (!m_provisionalDocumentLoader) { + RELEASE_LOG_IF_ALLOWED("continueLoadAfterWillSubmitForm: Frame load canceled (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame()); return; + } DocumentLoader* activeDocLoader = activeDocumentLoader(); - if (activeDocLoader && activeDocLoader->isLoadingMainResource()) + if (activeDocLoader && activeDocLoader->isLoadingMainResource()) { + RELEASE_LOG_IF_ALLOWED("continueLoadAfterWillSubmitForm: Main frame already being loaded (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame()); return; + } m_loadingFromCachedPage = false; m_provisionalDocumentLoader->startLoadingMainResource(); } -static URL originatingURLFromBackForwardList(Page* page) -{ - // FIXME: Can this logic be replaced with m_frame.document()->firstPartyForCookies()? - // It has the same meaning of "page a user thinks is the current one". - - URL originalURL; - int backCount = page->backForward().backCount(); - for (int backIndex = 0; backIndex <= backCount; backIndex++) { - // FIXME: At one point we had code here to check a "was user gesture" flag. - // Do we need to restore that logic? - HistoryItem* historyItem = page->backForward().itemAtIndex(-backIndex); - if (!historyItem) - continue; - - originalURL = historyItem->originalURL(); - if (!originalURL.isNull()) - return originalURL; - } - - return URL(); -} - void FrameLoader::setOriginalURLForDownloadRequest(ResourceRequest& request) { + // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies. + // The originalURL is defined as the URL of the page where the download was initiated. URL originalURL; - - // If there is no referrer, assume that the download was initiated directly, so current document is - // completely unrelated to it. See <rdar://problem/5294691>. - // FIXME: Referrer is not sent in many other cases, so we will often miss this important information. - // Find a better way to decide whether the download was unrelated to current document. - if (!request.httpReferrer().isNull()) { - // find the first item in the history that was originated by the user - originalURL = originatingURLFromBackForwardList(m_frame.page()); - } - - if (originalURL.isNull()) - originalURL = request.url(); - - if (!originalURL.protocol().isEmpty() && !originalURL.host().isEmpty()) { - unsigned port = originalURL.port(); - - // Original URL is needed to show the user where a file was downloaded from. We should make a URL that won't result in downloading the file again. - // FIXME: Using host-only URL is a very heavy-handed approach. We should attempt to provide the actual page where the download was initiated from, as a reminder to the user. - String hostOnlyURLString; - if (port) - hostOnlyURLString = makeString(originalURL.protocol(), "://", originalURL.host(), ":", String::number(port)); - else - hostOnlyURLString = makeString(originalURL.protocol(), "://", originalURL.host()); - - // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies. - request.setFirstPartyForCookies(URL(URL(), hostOnlyURLString)); - } + if (m_frame.document()) { + originalURL = m_frame.document()->firstPartyForCookies(); + // If there is no main document URL, it means that this document is newly opened and just for download purpose. + // In this case, we need to set the originalURL to this document's opener's main document URL. + if (originalURL.isEmpty() && opener() && opener()->document()) + originalURL = opener()->document()->firstPartyForCookies(); + } + // If the originalURL is the same as the requested URL, we are processing a download + // initiated directly without a page and do not need to specify the originalURL. + if (originalURL == request.url()) + request.setFirstPartyForCookies(URL()); + else + request.setFirstPartyForCookies(originalURL); } -void FrameLoader::didLayout(LayoutMilestones milestones) +void FrameLoader::didReachLayoutMilestone(LayoutMilestones milestones) { ASSERT(m_frame.isMainFrame()); - m_client.dispatchDidLayout(milestones); + m_client.dispatchDidReachLayoutMilestone(milestones); } void FrameLoader::didFirstLayout() @@ -2428,23 +2471,34 @@ void FrameLoader::frameLoadCompleted() void FrameLoader::detachChildren() { + // detachChildren() will fire the unload event in each subframe and the + // HTML specification states that the parent document's ignore-opens-during-unload counter while + // this event is being fired in its subframes: + // https://html.spec.whatwg.org/multipage/browsers.html#unload-a-document + IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document()); + + // Any subframe inserted by unload event handlers executed in the loop below will not get unloaded + // because we create a copy of the subframes list before looping. Therefore, it would be unsafe to + // allow loading of subframes at this point. + SubframeLoadingDisabler subframeLoadingDisabler(m_frame.document()); + Vector<Ref<Frame>, 16> childrenToDetach; childrenToDetach.reserveInitialCapacity(m_frame.tree().childCount()); for (Frame* child = m_frame.tree().lastChild(); child; child = child->tree().previousSibling()) childrenToDetach.uncheckedAppend(*child); - for (unsigned i = 0; i < childrenToDetach.size(); ++i) - childrenToDetach[i]->loader().detachFromParent(); + for (auto& child : childrenToDetach) + child->loader().detachFromParent(); } -void FrameLoader::closeAndRemoveChild(Frame* child) +void FrameLoader::closeAndRemoveChild(Frame& child) { - child->tree().detachFromParent(); + child.tree().detachFromParent(); - child->setView(0); - if (child->ownerElement() && child->page()) - child->page()->decrementSubframeCount(); - child->willDetachPage(); - child->detachFromPage(); + child.setView(nullptr); + if (child.ownerElement() && child.page()) + child.page()->decrementSubframeCount(); + child.willDetachPage(); + child.detachFromPage(); m_frame.tree().removeChild(child); } @@ -2466,18 +2520,20 @@ void FrameLoader::checkLoadComplete() frames.append(*frame); // To process children before their parents, iterate the vector backwards. - for (unsigned i = frames.size(); i; --i) - frames[i - 1]->loader().checkLoadCompleteForThisFrame(); + for (auto frame = frames.rbegin(); frame != frames.rend(); ++frame) { + if ((*frame)->page()) + (*frame)->loader().checkLoadCompleteForThisFrame(); + } } int FrameLoader::numPendingOrLoadingRequests(bool recurse) const { if (!recurse) - return m_frame.document()->cachedResourceLoader()->requestCount(); + return m_frame.document()->cachedResourceLoader().requestCount(); int count = 0; for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame)) - count += frame->document()->cachedResourceLoader()->requestCount(); + count += frame->document()->cachedResourceLoader().requestCount(); return count; } @@ -2486,44 +2542,53 @@ String FrameLoader::userAgent(const URL& url) const return m_client.userAgent(url); } -void FrameLoader::handledOnloadEvents() +void FrameLoader::dispatchOnloadEvents() { - m_client.dispatchDidHandleOnloadEvents(); + m_client.dispatchDidDispatchOnloadEvents(); if (documentLoader()) - documentLoader()->handledOnloadEvents(); + documentLoader()->dispatchOnloadEvents(); } void FrameLoader::frameDetached() { - stopAllLoaders(); - m_frame.document()->stopActiveDOMObjects(); + // Calling stopAllLoaders() can cause the frame to be deallocated, including the frame loader. + Ref<Frame> protectedFrame(m_frame); + + if (m_frame.document()->pageCacheState() != Document::InPageCache) { + stopAllLoaders(); + m_frame.document()->stopActiveDOMObjects(); + } + detachFromParent(); } void FrameLoader::detachFromParent() { + // Calling stopAllLoaders() can cause the frame to be deallocated, including the frame loader. Ref<Frame> protect(m_frame); closeURL(); history().saveScrollPositionAndViewStateToItem(history().currentItem()); detachChildren(); - // stopAllLoaders() needs to be called after detachChildren(), because detachedChildren() - // will trigger the unload event handlers of any child frames, and those event - // handlers might start a new subresource load in this frame. - stopAllLoaders(); + if (m_frame.document()->pageCacheState() != Document::InPageCache) { + // stopAllLoaders() needs to be called after detachChildren() if the document is not in the page cache, + // because detachedChildren() will trigger the unload event handlers of any child frames, and those event + // handlers might start a new subresource load in this frame. + stopAllLoaders(); + } - InspectorInstrumentation::frameDetachedFromParent(&m_frame); + InspectorInstrumentation::frameDetachedFromParent(m_frame); detachViewsAndDocumentLoader(); m_progressTracker = nullptr; if (Frame* parent = m_frame.tree().parent()) { - parent->loader().closeAndRemoveChild(&m_frame); + parent->loader().closeAndRemoveChild(m_frame); parent->loader().scheduleCheckCompleted(); } else { - m_frame.setView(0); + m_frame.setView(nullptr); m_frame.willDetachPage(); m_frame.detachFromPage(); } @@ -2532,7 +2597,7 @@ void FrameLoader::detachFromParent() void FrameLoader::detachViewsAndDocumentLoader() { m_client.detachedFromParent2(); - setDocumentLoader(0); + setDocumentLoader(nullptr); m_client.detachedFromParent3(); } @@ -2546,29 +2611,19 @@ void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request) // FIXME: Using m_loadType seems wrong for some callers. // If we are only preparing to load the main resource, that is previous load's load type! addExtraFieldsToRequest(request, m_loadType, true); + + // Upgrade-Insecure-Requests should only be added to main resource requests + addHTTPUpgradeInsecureRequestsIfNeeded(request); } -void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource) +ResourceRequestCachePolicy FrameLoader::defaultRequestCachingPolicy(const ResourceRequest& request, FrameLoadType loadType, bool isMainResource) { - // Don't set the cookie policy URL if it's already been set. - // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>). - if (request.firstPartyForCookies().isEmpty()) { - if (mainResource && m_frame.isMainFrame()) - request.setFirstPartyForCookies(request.url()); - else if (Document* document = m_frame.document()) - request.setFirstPartyForCookies(document->firstPartyForCookies()); - } - - // The remaining modifications are only necessary for HTTP and HTTPS. - if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily()) - return; - - applyUserAgent(request); - - if (!mainResource) { + if (m_overrideCachePolicyForTesting) + return m_overrideCachePolicyForTesting.value(); + if (!isMainResource) { if (request.isConditional()) - request.setCachePolicy(ReloadIgnoringCacheData); - else if (documentLoader()->isLoadingInAPISense()) { + return ReloadIgnoringCacheData; + if (documentLoader()->isLoadingInAPISense()) { // If we inherit cache policy from a main resource, we use the DocumentLoader's // original request cache policy for two reasons: // 1. For POST requests, we mutate the cache policy for the main resource, @@ -2579,26 +2634,55 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp ResourceRequestCachePolicy mainDocumentOriginalCachePolicy = documentLoader()->originalRequest().cachePolicy(); // Back-forward navigations try to load main resource from cache only to avoid re-submitting form data, and start over (with a warning dialog) if that fails. // This policy is set on initial request too, but should not be inherited. - ResourceRequestCachePolicy subresourceCachePolicy = (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy; - request.setCachePolicy(subresourceCachePolicy); - } else - request.setCachePolicy(UseProtocolCachePolicy); - + return (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy; + } // FIXME: Other FrameLoader functions have duplicated code for setting cache policy of main request when reloading. // It seems better to manage it explicitly than to hide the logic inside addExtraFieldsToRequest(). - } else if (loadType == FrameLoadTypeReload || loadType == FrameLoadTypeReloadFromOrigin || request.isConditional()) + } else if (loadType == FrameLoadType::Reload || loadType == FrameLoadType::ReloadFromOrigin || request.isConditional()) + return ReloadIgnoringCacheData; + + return UseProtocolCachePolicy; +} + +void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool isMainResource) +{ + // Don't set the cookie policy URL if it's already been set. + // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>). + if (request.firstPartyForCookies().isEmpty()) { + if (isMainResource && m_frame.isMainFrame()) + request.setFirstPartyForCookies(request.url()); + else if (Document* document = m_frame.document()) + request.setFirstPartyForCookies(document->firstPartyForCookies()); + } + + Page* page = frame().page(); + bool hasSpecificCachePolicy = request.cachePolicy() != UseProtocolCachePolicy; + + if (page && page->isResourceCachingDisabled()) { request.setCachePolicy(ReloadIgnoringCacheData); + loadType = FrameLoadType::ReloadFromOrigin; + } else if (!hasSpecificCachePolicy) + request.setCachePolicy(defaultRequestCachingPolicy(request, loadType, isMainResource)); + + // The remaining modifications are only necessary for HTTP and HTTPS. + if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily()) + return; - if (request.cachePolicy() == ReloadIgnoringCacheData) { - if (loadType == FrameLoadTypeReload) - request.setHTTPHeaderField("Cache-Control", "max-age=0"); - else if (loadType == FrameLoadTypeReloadFromOrigin) { - request.setHTTPHeaderField("Cache-Control", "no-cache"); - request.setHTTPHeaderField("Pragma", "no-cache"); + if (!hasSpecificCachePolicy && request.cachePolicy() == ReloadIgnoringCacheData) { + if (loadType == FrameLoadType::Reload) + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"); + else if (loadType == FrameLoadType::ReloadFromOrigin) { + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache"); + request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache"); } } - - if (mainResource) + + if (m_overrideResourceLoadPriorityForTesting) + request.setPriority(m_overrideResourceLoadPriorityForTesting.value()); + + applyUserAgent(request); + + if (isMainResource) request.setHTTPAccept(defaultAcceptHeader); // Make sure we send the Origin header. @@ -2638,16 +2722,25 @@ void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, const String& request.setHTTPOrigin(origin); } -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) +void FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(ResourceRequest& request) +{ + if (request.url().protocolIs("https")) { + // FIXME: Identify HSTS cases and avoid adding the header. <https://bugs.webkit.org/show_bug.cgi?id=157885> + return; + } + + request.setHTTPHeaderField(HTTPHeaderName::UpgradeInsecureRequests, ASCIILiteral("1")); +} + +void FrameLoader::loadPostRequest(const FrameLoadRequest& request, const String& referrer, FrameLoadType loadType, Event* event, FormState* formState) { - RefPtr<FormState> formState = prpFormState; + String frameName = request.frameName(); + LockHistory lockHistory = request.lockHistory(); + AllowNavigationToInvalidURL allowNavigationToInvalidURL = request.allowNavigationToInvalidURL(); + NewFrameOpenerPolicy openerPolicy = request.newFrameOpenerPolicy(); - // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a - // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case - // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest - // from scratch as it did all along. + const ResourceRequest& inRequest = request.resourceRequest(); const URL& url = inRequest.url(); - RefPtr<FormData> formData = inRequest.httpBody(); const String& contentType = inRequest.httpContentType(); String origin = inRequest.httpOrigin(); @@ -2657,28 +2750,31 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String workingResourceRequest.setHTTPReferrer(referrer); workingResourceRequest.setHTTPOrigin(origin); workingResourceRequest.setHTTPMethod("POST"); - workingResourceRequest.setHTTPBody(formData); + workingResourceRequest.setHTTPBody(inRequest.httpBody()); workingResourceRequest.setHTTPContentType(contentType); addExtraFieldsToRequest(workingResourceRequest, loadType, true); - NavigationAction action(workingResourceRequest, loadType, true, event); + if (Document* document = m_frame.document()) + document->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(workingResourceRequest, ContentSecurityPolicy::InsecureRequestType::Load); + + NavigationAction action(workingResourceRequest, loadType, true, event, request.shouldOpenExternalURLsPolicy(), request.downloadAttribute()); if (!frameName.isEmpty()) { // The search for a target frame is done earlier in the case of form submission. if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName)) { - targetFrame->loader().loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); + targetFrame->loader().loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, WTFMove(formState), allowNavigationToInvalidURL); return; } - policyChecker().checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName, [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { - continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue); + policyChecker().checkNewWindowPolicy(action, workingResourceRequest, WTFMove(formState), frameName, [this, allowNavigationToInvalidURL, openerPolicy] (const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { + continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, allowNavigationToInvalidURL, openerPolicy); }); return; } // must grab this now, since this load may stop the previous load and clear this flag bool isRedirect = m_quickRedirectComing; - loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); + loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, WTFMove(formState), allowNavigationToInvalidURL); if (isRedirect) { m_quickRedirectComing = false; if (m_provisionalDocumentLoader) @@ -2686,7 +2782,7 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String } } -unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy clientCredentialPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data) +unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy clientCredentialPolicy, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data) { ASSERT(m_frame.document()); String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), request.url(), outgoingReferrer()); @@ -2706,15 +2802,37 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ ResourceRequest newRequest(initialRequest); requestFromDelegate(newRequest, identifier, error); +#if ENABLE(CONTENT_EXTENSIONS) + if (error.isNull()) { + if (auto* page = m_frame.page()) { + if (m_documentLoader) { + auto blockedStatus = page->userContentProvider().processContentExtensionRulesForLoad(newRequest.url(), ResourceType::Raw, *m_documentLoader); + applyBlockedStatusToRequest(blockedStatus, newRequest); + if (blockedStatus.blockedLoad) { + newRequest = { }; + error = ResourceError(errorDomainWebKitInternal, 0, initialRequest.url(), emptyString()); + response = { }; + data = nullptr; + } + } + } + } +#endif + + m_frame.document()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(newRequest, ContentSecurityPolicy::InsecureRequestType::Load); + if (error.isNull()) { ASSERT(!newRequest.isNull()); - - if (!documentLoader()->applicationCacheHost()->maybeLoadSynchronously(newRequest, error, response, data)) { - platformStrategies()->loaderStrategy()->loadResourceSynchronously(networkingContext(), identifier, newRequest, storedCredentials, clientCredentialPolicy, error, response, data); - documentLoader()->applicationCacheHost()->maybeLoadFallbackSynchronously(newRequest, error, response, data); + + if (!documentLoader()->applicationCacheHost().maybeLoadSynchronously(newRequest, error, response, data)) { + Vector<char> buffer; + platformStrategies()->loaderStrategy()->loadResourceSynchronously(networkingContext(), identifier, newRequest, storedCredentials, clientCredentialPolicy, error, response, buffer); + data = SharedBuffer::adoptVector(buffer); + documentLoader()->applicationCacheHost().maybeLoadFallbackSynchronously(newRequest, error, response, data); + ResourceLoadObserver::sharedObserver().logSubresourceLoading(&m_frame, newRequest, response); } } - notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, data.data(), data.size(), -1, error); + notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, data ? data->data() : nullptr, data ? data->size() : 0, -1, error); return identifier; } @@ -2765,13 +2883,17 @@ void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequ if (!shouldContinue) return; + // Calling stopLoading() on the provisional document loader can cause the underlying + // frame to be deallocated. + Ref<Frame> protectedFrame(m_frame); + // If we have a provisional request for a different document, a fragment scroll should cancel it. if (m_provisionalDocumentLoader && !equalIgnoringFragmentIdentifier(m_provisionalDocumentLoader->request().url(), request.url())) { m_provisionalDocumentLoader->stopLoading(); - setProvisionalDocumentLoader(0); + setProvisionalDocumentLoader(nullptr); } - bool isRedirect = m_quickRedirectComing || policyChecker().loadType() == FrameLoadTypeRedirectWithLockedBackForwardList; + bool isRedirect = m_quickRedirectComing || policyChecker().loadType() == FrameLoadType::RedirectWithLockedBackForwardList; loadInSameDocument(request.url(), 0, !isRedirect); } @@ -2783,10 +2905,10 @@ bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const S // FIXME: What about load types other than Standard and Reload? - return (!isFormSubmission || equalIgnoringCase(httpMethod, "GET")) - && loadType != FrameLoadTypeReload - && loadType != FrameLoadTypeReloadFromOrigin - && loadType != FrameLoadTypeSame + return (!isFormSubmission || equalLettersIgnoringASCIICase(httpMethod, "get")) + && loadType != FrameLoadType::Reload + && loadType != FrameLoadType::ReloadFromOrigin + && loadType != FrameLoadType::Same && !shouldReload(m_frame.document()->url(), url) // We don't want to just scroll if a link from within a // frameset is trying to reload the frameset into _top. @@ -2827,13 +2949,13 @@ bool FrameLoader::shouldClose() bool shouldClose = false; { - NavigationDisablerForBeforeUnload navigationDisabler; + NavigationDisabler navigationDisabler; size_t i; for (i = 0; i < targetFrames.size(); i++) { if (!targetFrames[i]->tree().isDescendantOf(&m_frame)) continue; - if (!targetFrames[i]->loader().handleBeforeUnloadEvent(page->chrome(), this)) + if (!targetFrames[i]->loader().dispatchBeforeUnloadEvent(page->chrome(), this)) break; } @@ -2848,36 +2970,105 @@ bool FrameLoader::shouldClose() return shouldClose; } -bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoaderBeingNavigated) +void FrameLoader::dispatchUnloadEvents(UnloadEventPolicy unloadEventPolicy) +{ + if (!m_frame.document()) + return; + + // We store the frame's page in a local variable because the frame might get detached inside dispatchEvent. + ForbidPromptsScope forbidPrompts(m_frame.page()); + IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document()); + + if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) { + auto* currentFocusedElement = m_frame.document()->focusedElement(); + if (is<HTMLInputElement>(currentFocusedElement)) + downcast<HTMLInputElement>(*currentFocusedElement).endEditing(); + if (m_pageDismissalEventBeingDispatched == PageDismissalType::None) { + if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) { + m_pageDismissalEventBeingDispatched = PageDismissalType::PageHide; + m_frame.document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache), m_frame.document()); + } + + // FIXME: update Page Visibility state here. + // https://bugs.webkit.org/show_bug.cgi?id=116770 + + if (m_frame.document()->pageCacheState() == Document::NotInPageCache) { + Ref<Event> unloadEvent(Event::create(eventNames().unloadEvent, false, false)); + // The DocumentLoader (and thus its LoadTiming) might get destroyed + // while dispatching the event, so protect it to prevent writing the end + // time into freed memory. + RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader; + m_pageDismissalEventBeingDispatched = PageDismissalType::Unload; + if (documentLoader && documentLoader->timing().startTime() && !documentLoader->timing().unloadEventStart() && !documentLoader->timing().unloadEventEnd()) { + auto& timing = documentLoader->timing(); + timing.markUnloadEventStart(); + m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document()); + timing.markUnloadEventEnd(); + } else + m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document()); + } + } + m_pageDismissalEventBeingDispatched = PageDismissalType::None; + m_wasUnloadEventEmitted = true; + } + + // Dispatching the unload event could have made m_frame.document() null. + if (!m_frame.document()) + return; + + if (m_frame.document()->pageCacheState() != Document::NotInPageCache) + return; + + // Don't remove event listeners from a transitional empty document (see bug 28716 for more information). + bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader + && m_frame.document()->isSecureTransitionTo(m_provisionalDocumentLoader->url()); + + if (!keepEventListeners) + m_frame.document()->removeAllEventListeners(); +} + +static bool shouldAskForNavigationConfirmation(Document& document, const BeforeUnloadEvent& event) +{ + bool userDidInteractWithPage = document.topDocument().userDidInteractWithPage(); + // Web pages can request we ask for confirmation before navigating by: + // - Cancelling the BeforeUnloadEvent (modern way) + // - Setting the returnValue attribute on the BeforeUnloadEvent to a non-empty string. + // - Returning a non-empty string from the event handler, which is then set as returnValue + // attribute on the BeforeUnloadEvent. + return userDidInteractWithPage && (event.defaultPrevented() || !event.returnValue().isEmpty()); +} + +bool FrameLoader::dispatchBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoaderBeingNavigated) { DOMWindow* domWindow = m_frame.document()->domWindow(); if (!domWindow) return true; RefPtr<Document> document = m_frame.document(); - if (!document->body()) + if (!document->bodyOrFrameset()) return true; - RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); - m_pageDismissalEventBeingDispatched = BeforeUnloadDismissal; + Ref<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); + m_pageDismissalEventBeingDispatched = PageDismissalType::BeforeUnload; - // We store the frame's page in a local variable because the frame might get detached inside dispatchEvent. - Page* page = m_frame.page(); - page->incrementFrameHandlingBeforeUnloadEventCount(); - domWindow->dispatchEvent(beforeUnloadEvent.get(), domWindow->document()); - page->decrementFrameHandlingBeforeUnloadEventCount(); + { + ForbidPromptsScope forbidPrompts(m_frame.page()); + IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document()); + domWindow->dispatchEvent(beforeUnloadEvent, domWindow->document()); + } - m_pageDismissalEventBeingDispatched = NoDismissal; + m_pageDismissalEventBeingDispatched = PageDismissalType::None; if (!beforeUnloadEvent->defaultPrevented()) document->defaultEventHandler(beforeUnloadEvent.get()); - if (beforeUnloadEvent->returnValue().isNull()) + + if (!shouldAskForNavigationConfirmation(*document, beforeUnloadEvent)) return true; // If the navigating FrameLoader has already shown a beforeunload confirmation panel for the current navigation attempt, // this frame is not allowed to cause another one to be shown. if (frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel) { - document->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Blocked attempt to show multiple beforeunload confirmation dialogs for the same navigation."); + document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Blocked attempt to show multiple beforeunload confirmation dialogs for the same navigation.")); return true; } @@ -2889,8 +3080,8 @@ bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoad Document* parentDocument = parentFrame->document(); if (!parentDocument) return true; - if (!m_frame.document() || !m_frame.document()->securityOrigin()->canAccess(parentDocument->securityOrigin())) { - document->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Blocked attempt to show beforeunload confirmation dialog on behalf of a frame with different security origin. Protocols, domains, and ports must match."); + if (!m_frame.document() || !m_frame.document()->securityOrigin().canAccess(parentDocument->securityOrigin())) { + document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Blocked attempt to show beforeunload confirmation dialog on behalf of a frame with different security origin. Protocols, domains, and ports must match.")); return true; } @@ -2908,10 +3099,10 @@ bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoad frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel = true; String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue()); - return chrome.runBeforeUnloadConfirmPanel(text, &m_frame); + return chrome.runBeforeUnloadConfirmPanel(text, m_frame); } -void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue) +void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, FormState* formState, bool shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL) { // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a // nil policyDataSource because loading the alternate page will have passed @@ -2920,19 +3111,23 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass bool isTargetItem = history().provisionalItem() ? history().provisionalItem()->isTargetItem() : false; - // Two reasons we can't continue: + bool urlIsDisallowed = allowNavigationToInvalidURL == AllowNavigationToInvalidURL::No && !request.url().isValid(); + + // Three reasons we can't continue: // 1) Navigation policy delegate said we can't so request is nil. A primary case of this // is the user responding Cancel to the form repost nag sheet. // 2) User responded Cancel to an alert popped up by the before unload event handler. - bool canContinue = shouldContinue && shouldClose(); + // 3) The request's URL is invalid and navigation to invalid URLs is disallowed. + bool canContinue = shouldContinue && shouldClose() && !urlIsDisallowed; if (!canContinue) { // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we // need to report that the client redirect was cancelled. + // FIXME: The client should be told about ignored non-quick redirects, too. if (m_quickRedirectComing) clientRedirectCancelledOrFinished(false); - setPolicyDocumentLoader(0); + setPolicyDocumentLoader(nullptr); // If the navigation request came from the back/forward menu, and we punt on it, we have the // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity, @@ -2957,22 +3152,20 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass if (!m_frame.page()) return; -#if ENABLE(INSPECTOR) - if (Page* page = m_frame.page()) { - if (m_frame.isMainFrame()) - page->inspectorController().resume(); - } -#endif - setProvisionalDocumentLoader(m_policyDocumentLoader.get()); m_loadType = type; setState(FrameStateProvisional); - setPolicyDocumentLoader(0); + setPolicyDocumentLoader(nullptr); - if (isBackForwardLoadType(type) && history().provisionalItem()->isInPageCache()) { - loadProvisionalItemFromCachedPage(); - return; + if (isBackForwardLoadType(type)) { + auto& diagnosticLoggingClient = m_frame.page()->diagnosticLoggingClient(); + if (history().provisionalItem()->isInPageCache()) { + diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultPass, ShouldSample::Yes); + loadProvisionalItemFromCachedPage(); + return; + } + diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultFail, ShouldSample::Yes); } if (!formState) { @@ -2980,13 +3173,13 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass return; } - m_client.dispatchWillSubmitForm(formState, [this](PolicyAction action) { + m_client.dispatchWillSubmitForm(*formState, [this] (PolicyAction action) { policyChecker().continueLoadAfterWillSubmitForm(action); }); } void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request, - PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) + FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy openerPolicy) { if (!shouldContinue) return; @@ -2996,16 +3189,20 @@ void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& reques if (!mainFrame) return; + mainFrame->loader().forceSandboxFlags(frame->loader().effectiveSandboxFlags()); + if (frameName != "_blank") mainFrame->tree().setName(frameName); mainFrame->page()->setOpenedByDOM(); mainFrame->loader().m_client.dispatchShow(); - if (!m_suppressOpenerInNewFrame) { - mainFrame->loader().setOpener(&frame.get()); + if (openerPolicy == NewFrameOpenerPolicy::Allow) { + mainFrame->loader().setOpener(frame.ptr()); mainFrame->document()->setReferrerPolicy(frame->document()->referrerPolicy()); } - mainFrame->loader().loadWithNavigationAction(request, NavigationAction(request), false, FrameLoadTypeStandard, formState); + + NavigationAction newAction(request, action.shouldOpenExternalURLsPolicy()); + mainFrame->loader().loadWithNavigationAction(request, newAction, LockHistory::No, FrameLoadType::Standard, formState, allowNavigationToInvalidURL); } void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error) @@ -3031,8 +3228,6 @@ void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& i void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, ResourceRequest& newRequest) { - newRequest = ResourceRequest(resource->url()); - Page* page = m_frame.page(); if (!page) return; @@ -3045,14 +3240,14 @@ void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, Resour return; if (!page->areMemoryCacheClientCallsEnabled()) { - InspectorInstrumentation::didLoadResourceFromMemoryCache(page, m_documentLoader.get(), resource); + InspectorInstrumentation::didLoadResourceFromMemoryCache(*page, m_documentLoader.get(), resource); m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->resourceRequest()); m_documentLoader->didTellClientAboutLoad(resource->url()); return; } if (m_client.dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), newRequest, resource->response(), resource->encodedSize())) { - InspectorInstrumentation::didLoadResourceFromMemoryCache(page, m_documentLoader.get(), resource); + InspectorInstrumentation::didLoadResourceFromMemoryCache(*page, m_documentLoader.get(), resource); m_documentLoader->didTellClientAboutLoad(resource->url()); return; } @@ -3060,7 +3255,7 @@ void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, Resour unsigned long identifier; ResourceError error; requestFromDelegate(newRequest, identifier, error); - InspectorInstrumentation::markResourceAsCached(page, identifier); + InspectorInstrumentation::markResourceAsCached(*page, identifier); notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, newRequest, resource->response(), 0, resource->encodedSize(), 0, error); } @@ -3073,8 +3268,6 @@ void FrameLoader::applyUserAgent(ResourceRequest& request) bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const URL& url, unsigned long requestIdentifier) { - FeatureObserver::observe(m_frame.document(), FeatureObserver::XFrameOptions); - Frame& topFrame = m_frame.tree().top(); if (&m_frame == &topFrame) return false; @@ -3083,15 +3276,12 @@ bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, con switch (disposition) { case XFrameOptionsSameOrigin: { - FeatureObserver::observe(m_frame.document(), FeatureObserver::XFrameOptionsSameOrigin); RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); if (!origin->isSameSchemeHostPort(topFrame.document()->securityOrigin())) return true; for (Frame* frame = m_frame.tree().parent(); frame; frame = frame->tree().parent()) { - if (!origin->isSameSchemeHostPort(frame->document()->securityOrigin())) { - FeatureObserver::observe(m_frame.document(), FeatureObserver::XFrameOptionsSameOriginWithBadAncestorChain); + if (!origin->isSameSchemeHostPort(frame->document()->securityOrigin())) break; - } } return false; } @@ -3100,15 +3290,16 @@ bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, con case XFrameOptionsAllowAll: return false; case XFrameOptionsConflict: - m_frame.document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.", requestIdentifier); + m_frame.document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.", requestIdentifier); return true; case XFrameOptionsInvalid: - m_frame.document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + content + "' is not a recognized directive. The header will be ignored.", requestIdentifier); + m_frame.document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + content + "' is not a recognized directive. The header will be ignored.", requestIdentifier); return false; - default: - ASSERT_NOT_REACHED(); + case XFrameOptionsNone: return false; } + ASSERT_NOT_REACHED(); + return false; } void FrameLoader::loadProvisionalItemFromCachedPage() @@ -3121,9 +3312,9 @@ void FrameLoader::loadProvisionalItemFromCachedPage() m_loadingFromCachedPage = true; // Should have timing data from previous time(s) the page was shown. - ASSERT(provisionalLoader->timing()->navigationStart()); + ASSERT(provisionalLoader->timing().startTime()); provisionalLoader->resetTiming(); - provisionalLoader->timing()->markNavigationStart(); + provisionalLoader->timing().markStartTime(); provisionalLoader->setCommitted(true); commitProvisionalLoad(); @@ -3138,52 +3329,35 @@ bool FrameLoader::shouldTreatURLAsSameAsCurrent(const URL& url) const bool FrameLoader::shouldTreatURLAsSrcdocDocument(const URL& url) const { - if (!equalIgnoringCase(url.string(), "about:srcdoc")) + if (!equalLettersIgnoringASCIICase(url.string(), "about:srcdoc")) return false; HTMLFrameOwnerElement* ownerElement = m_frame.ownerElement(); if (!ownerElement) return false; if (!ownerElement->hasTagName(iframeTag)) return false; - return ownerElement->fastHasAttribute(srcdocAttr); + return ownerElement->hasAttributeWithoutSynchronization(srcdocAttr); } Frame* FrameLoader::findFrameForNavigation(const AtomicString& name, Document* activeDocument) { Frame* frame = m_frame.tree().find(name); - // From http://www.whatwg.org/specs/web-apps/current-work/#seamlessLinks: - // - // If the source browsing context is the same as the browsing context - // being navigated, and this browsing context has its seamless browsing - // context flag set, and the browsing context being navigated was not - // chosen using an explicit self-navigation override, then find the - // nearest ancestor browsing context that does not have its seamless - // browsing context flag set, and continue these steps as if that - // browsing context was the one that was going to be navigated instead. - if (frame == &m_frame && name != "_self" && m_frame.document()->shouldDisplaySeamlesslyWithParent()) { - for (Frame* ancestor = &m_frame; ancestor; ancestor = ancestor->tree().parent()) { - if (!ancestor->document()->shouldDisplaySeamlesslyWithParent()) { - frame = ancestor; - break; - } - } - ASSERT(frame != &m_frame); - } - // FIXME: Eventually all callers should supply the actual activeDocument so we can call canNavigate with the right document. if (!activeDocument) activeDocument = m_frame.document(); if (!activeDocument->canNavigate(frame)) - return 0; + return nullptr; return frame; } -void FrameLoader::loadSameDocumentItem(HistoryItem* item) +void FrameLoader::loadSameDocumentItem(HistoryItem& item) { - ASSERT(item->documentSequenceNumber() == history().currentItem()->documentSequenceNumber()); + ASSERT(item.documentSequenceNumber() == history().currentItem()->documentSequenceNumber()); + + Ref<Frame> protect(m_frame); // Save user view state to the current history item here since we don't do a normal load. // FIXME: Does form state need to be saved here too? @@ -3191,10 +3365,10 @@ void FrameLoader::loadSameDocumentItem(HistoryItem* item) if (FrameView* view = m_frame.view()) view->setWasScrolledByUser(false); - history().setCurrentItem(item); + history().setCurrentItem(&item); // loadInSameDocument() actually changes the URL and notifies load delegates of a "fake" load - loadInSameDocument(item->url(), item->stateObject(), false); + loadInSameDocument(item.url(), item.stateObject(), false); // Restore user view state from the current history item here since we don't do a normal load. history().restoreScrollPositionAndViewState(); @@ -3203,38 +3377,47 @@ void FrameLoader::loadSameDocumentItem(HistoryItem* item) // FIXME: This function should really be split into a couple pieces, some of // which should be methods of HistoryController and some of which should be // methods of FrameLoader. -void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy) +void FrameLoader::loadDifferentDocumentItem(HistoryItem& item, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy) { // Remember this item so we can traverse any child items as child frames load - history().setProvisionalItem(item); + history().setProvisionalItem(&item); - if (CachedPage* cachedPage = pageCache()->get(item)) { - loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); + if (CachedPage* cachedPage = PageCache::singleton().get(item, m_frame.page())) { + auto documentLoader = cachedPage->documentLoader(); + m_client.updateCachedDocumentLoader(*documentLoader); + documentLoader->setTriggeringAction(NavigationAction(documentLoader->request(), loadType, false)); + documentLoader->setLastCheckedRequest(ResourceRequest()); + loadWithDocumentLoader(documentLoader, loadType, 0, AllowNavigationToInvalidURL::Yes); return; } - URL itemURL = item->url(); - URL itemOriginalURL = item->originalURL(); + URL itemURL = item.url(); + URL itemOriginalURL = item.originalURL(); URL currentURL; if (documentLoader()) currentURL = documentLoader()->url(); - RefPtr<FormData> formData = item->formData(); + RefPtr<FormData> formData = item.formData(); ResourceRequest request(itemURL); - if (!item->referrer().isNull()) - request.setHTTPReferrer(item->referrer()); - + if (!item.referrer().isNull()) + request.setHTTPReferrer(item.referrer()); + + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicyToApply(m_frame, item.shouldOpenExternalURLsPolicy()); + bool isFormSubmission = false; + Event* event = nullptr; + // If this was a repost that failed the page cache, we might try to repost the form. NavigationAction action; if (formData) { formData->generateFiles(m_frame.document()); request.setHTTPMethod("POST"); - request.setHTTPBody(formData); - request.setHTTPContentType(item->formContentType()); - RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer()); + request.setHTTPBody(WTFMove(formData)); + request.setHTTPContentType(item.formContentType()); + RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item.referrer()); addHTTPOriginIfNeeded(request, securityOrigin->toString()); + addHTTPUpgradeInsecureRequestsIfNeeded(request); // Make sure to add extra fields to the request after the Origin header is added for the FormData case. // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion. @@ -3250,49 +3433,54 @@ void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loa if (cacheLoadPolicy == MayAttemptCacheOnlyLoadForFormSubmissionItem) { request.setCachePolicy(ReturnCacheDataDontLoad); - action = NavigationAction(request, loadType, false); + action = NavigationAction(request, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy); } else { request.setCachePolicy(ReturnCacheDataElseLoad); - action = NavigationAction(request, NavigationTypeFormResubmitted); + action = NavigationAction(request, NavigationType::FormResubmitted, event, shouldOpenExternalURLsPolicy); } } else { switch (loadType) { - case FrameLoadTypeReload: - case FrameLoadTypeReloadFromOrigin: - request.setCachePolicy(ReloadIgnoringCacheData); - break; - case FrameLoadTypeBack: - case FrameLoadTypeForward: - case FrameLoadTypeIndexedBackForward: - // If the first load within a frame is a navigation within a back/forward list that was attached - // without any of the items being loaded then we should use the default caching policy (<rdar://problem/8131355>). - if (m_stateMachine.committedFirstRealDocumentLoad()) - request.setCachePolicy(ReturnCacheDataElseLoad); - break; - case FrameLoadTypeStandard: - case FrameLoadTypeRedirectWithLockedBackForwardList: - break; - case FrameLoadTypeSame: - default: - ASSERT_NOT_REACHED(); + case FrameLoadType::Reload: + case FrameLoadType::ReloadFromOrigin: + request.setCachePolicy(ReloadIgnoringCacheData); + break; + case FrameLoadType::Back: + case FrameLoadType::Forward: + case FrameLoadType::IndexedBackForward: { +#if PLATFORM(COCOA) + bool allowStaleData = true; +#else + bool allowStaleData = !item.wasRestoredFromSession(); +#endif + if (allowStaleData) + request.setCachePolicy(ReturnCacheDataElseLoad); + item.setWasRestoredFromSession(false); + break; + } + case FrameLoadType::Standard: + case FrameLoadType::RedirectWithLockedBackForwardList: + break; + case FrameLoadType::Same: + default: + ASSERT_NOT_REACHED(); } addExtraFieldsToRequest(request, loadType, true); ResourceRequest requestForOriginalURL(request); requestForOriginalURL.setURL(itemOriginalURL); - action = NavigationAction(requestForOriginalURL, loadType, false); + action = NavigationAction(requestForOriginalURL, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy); } - loadWithNavigationAction(request, action, false, loadType, 0); + loadWithNavigationAction(request, action, LockHistory::No, loadType, 0, AllowNavigationToInvalidURL::Yes); } // Loads content into this frame, as specified by history item -void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) +void FrameLoader::loadItem(HistoryItem& item, FrameLoadType loadType) { - m_requestedHistoryItem = item; + m_requestedHistoryItem = &item; HistoryItem* currentItem = history().currentItem(); - bool sameDocumentNavigation = currentItem && item->shouldDoSameDocumentNavigationTo(currentItem); + bool sameDocumentNavigation = currentItem && item.shouldDoSameDocumentNavigationTo(*currentItem); if (sameDocumentNavigation) loadSameDocumentItem(item); @@ -3304,13 +3492,12 @@ void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad() { ASSERT(m_state == FrameStateProvisional); ASSERT(!m_loadingFromCachedPage); - // We only use cache-only loads to avoid resubmitting forms. - ASSERT(isBackForwardLoadType(m_loadType)); + ASSERT(history().provisionalItem()); ASSERT(history().provisionalItem()->formData()); ASSERT(history().provisionalItem() == m_requestedHistoryItem.get()); FrameLoadType loadType = m_loadType; - HistoryItem* item = history().provisionalItem(); + HistoryItem& item = *history().provisionalItem(); stopAllLoaders(ShouldNotClearProvisionalItem); loadDifferentDocumentItem(item, loadType, MayNotAttemptCacheOnlyLoadForFormSubmissionItem); @@ -3319,10 +3506,31 @@ void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad() ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const { ResourceError error = m_client.cancelledError(request); - error.setIsCancellation(true); + error.setType(ResourceError::Type::Cancellation); + return error; +} + +ResourceError FrameLoader::blockedByContentBlockerError(const ResourceRequest& request) const +{ + return m_client.blockedByContentBlockerError(request); +} + +ResourceError FrameLoader::blockedError(const ResourceRequest& request) const +{ + ResourceError error = m_client.blockedError(request); + error.setType(ResourceError::Type::Cancellation); return error; } +#if ENABLE(CONTENT_FILTERING) +ResourceError FrameLoader::blockedByContentFilterError(const ResourceRequest& request) const +{ + ResourceError error = m_client.blockedByContentFilterError(request); + error.setType(ResourceError::Type::General); + return error; +} +#endif + #if PLATFORM(IOS) RetainPtr<CFDictionaryRef> FrameLoader::connectionProperties(ResourceLoader* loader) { @@ -3332,7 +3540,7 @@ RetainPtr<CFDictionaryRef> FrameLoader::connectionProperties(ResourceLoader* loa String FrameLoader::referrer() const { - return m_documentLoader ? m_documentLoader->request().httpReferrer() : ""; + return m_documentLoader ? m_documentLoader->request().httpReferrer() : emptyString(); } void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() @@ -3342,8 +3550,8 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() Vector<Ref<DOMWrapperWorld>> worlds; ScriptController::getAllWorlds(worlds); - for (size_t i = 0; i < worlds.size(); ++i) - dispatchDidClearWindowObjectInWorld(worlds[i].get()); + for (auto& world : worlds) + dispatchDidClearWindowObjectInWorld(world); } void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world) @@ -3353,20 +3561,18 @@ void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world) m_client.dispatchDidClearWindowObjectInWorld(world); -#if ENABLE(INSPECTOR) if (Page* page = m_frame.page()) - page->inspectorController().didClearWindowObjectInWorld(&m_frame, world); -#endif + page->inspectorController().didClearWindowObjectInWorld(m_frame, world); - InspectorInstrumentation::didClearWindowObjectInWorld(&m_frame, world); + InspectorInstrumentation::didClearWindowObjectInWorld(m_frame, world); } void FrameLoader::dispatchGlobalObjectAvailableInAllWorlds() { Vector<Ref<DOMWrapperWorld>> worlds; ScriptController::getAllWorlds(worlds); - for (size_t i = 0; i < worlds.size(); ++i) - m_client.dispatchGlobalObjectAvailable(worlds[i].get()); + for (auto& world : worlds) + m_client.dispatchGlobalObjectAvailable(world); } SandboxFlags FrameLoader::effectiveSandboxFlags() const @@ -3398,31 +3604,24 @@ void FrameLoader::didChangeTitle(DocumentLoader* loader) #endif } -void FrameLoader::didChangeIcons(IconType type) -{ - m_client.dispatchDidChangeIcons(type); -} - -void FrameLoader::dispatchDidCommitLoad() +void FrameLoader::dispatchDidCommitLoad(std::optional<HasInsecureContent> initialHasInsecureContent) { if (m_stateMachine.creatingInitialEmptyDocument()) return; - m_client.dispatchDidCommitLoad(); + m_client.dispatchDidCommitLoad(initialHasInsecureContent); if (m_frame.isMainFrame()) { m_frame.page()->resetSeenPlugins(); m_frame.page()->resetSeenMediaEngines(); } - InspectorInstrumentation::didCommitLoad(&m_frame, m_documentLoader.get()); + InspectorInstrumentation::didCommitLoad(m_frame, m_documentLoader.get()); - if (m_frame.isMainFrame()) { - m_frame.page()->featureObserver()->didCommitLoad(); #if ENABLE(REMOTE_INSPECTOR) + if (m_frame.isMainFrame()) m_frame.page()->remoteInspectorInformationDidChange(); #endif - } } void FrameLoader::tellClientAboutPastMemoryCacheLoads() @@ -3436,9 +3635,8 @@ void FrameLoader::tellClientAboutPastMemoryCacheLoads() Vector<ResourceRequest> pastLoads; m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads); - size_t size = pastLoads.size(); - for (size_t i = 0; i < size; ++i) { - CachedResource* resource = memoryCache()->resourceForRequest(pastLoads[i]); + for (auto& pastLoad : pastLoads) { + CachedResource* resource = MemoryCache::singleton().resourceForRequest(pastLoad, m_frame.page()->sessionID()); // FIXME: These loads, loaded from cache, but now gone from the cache by the time // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client. @@ -3459,12 +3657,8 @@ NetworkingContext* FrameLoader::networkingContext() const void FrameLoader::loadProgressingStatusChanged() { - FrameView* view = m_frame.mainFrame().view(); - if (!view) - return; - - view->updateLayerFlushThrottlingInAllFrames(); - view->adjustTiledBackingCoverage(); + if (auto* view = m_frame.mainFrame().view()) + view->loadProgressingStatusChanged(); } void FrameLoader::forcePageTransitionIfNeeded() @@ -3472,22 +3666,40 @@ void FrameLoader::forcePageTransitionIfNeeded() m_client.forcePageTransitionIfNeeded(); } +void FrameLoader::clearTestingOverrides() +{ + m_overrideCachePolicyForTesting = std::nullopt; + m_overrideResourceLoadPriorityForTesting = std::nullopt; + m_isStrictRawResourceValidationPolicyDisabledForTesting = false; +} + +void FrameLoader::applyShouldOpenExternalURLsPolicyToNewDocumentLoader(DocumentLoader& documentLoader, ShouldOpenExternalURLsPolicy propagatedPolicy) +{ + documentLoader.setShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(m_frame, propagatedPolicy)); +} + +bool FrameLoader::isAlwaysOnLoggingAllowed() const +{ + return frame().isAlwaysOnLoggingAllowed(); +} + bool FrameLoaderClient::hasHTMLView() const { return true; } -PassRefPtr<Frame> createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) +RefPtr<Frame> createWindow(Frame& openerFrame, Frame& lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) { ASSERT(!features.dialog || request.frameName().isEmpty()); + created = false; + if (!request.frameName().isEmpty() && request.frameName() != "_blank") { - if (Frame* frame = lookupFrame->loader().findFrameForNavigation(request.frameName(), openerFrame->document())) { + if (RefPtr<Frame> frame = lookupFrame.loader().findFrameForNavigation(request.frameName(), openerFrame.document())) { if (request.frameName() != "_self") { if (Page* page = frame->page()) page->chrome().focus(); } - created = false; return frame; } } @@ -3495,74 +3707,98 @@ PassRefPtr<Frame> createWindow(Frame* openerFrame, Frame* lookupFrame, const Fra // Sandboxed frames cannot open new auxiliary browsing contexts. if (isDocumentSandboxed(openerFrame, SandboxPopups)) { // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. - openerFrame->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked opening '" + request.resourceRequest().url().stringCenterEllipsizedToLength() + "' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set."); - return 0; + openerFrame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked opening '" + request.resourceRequest().url().stringCenterEllipsizedToLength() + "' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set."); + return nullptr; } // FIXME: Setting the referrer should be the caller's responsibility. FrameLoadRequest requestWithReferrer = request; - String referrer = SecurityPolicy::generateReferrerHeader(openerFrame->document()->referrerPolicy(), request.resourceRequest().url(), openerFrame->loader().outgoingReferrer()); + String referrer = SecurityPolicy::generateReferrerHeader(openerFrame.document()->referrerPolicy(), request.resourceRequest().url(), openerFrame.loader().outgoingReferrer()); if (!referrer.isEmpty()) requestWithReferrer.resourceRequest().setHTTPReferrer(referrer); - FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame->loader().outgoingOrigin()); + FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame.loader().outgoingOrigin()); + FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(requestWithReferrer.resourceRequest()); - Page* oldPage = openerFrame->page(); + Page* oldPage = openerFrame.page(); if (!oldPage) - return 0; + return nullptr; - NavigationAction action(requestWithReferrer.resourceRequest()); - Page* page = oldPage->chrome().createWindow(openerFrame, requestWithReferrer, features, action); + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicyToApply(openerFrame, request.shouldOpenExternalURLsPolicy()); + Page* page = oldPage->chrome().createWindow(openerFrame, requestWithReferrer, features, NavigationAction(requestWithReferrer.resourceRequest(), shouldOpenExternalURLsPolicy)); if (!page) - return 0; + return nullptr; + + RefPtr<Frame> frame = &page->mainFrame(); - page->mainFrame().loader().forceSandboxFlags(openerFrame->document()->sandboxFlags()); + frame->loader().forceSandboxFlags(openerFrame.document()->sandboxFlags()); if (request.frameName() != "_blank") - page->mainFrame().tree().setName(request.frameName()); + frame->tree().setName(request.frameName()); page->chrome().setToolbarsVisible(features.toolBarVisible || features.locationBarVisible); + + if (!frame->page()) + return nullptr; page->chrome().setStatusbarVisible(features.statusBarVisible); + + if (!frame->page()) + return nullptr; page->chrome().setScrollbarsVisible(features.scrollbarsVisible); + + if (!frame->page()) + return nullptr; page->chrome().setMenubarVisible(features.menuBarVisible); + + if (!frame->page()) + return nullptr; page->chrome().setResizable(features.resizable); // 'x' and 'y' specify the location of the window, while 'width' and 'height' // specify the size of the viewport. We can only resize the window, so adjust // for the difference between the window size and the viewport size. -// FIXME: We should reconcile the initialization of viewport arguments between iOS and OpenSource. + // FIXME: We should reconcile the initialization of viewport arguments between iOS and non-IOS. #if !PLATFORM(IOS) FloatSize viewportSize = page->chrome().pageRect().size(); FloatRect windowRect = page->chrome().windowRect(); - if (features.xSet) - windowRect.setX(features.x); - if (features.ySet) - windowRect.setY(features.y); + if (features.x) + windowRect.setX(*features.x); + if (features.y) + windowRect.setY(*features.y); // Zero width and height mean using default size, not minumum one. - if (features.widthSet && features.width) - windowRect.setWidth(features.width + (windowRect.width() - viewportSize.width())); - if (features.heightSet && features.height) - windowRect.setHeight(features.height + (windowRect.height() - viewportSize.height())); + if (features.width && *features.width) + windowRect.setWidth(*features.width + (windowRect.width() - viewportSize.width())); + if (features.height && *features.height) + windowRect.setHeight(*features.height + (windowRect.height() - viewportSize.height())); // Ensure non-NaN values, minimum size as well as being within valid screen area. - FloatRect newWindowRect = DOMWindow::adjustWindowRect(page, windowRect); + FloatRect newWindowRect = DOMWindow::adjustWindowRect(*page, windowRect); + if (!frame->page()) + return nullptr; page->chrome().setWindowRect(newWindowRect); #else // On iOS, width and height refer to the viewport dimensions. ViewportArguments arguments; // Zero width and height mean using default size, not minimum one. - if (features.widthSet && features.width) - arguments.width = features.width; - if (features.heightSet && features.height) - arguments.height = features.height; - page->mainFrame().setViewportArguments(arguments); + if (features.width && *features.width) + arguments.width = *features.width; + if (features.height && *features.height) + arguments.height = *features.height; + frame->setViewportArguments(arguments); #endif + if (!frame->page()) + return nullptr; page->chrome().show(); created = true; - return &page->mainFrame(); + return frame; +} + +bool FrameLoader::shouldSuppressKeyboardInput() const +{ + return m_frame.settings().shouldSuppressKeyboardInputDuringProvisionalNavigation() && m_state == FrameStateProvisional; } } // namespace WebCore diff --git a/Source/WebCore/loader/FrameLoader.h b/Source/WebCore/loader/FrameLoader.h index 4244bf30a..f956e1687 100644 --- a/Source/WebCore/loader/FrameLoader.h +++ b/Source/WebCore/loader/FrameLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) Research In Motion Limited 2009. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. @@ -13,7 +13,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. * @@ -29,25 +29,27 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameLoader_h -#define FrameLoader_h +#pragma once #include "CachePolicy.h" #include "FrameLoaderStateMachine.h" #include "FrameLoaderTypes.h" -#include "IconURL.h" #include "LayoutMilestones.h" #include "MixedContentChecker.h" #include "ResourceHandleTypes.h" #include "ResourceLoadNotifier.h" +#include "ResourceLoaderOptions.h" +#include "ResourceRequestBase.h" #include "SecurityContext.h" #include "Timer.h" #include <wtf/Forward.h> #include <wtf/HashSet.h> +#include <wtf/Optional.h> namespace WebCore { class Archive; +class CachedFrame; class CachedFrameBase; class CachedPage; class CachedResource; @@ -66,21 +68,19 @@ class IconController; class NavigationAction; class NetworkingContext; class Page; -class PageActivityAssertionToken; class PolicyChecker; class ResourceError; class ResourceRequest; class ResourceResponse; -class SecurityOrigin; class SerializedScriptValue; -class StringWithDirection; +class SharedBuffer; class SubframeLoader; class SubstituteData; struct FrameLoadRequest; struct WindowFeatures; -bool isBackForwardLoadType(FrameLoadType); +WEBCORE_EXPORT bool isBackForwardLoadType(FrameLoadType); class FrameLoader { WTF_MAKE_NONCOPYABLE(FrameLoader); @@ -88,7 +88,7 @@ public: FrameLoader(Frame&, FrameLoaderClient&); ~FrameLoader(); - void init(); + WEBCORE_EXPORT void init(); #if PLATFORM(IOS) void initForSynthesizedDocument(const URL&); #endif @@ -102,40 +102,38 @@ public: IconController& icon() const { return *m_icon; } MixedContentChecker& mixedContentChecker() const { return m_mixedContentChecker; } - void prepareForHistoryNavigation(); void setupForReplace(); // FIXME: These are all functions which start loads. We have too many. - void loadURLIntoChildFrame(const URL&, const String& referer, Frame*); - void loadFrameRequest(const FrameLoadRequest&, bool lockHistory, bool lockBackForwardList, // Called by submitForm, calls loadPostRequest and loadURL. - PassRefPtr<Event>, PassRefPtr<FormState>, ShouldSendReferrer); + WEBCORE_EXPORT void loadURLIntoChildFrame(const URL&, const String& referer, Frame*); + WEBCORE_EXPORT void loadFrameRequest(const FrameLoadRequest&, Event*, FormState*); // Called by submitForm, calls loadPostRequest and loadURL. - void load(const FrameLoadRequest&); + WEBCORE_EXPORT void load(const FrameLoadRequest&); #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - void loadArchive(PassRefPtr<Archive>); + WEBCORE_EXPORT void loadArchive(Ref<Archive>&&); #endif - unsigned long loadResourceSynchronously(const ResourceRequest&, StoredCredentials, ClientCredentialPolicy, ResourceError&, ResourceResponse&, Vector<char>& data); + unsigned long loadResourceSynchronously(const ResourceRequest&, StoredCredentials, ClientCredentialPolicy, ResourceError&, ResourceResponse&, RefPtr<SharedBuffer>& data); - void changeLocation(SecurityOrigin*, const URL&, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool refresh = false); - void urlSelected(const URL&, const String& target, PassRefPtr<Event>, bool lockHistory, bool lockBackForwardList, ShouldSendReferrer); - void submitForm(PassRefPtr<FormSubmission>); + void changeLocation(const FrameLoadRequest&); + WEBCORE_EXPORT void urlSelected(const URL&, const String& target, Event*, LockHistory, LockBackForwardList, ShouldSendReferrer, ShouldOpenExternalURLsPolicy, std::optional<NewFrameOpenerPolicy> = std::nullopt, const AtomicString& downloadAttribute = nullAtom); + void submitForm(Ref<FormSubmission>&&); - void reload(bool endToEndReload = false); - void reloadWithOverrideEncoding(const String& overrideEncoding); - void reloadWithOverrideURL(const URL& overrideUrl, bool endToEndReload = false); + WEBCORE_EXPORT void reload(bool endToEndReload = false, bool contentBlockersEnabled = true); + WEBCORE_EXPORT void reloadWithOverrideEncoding(const String& overrideEncoding); void open(CachedFrameBase&); - void loadItem(HistoryItem*, FrameLoadType); + void loadItem(HistoryItem&, FrameLoadType); HistoryItem* requestedHistoryItem() const { return m_requestedHistoryItem.get(); } void retryAfterFailedCacheOnlyMainResourceLoad(); static void reportLocalLoadFailed(Frame*, const String& url); + static void reportBlockedPortFailed(Frame*, const String& url); // FIXME: These are all functions which stop loads. We have too many. - void stopAllLoaders(ClearProvisionalItemPolicy = ShouldClearProvisionalItem); - void stopForUserCancel(bool deferCheckLoadComplete = false); + WEBCORE_EXPORT void stopAllLoaders(ClearProvisionalItemPolicy = ShouldClearProvisionalItem); + WEBCORE_EXPORT void stopForUserCancel(bool deferCheckLoadComplete = false); void stop(); void stopLoading(UnloadEventPolicy); bool closeURL(); @@ -144,14 +142,14 @@ public: void clear(Document* newDocument, bool clearWindowProperties = true, bool clearScriptObjects = true, bool clearFrameView = true); bool isLoading() const; - bool frameHasLoaded() const; + WEBCORE_EXPORT bool frameHasLoaded() const; - int numPendingOrLoadingRequests(bool recurse) const; + WEBCORE_EXPORT int numPendingOrLoadingRequests(bool recurse) const; String referrer() const; - String outgoingReferrer() const; + WEBCORE_EXPORT String outgoingReferrer() const; String outgoingOrigin() const; - DocumentLoader* activeDocumentLoader() const; + WEBCORE_EXPORT DocumentLoader* activeDocumentLoader() const; DocumentLoader* documentLoader() const { return m_documentLoader.get(); } DocumentLoader* policyDocumentLoader() const { return m_policyDocumentLoader.get(); } DocumentLoader* provisionalDocumentLoader() const { return m_provisionalDocumentLoader.get(); } @@ -168,7 +166,12 @@ public: void handleFallbackContent(); - ResourceError cancelledError(const ResourceRequest&) const; + WEBCORE_EXPORT ResourceError cancelledError(const ResourceRequest&) const; + WEBCORE_EXPORT ResourceError blockedByContentBlockerError(const ResourceRequest&) const; + ResourceError blockedError(const ResourceRequest&) const; +#if ENABLE(CONTENT_FILTERING) + ResourceError blockedByContentFilterError(const ResourceRequest&) const; +#endif bool isHostedByObjectElement() const; @@ -177,28 +180,28 @@ public: bool subframeIsLoading() const; void willChangeTitle(DocumentLoader*); void didChangeTitle(DocumentLoader*); - void didChangeIcons(IconType); bool shouldTreatURLAsSrcdocDocument(const URL&) const; - FrameLoadType loadType() const; + WEBCORE_EXPORT FrameLoadType loadType() const; CachePolicy subresourceCachePolicy() const; - void didLayout(LayoutMilestones); + void didReachLayoutMilestone(LayoutMilestones); void didFirstLayout(); void loadedResourceFromMemoryCache(CachedResource*, ResourceRequest& newRequest); void tellClientAboutPastMemoryCacheLoads(); void checkLoadComplete(); - void detachFromParent(); + WEBCORE_EXPORT void detachFromParent(); void detachViewsAndDocumentLoader(); void addExtraFieldsToSubresourceRequest(ResourceRequest&); void addExtraFieldsToMainResourceRequest(ResourceRequest&); static void addHTTPOriginIfNeeded(ResourceRequest&, const String& origin); + static void addHTTPUpgradeInsecureRequestsIfNeeded(ResourceRequest&); FrameLoaderClient& client() const { return m_client; } @@ -211,7 +214,7 @@ public: void receivedFirstData(); - void handledOnloadEvents(); + void dispatchOnloadEvents(); String userAgent(const URL&) const; void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld&); @@ -222,10 +225,10 @@ public: void forceSandboxFlags(SandboxFlags flags) { m_forcedSandboxFlags |= flags; } SandboxFlags effectiveSandboxFlags() const; - bool checkIfFormActionAllowedByCSP(const URL&) const; + bool checkIfFormActionAllowedByCSP(const URL&, bool didReceiveRedirectResponse) const; - Frame* opener(); - void setOpener(Frame*); + WEBCORE_EXPORT Frame* opener(); + WEBCORE_EXPORT void setOpener(Frame*); void resetMultipleFormSubmissionProtection(); @@ -239,16 +242,16 @@ public: void finishedParsing(); void checkCompleted(); - bool isComplete() const; + WEBCORE_EXPORT bool isComplete() const; void commitProvisionalLoad(); void setLoadsSynchronously(bool loadsSynchronously) { m_loadsSynchronously = loadsSynchronously; } bool loadsSynchronously() const { return m_loadsSynchronously; } - FrameLoaderStateMachine* stateMachine() const { return &m_stateMachine; } + FrameLoaderStateMachine& stateMachine() { return m_stateMachine; } - Frame* findFrameForNavigation(const AtomicString& name, Document* activeDocument = 0); + WEBCORE_EXPORT Frame* findFrameForNavigation(const AtomicString& name, Document* activeDocument = nullptr); void applyUserAgent(ResourceRequest&); @@ -256,7 +259,7 @@ public: void completed(); bool allAncestorsAreComplete() const; // including this - void clientRedirected(const URL&, double delay, double fireDate, bool lockBackForwardList); + void clientRedirected(const URL&, double delay, double fireDate, LockBackForwardList); void clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress); // FIXME: This is public because this asynchronous callback from the FrameLoaderClient @@ -264,27 +267,18 @@ public: // introduce a proper callback type for this function, we should make it private again. void continueLoadAfterWillSubmitForm(); - void setOriginalURLForDownloadRequest(ResourceRequest&); - - bool suppressOpenerInNewFrame() const { return m_suppressOpenerInNewFrame; } - - static ObjectContentType defaultObjectContentType(const URL&, const String& mimeType, bool shouldPreferPlugInsForImages); + WEBCORE_EXPORT void setOriginalURLForDownloadRequest(ResourceRequest&); bool quickRedirectComing() const { return m_quickRedirectComing; } - bool shouldClose(); + WEBCORE_EXPORT bool shouldClose(); void started(); - enum PageDismissalType { - NoDismissal = 0, - BeforeUnloadDismissal = 1, - PageHideDismissal = 2, - UnloadDismissal = 3 - }; + enum class PageDismissalType { None, BeforeUnload, PageHide, Unload }; PageDismissalType pageDismissalEventBeingDispatched() const { return m_pageDismissalEventBeingDispatched; } - NetworkingContext* networkingContext() const; + WEBCORE_EXPORT NetworkingContext* networkingContext() const; void loadProgressingStatusChanged(); @@ -292,6 +286,19 @@ public: void forcePageTransitionIfNeeded(); + void setOverrideCachePolicyForTesting(ResourceRequestCachePolicy policy) { m_overrideCachePolicyForTesting = policy; } + void setOverrideResourceLoadPriorityForTesting(ResourceLoadPriority priority) { m_overrideResourceLoadPriorityForTesting = priority; } + void setStrictRawResourceValidationPolicyDisabledForTesting(bool disabled) { m_isStrictRawResourceValidationPolicyDisabledForTesting = disabled; } + bool isStrictRawResourceValidationPolicyDisabledForTesting() { return m_isStrictRawResourceValidationPolicyDisabledForTesting; } + + WEBCORE_EXPORT void clearTestingOverrides(); + + const URL& provisionalLoadErrorBeingHandledURL() const { return m_provisionalLoadErrorBeingHandledURL; } + void setProvisionalLoadErrorBeingHandledURL(const URL& url) { m_provisionalLoadErrorBeingHandledURL = url; } + + bool isAlwaysOnLoggingAllowed() const; + bool shouldSuppressKeyboardInput() const; + private: enum FormSubmissionCacheLoadPolicy { MayAttemptCacheOnlyLoadForFormSubmissionItem, @@ -300,17 +307,18 @@ private: bool allChildrenAreComplete() const; // immediate children, not all descendants - void checkTimerFired(Timer<FrameLoader>&); - - void loadSameDocumentItem(HistoryItem*); - void loadDifferentDocumentItem(HistoryItem*, FrameLoadType, FormSubmissionCacheLoadPolicy); - + void checkTimerFired(); + + void loadSameDocumentItem(HistoryItem&); + void loadDifferentDocumentItem(HistoryItem&, FrameLoadType, FormSubmissionCacheLoadPolicy); + void loadProvisionalItemFromCachedPage(); void updateFirstPartyForCookies(); void setFirstPartyForCookies(const URL&); - + void addExtraFieldsToRequest(ResourceRequest&, FrameLoadType, bool isMainResource); + ResourceRequestCachePolicy defaultRequestCachingPolicy(const ResourceRequest&, FrameLoadType, bool isMainResource); void clearProvisionalLoad(); void transitionToCommitted(CachedPage*); @@ -318,10 +326,11 @@ private: SubstituteData defaultSubstituteDataForURL(const URL&); - bool handleBeforeUnloadEvent(Chrome&, FrameLoader* frameLoaderBeingNavigated); + bool dispatchBeforeUnloadEvent(Chrome&, FrameLoader* frameLoaderBeingNavigated); + void dispatchUnloadEvents(UnloadEventPolicy); - void continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); - void continueLoadAfterNewWindowPolicy(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, bool shouldContinue); + void continueLoadAfterNavigationPolicy(const ResourceRequest&, FormState*, bool shouldContinue, AllowNavigationToInvalidURL); + void continueLoadAfterNewWindowPolicy(const ResourceRequest&, FormState*, const String& frameName, const NavigationAction&, bool shouldContinue, AllowNavigationToInvalidURL, NewFrameOpenerPolicy); void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue); bool shouldPerformFragmentNavigation(bool isFormSubmission, const String& httpMethod, FrameLoadType, const URL&); @@ -336,35 +345,31 @@ private: void setState(FrameState); void closeOldDataSources(); - void prepareForCachedPageRestore(); + void willRestoreFromCachedPage(); + void didRestoreFromCachedPage(); bool shouldReloadToHandleUnreachableURL(DocumentLoader*); - void dispatchDidCommitLoad(); - - void urlSelected(const FrameLoadRequest&, PassRefPtr<Event>, bool lockHistory, bool lockBackForwardList, ShouldSendReferrer, ShouldReplaceDocumentIfJavaScriptURL); + void dispatchDidCommitLoad(std::optional<HasInsecureContent> initialHasInsecureContent); - void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); // Calls continueLoadAfterNavigationPolicy - void load(DocumentLoader*); // Calls loadWithDocumentLoader + void urlSelected(const FrameLoadRequest&, Event*); - void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader - bool lockHistory, FrameLoadType, PassRefPtr<FormState>); + void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, FormState*, AllowNavigationToInvalidURL); // Calls continueLoadAfterNavigationPolicy + void load(DocumentLoader*); // Calls loadWithDocumentLoader - void loadPostRequest(const ResourceRequest&, const String& referrer, // Called by loadFrameRequest, calls loadWithNavigationAction - const String& frameName, bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>); - void loadURL(const URL&, const String& referrer, const String& frameName, // Called by loadFrameRequest, calls loadWithNavigationAction or dispatches to navigation policy delegate - bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>); + void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, LockHistory, FrameLoadType, FormState*, AllowNavigationToInvalidURL); // Calls loadWithDocumentLoader - void reloadWithRequest(const ResourceRequest&, bool endToEndReload); + void loadPostRequest(const FrameLoadRequest&, const String& referrer, FrameLoadType, Event*, FormState*); + void loadURL(const FrameLoadRequest&, const String& referrer, FrameLoadType, Event*, FormState*); bool shouldReload(const URL& currentURL, const URL& destinationURL); void requestFromDelegate(ResourceRequest&, unsigned long& identifier, ResourceError&); - void detachChildren(); - void closeAndRemoveChild(Frame*); + WEBCORE_EXPORT void detachChildren(); + void closeAndRemoveChild(Frame&); - void loadInSameDocument(const URL&, PassRefPtr<SerializedScriptValue> stateObject, bool isNewNavigation); + void loadInSameDocument(const URL&, SerializedScriptValue* stateObject, bool isNewNavigation); void prepareForLoadStart(); void provisionalLoadStarted(); @@ -380,6 +385,10 @@ private: void dispatchGlobalObjectAvailableInAllWorlds(); + void applyShouldOpenExternalURLsPolicyToNewDocumentLoader(DocumentLoader&, ShouldOpenExternalURLsPolicy propagatedPolicy); + + bool isNavigationAllowed() const; + Frame& m_frame; FrameLoaderClient& m_client; @@ -405,7 +414,7 @@ private: RefPtr<DocumentLoader> m_provisionalDocumentLoader; RefPtr<DocumentLoader> m_policyDocumentLoader; - bool m_delegateIsHandlingProvisionalLoadError; + URL m_provisionalLoadErrorBeingHandledURL; bool m_quickRedirectComing; bool m_sentRedirectNotification; @@ -417,7 +426,8 @@ private: bool m_didCallImplicitClose; bool m_wasUnloadEventEmitted; - PageDismissalType m_pageDismissalEventBeingDispatched; + + PageDismissalType m_pageDismissalEventBeingDispatched { PageDismissalType::None }; bool m_isComplete; RefPtr<SerializedScriptValue> m_pendingStateObject; @@ -426,7 +436,7 @@ private: URL m_submittedFormURL; - Timer<FrameLoader> m_checkTimer; + Timer m_checkTimer; bool m_shouldCallCheckCompleted; bool m_shouldCallCheckLoadComplete; @@ -434,7 +444,6 @@ private: HashSet<Frame*> m_openedFrames; bool m_loadingFromCachedPage; - bool m_suppressOpenerInNewFrame; bool m_currentNavigationHasShownBeforeUnloadConfirmPanel; @@ -444,9 +453,12 @@ private: RefPtr<FrameNetworkingContext> m_networkingContext; + std::optional<ResourceRequestCachePolicy> m_overrideCachePolicyForTesting; + std::optional<ResourceLoadPriority> m_overrideResourceLoadPriorityForTesting; + bool m_isStrictRawResourceValidationPolicyDisabledForTesting { false }; + URL m_previousURL; RefPtr<HistoryItem> m_requestedHistoryItem; - std::unique_ptr<PageActivityAssertionToken> m_activityAssertion; }; // This function is called by createWindow() in JSDOMWindowBase.cpp, for example, for @@ -456,8 +468,6 @@ private: // // FIXME: Consider making this function part of an appropriate class (not FrameLoader) // and moving it to a more appropriate location. -PassRefPtr<Frame> createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest&, const WindowFeatures&, bool& created); +RefPtr<Frame> createWindow(Frame& openerFrame, Frame& lookupFrame, const FrameLoadRequest&, const WindowFeatures&, bool& created); } // namespace WebCore - -#endif // FrameLoader_h diff --git a/Source/WebCore/loader/FrameLoaderClient.h b/Source/WebCore/loader/FrameLoaderClient.h index c81bdba41..66e386e52 100644 --- a/Source/WebCore/loader/FrameLoaderClient.h +++ b/Source/WebCore/loader/FrameLoaderClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2012 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. * @@ -27,18 +27,22 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameLoaderClient_h -#define FrameLoaderClient_h +#pragma once #include "FrameLoaderTypes.h" -#include "IconURL.h" #include "LayoutMilestones.h" +#include "LinkIcon.h" #include "ResourceLoadPriority.h" #include <functional> #include <wtf/Forward.h> #include <wtf/Vector.h> +#include <wtf/text/WTFString.h> -#if PLATFORM(MAC) +#if ENABLE(CONTENT_FILTERING) +#include "ContentFilterUnblockHandler.h" +#endif + +#if PLATFORM(COCOA) #ifdef __OBJC__ #import <Foundation/Foundation.h> typedef id RemoteAXObjectRef; @@ -47,296 +51,311 @@ typedef void* RemoteAXObjectRef; #endif #endif -typedef class _jobject* jobject; - -#if PLATFORM(MAC) && !defined(__OBJC__) -class NSCachedURLResponse; -class NSView; +#if PLATFORM(COCOA) +OBJC_CLASS NSArray; +OBJC_CLASS NSCachedURLResponse; +OBJC_CLASS NSDictionary; +OBJC_CLASS NSView; #endif namespace WebCore { - class AuthenticationChallenge; - class CachedFrame; - class CachedResourceRequest; - class Color; - class DOMWindowExtension; - class DOMWrapperWorld; - class DocumentLoader; - class Element; - class FormState; - class Frame; - class FrameLoader; - class FrameNetworkingContext; - class HistoryItem; - class HTMLAppletElement; - class HTMLFormElement; - class HTMLFrameOwnerElement; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - class HTMLMediaElement; -#endif - class HTMLPlugInElement; - class IntSize; - class URL; - class MessageEvent; - class NavigationAction; - class Page; - class ProtectionSpace; - class PluginView; - class PolicyChecker; - class ResourceError; - class ResourceHandle; - class ResourceRequest; - class ResourceResponse; -#if ENABLE(MEDIA_STREAM) - class RTCPeerConnectionHandler; +class AuthenticationChallenge; +class CachedFrame; +class CachedResourceRequest; +class Color; +class DOMWindowExtension; +class DOMWrapperWorld; +class DocumentLoader; +class Element; +class FormState; +class Frame; +class FrameLoader; +class FrameNetworkingContext; +class HTMLAppletElement; +class HTMLFormElement; +class HTMLFrameOwnerElement; +class HTMLPlugInElement; +class HistoryItem; +class IntSize; +class MessageEvent; +class NavigationAction; +class Page; +class PluginViewBase; +class PolicyChecker; +class ProtectionSpace; +class QuickLookHandleClient; +class RTCPeerConnectionHandler; +class ResourceError; +class ResourceHandle; +class ResourceRequest; +class ResourceResponse; +class SecurityOrigin; +class SessionID; +class SharedBuffer; +class SubstituteData; +class URL; +class Widget; + +struct StringWithDirection; + +typedef std::function<void (PolicyAction)> FramePolicyFunction; + +class WEBCORE_EXPORT FrameLoaderClient { +public: + // An inline function cannot be the first non-abstract virtual function declared + // in the class as it results in the vtable being generated as a weak symbol. + // This hurts performance (in Mac OS X at least, when loadig frameworks), so we + // don't want to do it in WebKit. + virtual bool hasHTMLView() const; + + virtual ~FrameLoaderClient() { } + + virtual void frameLoaderDestroyed() = 0; + + virtual bool hasWebView() const = 0; // mainly for assertions + + virtual void makeRepresentation(DocumentLoader*) = 0; + +#if PLATFORM(IOS) + // Returns true if the client forced the layout. + virtual bool forceLayoutOnRestoreFromPageCache() = 0; #endif - class SecurityOrigin; - class SharedBuffer; - class SocketStreamHandle; - class StringWithDirection; - class SubstituteData; - class Widget; - - typedef std::function<void (PolicyAction)> FramePolicyFunction; + virtual void forceLayoutForNonHTML() = 0; - class FrameLoaderClient { - public: - // An inline function cannot be the first non-abstract virtual function declared - // in the class as it results in the vtable being generated as a weak symbol. - // This hurts performance (in Mac OS X at least, when loadig frameworks), so we - // don't want to do it in WebKit. - virtual bool hasHTMLView() const; + virtual void setCopiesOnScroll() = 0; - virtual ~FrameLoaderClient() { } + virtual void detachedFromParent2() = 0; + virtual void detachedFromParent3() = 0; - virtual void frameLoaderDestroyed() = 0; + virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) = 0; - virtual bool hasWebView() const = 0; // mainly for assertions + virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse) = 0; + virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) = 0; + virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(DocumentLoader*, unsigned long identifier, const ProtectionSpace&) = 0; +#endif - virtual void makeRepresentation(DocumentLoader*) = 0; - virtual void forceLayout() = 0; #if PLATFORM(IOS) - virtual void forceLayoutWithoutRecalculatingStyles() = 0; + virtual RetainPtr<CFDictionaryRef> connectionProperties(DocumentLoader*, unsigned long identifier) = 0; #endif - virtual void forceLayoutForNonHTML() = 0; - virtual void setCopiesOnScroll() = 0; + virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&) = 0; + virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int dataLength) = 0; + virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier) = 0; + virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&) = 0; + virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) = 0; + + virtual void dispatchDidDispatchOnloadEvents() = 0; + virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() = 0; + virtual void dispatchDidChangeProvisionalURL() { } + virtual void dispatchDidCancelClientRedirect() = 0; + virtual void dispatchWillPerformClientRedirect(const URL&, double interval, double fireDate) = 0; + virtual void dispatchDidNavigateWithinPage() { } + virtual void dispatchDidChangeLocationWithinPage() = 0; + virtual void dispatchDidPushStateWithinPage() = 0; + virtual void dispatchDidReplaceStateWithinPage() = 0; + virtual void dispatchDidPopStateWithinPage() = 0; + virtual void dispatchWillClose() = 0; + virtual void dispatchDidReceiveIcon() = 0; + virtual void dispatchDidStartProvisionalLoad() = 0; + virtual void dispatchDidReceiveTitle(const StringWithDirection&) = 0; + virtual void dispatchDidCommitLoad(std::optional<HasInsecureContent>) = 0; + virtual void dispatchDidFailProvisionalLoad(const ResourceError&) = 0; + virtual void dispatchDidFailLoad(const ResourceError&) = 0; + virtual void dispatchDidFinishDocumentLoad() = 0; + virtual void dispatchDidFinishLoad() = 0; +#if ENABLE(DATA_DETECTION) + virtual void dispatchDidFinishDataDetection(NSArray *detectionResults) = 0; +#endif - virtual void detachedFromParent2() = 0; - virtual void detachedFromParent3() = 0; + virtual void dispatchDidLayout() { } + virtual void dispatchDidReachLayoutMilestone(LayoutMilestones) { } - virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) = 0; + virtual Frame* dispatchCreatePage(const NavigationAction&) = 0; + virtual void dispatchShow() = 0; - virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse) = 0; - virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) = 0; - virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; - virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) - virtual bool canAuthenticateAgainstProtectionSpace(DocumentLoader*, unsigned long identifier, const ProtectionSpace&) = 0; -#endif + virtual void dispatchDecidePolicyForResponse(const ResourceResponse&, const ResourceRequest&, FramePolicyFunction) = 0; + virtual void dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, FormState*, const String& frameName, FramePolicyFunction) = 0; + virtual void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, FormState*, FramePolicyFunction) = 0; + virtual void cancelPolicyCheck() = 0; -#if PLATFORM(IOS) - virtual RetainPtr<CFDictionaryRef> connectionProperties(DocumentLoader*, unsigned long identifier) = 0; -#endif + virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0; - virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&) = 0; - virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int dataLength) = 0; - virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier) = 0; - virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&) = 0; - virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) = 0; - - virtual void dispatchDidHandleOnloadEvents() = 0; - virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() = 0; - virtual void dispatchDidCancelClientRedirect() = 0; - virtual void dispatchWillPerformClientRedirect(const URL&, double interval, double fireDate) = 0; - virtual void dispatchDidNavigateWithinPage() { } - virtual void dispatchDidChangeLocationWithinPage() = 0; - virtual void dispatchDidPushStateWithinPage() = 0; - virtual void dispatchDidReplaceStateWithinPage() = 0; - virtual void dispatchDidPopStateWithinPage() = 0; - virtual void dispatchWillClose() = 0; - virtual void dispatchDidReceiveIcon() = 0; - virtual void dispatchDidStartProvisionalLoad() = 0; - virtual void dispatchDidReceiveTitle(const StringWithDirection&) = 0; - virtual void dispatchDidChangeIcons(IconType) = 0; - virtual void dispatchDidCommitLoad() = 0; - virtual void dispatchDidFailProvisionalLoad(const ResourceError&) = 0; - virtual void dispatchDidFailLoad(const ResourceError&) = 0; - virtual void dispatchDidFinishDocumentLoad() = 0; - virtual void dispatchDidFinishLoad() = 0; - - virtual void dispatchDidLayout() { } - virtual void dispatchDidLayout(LayoutMilestones) { } - - virtual Frame* dispatchCreatePage(const NavigationAction&) = 0; - virtual void dispatchShow() = 0; - - virtual void dispatchDecidePolicyForResponse(const ResourceResponse&, const ResourceRequest&, FramePolicyFunction) = 0; - virtual void dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, FramePolicyFunction) = 0; - virtual void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, FramePolicyFunction) = 0; - virtual void cancelPolicyCheck() = 0; - - virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0; - - virtual void dispatchWillRequestResource(CachedResourceRequest*) { } - - virtual void dispatchWillSendSubmitEvent(PassRefPtr<FormState>) = 0; - virtual void dispatchWillSubmitForm(PassRefPtr<FormState>, FramePolicyFunction) = 0; - - virtual void revertToProvisionalState(DocumentLoader*) = 0; - virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) = 0; - - virtual void setMainFrameDocumentReady(bool) = 0; - - virtual void startDownload(const ResourceRequest&, const String& suggestedName = String()) = 0; - - virtual void willChangeTitle(DocumentLoader*) = 0; - virtual void didChangeTitle(DocumentLoader*) = 0; - - virtual void committedLoad(DocumentLoader*, const char*, int) = 0; - virtual void finishedLoading(DocumentLoader*) = 0; - - virtual void updateGlobalHistory() = 0; - virtual void updateGlobalHistoryRedirectLinks() = 0; - - virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0; - virtual void updateGlobalHistoryItemForPage() { } - - // This frame has set its opener to null, disowning it for the lifetime of the frame. - // See http://html.spec.whatwg.org/#dom-opener. - // FIXME: JSC should allow disowning opener. - <https://bugs.webkit.org/show_bug.cgi?id=103913>. - virtual void didDisownOpener() { } - - // This frame has displayed inactive content (such as an image) from an - // insecure source. Inactive content cannot spread to other frames. - virtual void didDisplayInsecureContent() = 0; - - // The indicated security origin has run active content (such as a - // script) from an insecure source. Note that the insecure content can - // spread to other frames in the same origin. - virtual void didRunInsecureContent(SecurityOrigin*, const URL&) = 0; - virtual void didDetectXSS(const URL&, bool didBlockEntirePage) = 0; - - virtual ResourceError cancelledError(const ResourceRequest&) = 0; - virtual ResourceError blockedError(const ResourceRequest&) = 0; - virtual ResourceError cannotShowURLError(const ResourceRequest&) = 0; - virtual ResourceError interruptedForPolicyChangeError(const ResourceRequest&) = 0; - - virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) = 0; - virtual ResourceError fileDoesNotExistError(const ResourceResponse&) = 0; - virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) = 0; - - virtual bool shouldFallBack(const ResourceError&) = 0; - - virtual bool canHandleRequest(const ResourceRequest&) const = 0; - virtual bool canShowMIMEType(const String& MIMEType) const = 0; - virtual bool canShowMIMETypeAsHTML(const String& MIMEType) const = 0; - virtual bool representationExistsForURLScheme(const String& URLScheme) const = 0; - virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const = 0; - - virtual void frameLoadCompleted() = 0; - virtual void saveViewStateToItem(HistoryItem*) = 0; - virtual void restoreViewState() = 0; - virtual void provisionalLoadStarted() = 0; - virtual void didFinishLoad() = 0; - virtual void prepareForDataSourceReplacement() = 0; - - virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&) = 0; - virtual void setTitle(const StringWithDirection&, const URL&) = 0; - - virtual String userAgent(const URL&) = 0; - - virtual void savePlatformDataToCachedFrame(CachedFrame*) = 0; - virtual void transitionToCommittedFromCachedFrame(CachedFrame*) = 0; -#if PLATFORM(IOS) - virtual void didRestoreFrameHierarchyForCachedFrame() = 0; + virtual void dispatchWillSendSubmitEvent(Ref<FormState>&&) = 0; + virtual void dispatchWillSubmitForm(FormState&, FramePolicyFunction) = 0; + + virtual void revertToProvisionalState(DocumentLoader*) = 0; + virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) = 0; + + virtual void setMainFrameDocumentReady(bool) = 0; + + virtual void startDownload(const ResourceRequest&, const String& suggestedName = String()) = 0; + + virtual void willChangeTitle(DocumentLoader*) = 0; + virtual void didChangeTitle(DocumentLoader*) = 0; + + virtual void willReplaceMultipartContent() = 0; + virtual void didReplaceMultipartContent() = 0; + + virtual void committedLoad(DocumentLoader*, const char*, int) = 0; + virtual void finishedLoading(DocumentLoader*) = 0; + + virtual void updateGlobalHistory() = 0; + virtual void updateGlobalHistoryRedirectLinks() = 0; + + virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0; + virtual void updateGlobalHistoryItemForPage() { } + + // This frame has set its opener to null, disowning it for the lifetime of the frame. + // See http://html.spec.whatwg.org/#dom-opener. + // FIXME: JSC should allow disowning opener. - <https://bugs.webkit.org/show_bug.cgi?id=103913>. + virtual void didDisownOpener() { } + + // This frame has displayed inactive content (such as an image) from an + // insecure source. Inactive content cannot spread to other frames. + virtual void didDisplayInsecureContent() = 0; + + // The indicated security origin has run active content (such as a + // script) from an insecure source. Note that the insecure content can + // spread to other frames in the same origin. + virtual void didRunInsecureContent(SecurityOrigin&, const URL&) = 0; + virtual void didDetectXSS(const URL&, bool didBlockEntirePage) = 0; + + virtual ResourceError cancelledError(const ResourceRequest&) = 0; + virtual ResourceError blockedError(const ResourceRequest&) = 0; + virtual ResourceError blockedByContentBlockerError(const ResourceRequest&) = 0; + virtual ResourceError cannotShowURLError(const ResourceRequest&) = 0; + virtual ResourceError interruptedForPolicyChangeError(const ResourceRequest&) = 0; +#if ENABLE(CONTENT_FILTERING) + virtual ResourceError blockedByContentFilterError(const ResourceRequest&) = 0; #endif - virtual void transitionToCommittedForNewPage() = 0; - virtual void didSaveToPageCache() = 0; - virtual void didRestoreFromPageCache() = 0; + virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) = 0; + virtual ResourceError fileDoesNotExistError(const ResourceResponse&) = 0; + virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) = 0; + + virtual bool shouldFallBack(const ResourceError&) = 0; - virtual void dispatchDidBecomeFrameset(bool) = 0; // Can change due to navigation or DOM modification. + virtual bool canHandleRequest(const ResourceRequest&) const = 0; + virtual bool canShowMIMEType(const String& MIMEType) const = 0; + virtual bool canShowMIMETypeAsHTML(const String& MIMEType) const = 0; + virtual bool representationExistsForURLScheme(const String& URLScheme) const = 0; + virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const = 0; - virtual bool canCachePage() const = 0; - virtual void convertMainResourceLoadToDownload(DocumentLoader*, const ResourceRequest&, const ResourceResponse&) = 0; + virtual void frameLoadCompleted() = 0; + virtual void saveViewStateToItem(HistoryItem&) = 0; + virtual void restoreViewState() = 0; + virtual void provisionalLoadStarted() = 0; + virtual void didFinishLoad() = 0; + virtual void prepareForDataSourceReplacement() = 0; - virtual PassRefPtr<Frame> createFrame(const URL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) = 0; - virtual PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const URL&, const Vector<String>&, const Vector<String>&, const String&, bool loadManually) = 0; - virtual void recreatePlugin(Widget*) = 0; - virtual void redirectDataToPlugin(Widget* pluginWidget) = 0; + virtual Ref<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&) = 0; + virtual void updateCachedDocumentLoader(DocumentLoader&) = 0; + virtual void setTitle(const StringWithDirection&, const URL&) = 0; - virtual PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const URL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues) = 0; + virtual String userAgent(const URL&) = 0; - virtual void dispatchDidFailToStartPlugin(const PluginView*) const { } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - virtual PassRefPtr<Widget> createMediaPlayerProxyPlugin(const IntSize&, HTMLMediaElement*, const URL&, const Vector<String>&, const Vector<String>&, const String&) = 0; - virtual void hideMediaPlayerProxyPlugin(Widget*) = 0; - virtual void showMediaPlayerProxyPlugin(Widget*) = 0; + virtual String overrideContentSecurityPolicy() const { return String(); } + + virtual void savePlatformDataToCachedFrame(CachedFrame*) = 0; + virtual void transitionToCommittedFromCachedFrame(CachedFrame*) = 0; +#if PLATFORM(IOS) + virtual void didRestoreFrameHierarchyForCachedFrame() = 0; #endif + virtual void transitionToCommittedForNewPage() = 0; + + virtual void didSaveToPageCache() = 0; + virtual void didRestoreFromPageCache() = 0; + + virtual void dispatchDidBecomeFrameset(bool) = 0; // Can change due to navigation or DOM modification. + + virtual bool canCachePage() const = 0; + virtual void convertMainResourceLoadToDownload(DocumentLoader*, SessionID, const ResourceRequest&, const ResourceResponse&) = 0; + + virtual RefPtr<Frame> createFrame(const URL&, const String& name, HTMLFrameOwnerElement&, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) = 0; + virtual RefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement&, const URL&, const Vector<String>&, const Vector<String>&, const String&, bool loadManually) = 0; + virtual void recreatePlugin(Widget*) = 0; + virtual void redirectDataToPlugin(Widget&) = 0; + + virtual RefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement&, const URL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues) = 0; - virtual ObjectContentType objectContentType(const URL&, const String& mimeType, bool shouldPreferPlugInsForImages) = 0; - virtual String overrideMediaType() const = 0; + virtual ObjectContentType objectContentType(const URL&, const String& mimeType) = 0; + virtual String overrideMediaType() const = 0; - virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld&) = 0; + virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld&) = 0; - virtual void registerForIconNotification(bool listen = true) = 0; + virtual void registerForIconNotification(bool listen = true) = 0; -#if PLATFORM(MAC) - // Allow an accessibility object to retrieve a Frame parent if there's no PlatformWidget. - virtual RemoteAXObjectRef accessibilityRemoteObject() = 0; - virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0; +#if PLATFORM(COCOA) + // Allow an accessibility object to retrieve a Frame parent if there's no PlatformWidget. + virtual RemoteAXObjectRef accessibilityRemoteObject() = 0; + virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0; + virtual NSDictionary *dataDetectionContext() { return nullptr; } #endif -#if PLATFORM(WIN) && USE(CFNETWORK) - // FIXME: Windows should use willCacheResponse - <https://bugs.webkit.org/show_bug.cgi?id=57257>. - virtual bool shouldCacheResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&, const unsigned char* data, unsigned long long length) = 0; + +#if PLATFORM(WIN) && USE(CFURLCONNECTION) + // FIXME: Windows should use willCacheResponse - <https://bugs.webkit.org/show_bug.cgi?id=57257>. + virtual bool shouldCacheResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&, const unsigned char* data, unsigned long long length) = 0; #endif - virtual bool shouldAlwaysUsePluginDocument(const String& /*mimeType*/) const { return false; } - virtual bool shouldLoadMediaElementURL(const URL&) const { return true; } + virtual bool shouldAlwaysUsePluginDocument(const String& /*mimeType*/) const { return false; } + virtual bool shouldLoadMediaElementURL(const URL&) const { return true; } - virtual void didChangeScrollOffset() { } + virtual void didChangeScrollOffset() { } - virtual bool allowScript(bool enabledPerSettings) { return enabledPerSettings; } - virtual bool allowScriptFromSource(bool enabledPerSettings, const URL&) { return enabledPerSettings; } - virtual bool allowPlugins(bool enabledPerSettings) { return enabledPerSettings; } - virtual bool allowImage(bool enabledPerSettings, const URL&) { return enabledPerSettings; } - virtual bool allowDisplayingInsecureContent(bool enabledPerSettings, SecurityOrigin*, const URL&) { return enabledPerSettings; } - virtual bool allowRunningInsecureContent(bool enabledPerSettings, SecurityOrigin*, const URL&) { return enabledPerSettings; } + virtual bool allowScript(bool enabledPerSettings) { return enabledPerSettings; } - // Clients that generally disallow universal access can make exceptions for particular URLs. - virtual bool shouldForceUniversalAccessFromLocalURL(const URL&) { return false; } + // Clients that generally disallow universal access can make exceptions for particular URLs. + virtual bool shouldForceUniversalAccessFromLocalURL(const URL&) { return false; } - virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext() = 0; + virtual Ref<FrameNetworkingContext> createNetworkingContext() = 0; - virtual bool shouldPaintBrokenImage(const URL&) const { return true; } +#if ENABLE(REQUEST_AUTOCOMPLETE) + virtual void didRequestAutocomplete(Ref<FormState>&&) = 0; +#endif - virtual void dispatchWillOpenSocketStream(SocketStreamHandle*) { } + virtual bool shouldPaintBrokenImage(const URL&) const { return true; } - virtual void dispatchGlobalObjectAvailable(DOMWrapperWorld&) { } - virtual void dispatchWillDisconnectDOMWindowExtensionFromGlobalObject(DOMWindowExtension*) { } - virtual void dispatchDidReconnectDOMWindowExtensionToGlobalObject(DOMWindowExtension*) { } - virtual void dispatchWillDestroyGlobalObjectForDOMWindowExtension(DOMWindowExtension*) { } + virtual void dispatchGlobalObjectAvailable(DOMWrapperWorld&) { } + virtual void dispatchWillDisconnectDOMWindowExtensionFromGlobalObject(DOMWindowExtension*) { } + virtual void dispatchDidReconnectDOMWindowExtensionToGlobalObject(DOMWindowExtension*) { } + virtual void dispatchWillDestroyGlobalObjectForDOMWindowExtension(DOMWindowExtension*) { } -#if ENABLE(MEDIA_STREAM) - virtual void dispatchWillStartUsingPeerConnectionHandler(RTCPeerConnectionHandler*) { } +#if ENABLE(WEB_RTC) + virtual void dispatchWillStartUsingPeerConnectionHandler(RTCPeerConnectionHandler*) { } #endif #if ENABLE(WEBGL) - virtual bool allowWebGL(bool enabledPerSettings) { return enabledPerSettings; } - // Informs the embedder that a WebGL canvas inside this frame received a lost context - // notification with the given GL_ARB_robustness guilt/innocence code (see Extensions3D.h). - virtual void didLoseWebGLContext(int) { } - virtual WebGLLoadPolicy webGLPolicyForURL(const String&) const { return WebGLAllow; } + virtual bool allowWebGL(bool enabledPerSettings) { return enabledPerSettings; } + // Informs the embedder that a WebGL canvas inside this frame received a lost context + // notification with the given GL_ARB_robustness guilt/innocence code (see Extensions3D.h). + virtual void didLoseWebGLContext(int) { } + virtual WebGLLoadPolicy webGLPolicyForURL(const String&) const { return WebGLAllowCreation; } + virtual WebGLLoadPolicy resolveWebGLPolicyForURL(const String&) const { return WebGLAllowCreation; } #endif - virtual void forcePageTransitionIfNeeded() { } + virtual void forcePageTransitionIfNeeded() { } - // FIXME (bug 116233): We need to get rid of EmptyFrameLoaderClient completely, then this will no longer be needed. - virtual bool isEmptyFrameLoaderClient() { return false; } - }; + // FIXME (bug 116233): We need to get rid of EmptyFrameLoaderClient completely, then this will no longer be needed. + virtual bool isEmptyFrameLoaderClient() { return false; } -} // namespace WebCore +#if USE(QUICK_LOOK) + virtual RefPtr<QuickLookHandleClient> createQuickLookHandleClient(const String&, const String&) = 0; +#endif + +#if ENABLE(CONTENT_FILTERING) + virtual void contentFilterDidBlockLoad(ContentFilterUnblockHandler) { } +#endif + + virtual void prefetchDNS(const String&) = 0; + + virtual void didRestoreScrollPosition() { } -#endif // FrameLoaderClient_h + virtual bool useIconLoadingClient() { return false; } + virtual void getLoadDecisionForIcon(const LinkIcon&, uint64_t) { } + virtual void finishedLoadingIcon(uint64_t, SharedBuffer*) { } +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/FrameLoaderStateMachine.h b/Source/WebCore/loader/FrameLoaderStateMachine.h index 8bce923e8..714e5d6fa 100644 --- a/Source/WebCore/loader/FrameLoaderStateMachine.h +++ b/Source/WebCore/loader/FrameLoaderStateMachine.h @@ -26,9 +26,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameLoaderStateMachine_h -#define FrameLoaderStateMachine_h +#pragma once +#include "PlatformExportMacros.h" #include <wtf/Noncopyable.h> namespace WebCore { @@ -51,11 +51,11 @@ public: FirstLayoutDone }; - bool committingFirstRealLoad() const; + WEBCORE_EXPORT bool committingFirstRealLoad() const; bool committedFirstRealDocumentLoad() const; bool creatingInitialEmptyDocument() const; - bool isDisplayingInitialEmptyDocument() const; - bool firstLayoutDone() const; + WEBCORE_EXPORT bool isDisplayingInitialEmptyDocument() const; + WEBCORE_EXPORT bool firstLayoutDone() const; void advanceTo(State); private: @@ -63,5 +63,3 @@ private: }; } // namespace WebCore - -#endif // FrameLoaderStateMachine_h diff --git a/Source/WebCore/loader/FrameLoaderTypes.h b/Source/WebCore/loader/FrameLoaderTypes.h index 6385f6d70..a8b03341d 100644 --- a/Source/WebCore/loader/FrameLoaderTypes.h +++ b/Source/WebCore/loader/FrameLoaderTypes.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -26,91 +26,112 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameLoaderTypes_h -#define FrameLoaderTypes_h +#pragma once namespace WebCore { - enum FrameState { - FrameStateProvisional, - // This state indicates we are ready to commit to a page, - // which means the view will transition to use the new data source. - FrameStateCommittedPage, - FrameStateComplete - }; - - enum PolicyAction { - PolicyUse, - PolicyDownload, - PolicyIgnore - }; - - // NOTE: Keep in sync with WebKit/mac/WebView/WebFramePrivate.h and WebKit/win/Interfaces/IWebFramePrivate.idl - enum FrameLoadType { - FrameLoadTypeStandard, - FrameLoadTypeBack, - FrameLoadTypeForward, - FrameLoadTypeIndexedBackForward, // a multi-item hop in the backforward list - FrameLoadTypeReload, - // Skipped value: 'FrameLoadTypeReloadAllowingStaleData', still present in mac/win public API. Ready to be reused - FrameLoadTypeSame = FrameLoadTypeReload + 2, // user loads same URL again (but not reload button) - FrameLoadTypeRedirectWithLockedBackForwardList, // FIXME: Merge "lockBackForwardList", "lockHistory", "quickRedirect" and "clientRedirect" into a single concept of redirect. - FrameLoadTypeReplace, - FrameLoadTypeReloadFromOrigin, - }; - - enum NavigationType { - NavigationTypeLinkClicked, - NavigationTypeFormSubmitted, - NavigationTypeBackForward, - NavigationTypeReload, - NavigationTypeFormResubmitted, - NavigationTypeOther - }; - - enum ClearProvisionalItemPolicy { - ShouldClearProvisionalItem, - ShouldNotClearProvisionalItem - }; - - enum ObjectContentType { - ObjectContentNone, - ObjectContentImage, - ObjectContentFrame, - ObjectContentNetscapePlugin, - ObjectContentOtherPlugin - }; - - enum UnloadEventPolicy { - UnloadEventPolicyNone, - UnloadEventPolicyUnloadOnly, - UnloadEventPolicyUnloadAndPageHide - }; - - enum ShouldSendReferrer { - MaybeSendReferrer, - NeverSendReferrer - }; - - // Passed to FrameLoader::urlSelected() and ScriptController::executeIfJavaScriptURL() - // to control whether, in the case of a JavaScript URL, executeIfJavaScriptURL() should - // replace the document. It is a FIXME to eliminate this extra parameter from - // executeIfJavaScriptURL(), in which case this enum can go away. - enum ShouldReplaceDocumentIfJavaScriptURL { - ReplaceDocumentIfJavaScriptURL, - DoNotReplaceDocumentIfJavaScriptURL - }; - - enum ReasonForCallingAllowPlugins { - AboutToInstantiatePlugin, - NotAboutToInstantiatePlugin - }; - - enum WebGLLoadPolicy { - WebGLBlock = 0, - WebGLAllow - }; - -} - -#endif +enum FrameState { + FrameStateProvisional, + // This state indicates we are ready to commit to a page, + // which means the view will transition to use the new data source. + FrameStateCommittedPage, + FrameStateComplete +}; + +enum PolicyAction { + PolicyUse, + PolicyDownload, + PolicyIgnore +}; + +enum class FrameLoadType { + Standard, + Back, + Forward, + IndexedBackForward, // a multi-item hop in the backforward list + Reload, + Same, // user loads same URL again (but not reload button) + RedirectWithLockedBackForwardList, // FIXME: Merge "lockBackForwardList", "lockHistory", "quickRedirect" and "clientRedirect" into a single concept of redirect. + Replace, + ReloadFromOrigin, +}; + +enum class NewFrameOpenerPolicy { + Suppress, + Allow +}; + +enum class NavigationType { + LinkClicked, + FormSubmitted, + BackForward, + Reload, + FormResubmitted, + Other +}; + +enum class ShouldOpenExternalURLsPolicy { + ShouldNotAllow, + ShouldAllowExternalSchemes, + ShouldAllow, +}; + +enum ClearProvisionalItemPolicy { + ShouldClearProvisionalItem, + ShouldNotClearProvisionalItem +}; + +enum class ObjectContentType { + None, + Image, + Frame, + PlugIn, +}; + +enum UnloadEventPolicy { + UnloadEventPolicyNone, + UnloadEventPolicyUnloadOnly, + UnloadEventPolicyUnloadAndPageHide +}; + +enum ShouldSendReferrer { + MaybeSendReferrer, + NeverSendReferrer +}; + +// Passed to FrameLoader::urlSelected() and ScriptController::executeIfJavaScriptURL() +// to control whether, in the case of a JavaScript URL, executeIfJavaScriptURL() should +// replace the document. It is a FIXME to eliminate this extra parameter from +// executeIfJavaScriptURL(), in which case this enum can go away. +enum ShouldReplaceDocumentIfJavaScriptURL { + ReplaceDocumentIfJavaScriptURL, + DoNotReplaceDocumentIfJavaScriptURL +}; + +enum WebGLLoadPolicy { + WebGLBlockCreation, + WebGLAllowCreation, + WebGLPendingCreation +}; + +enum class LockHistory { + Yes, + No +}; + +enum class LockBackForwardList { + Yes, + No +}; + +enum class AllowNavigationToInvalidURL { + Yes, + No +}; + +enum class HasInsecureContent { + Yes, + No, +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/FrameNetworkingContext.h b/Source/WebCore/loader/FrameNetworkingContext.h index 72415abd3..d87cf9e1f 100644 --- a/Source/WebCore/loader/FrameNetworkingContext.h +++ b/Source/WebCore/loader/FrameNetworkingContext.h @@ -17,8 +17,7 @@ Boston, MA 02110-1301, USA. */ -#ifndef FrameNetworkingContext_h -#define FrameNetworkingContext_h +#pragma once #include "Document.h" #include "Frame.h" @@ -31,16 +30,16 @@ class FrameNetworkingContext : public NetworkingContext { public: void invalidate() { - m_frame = 0; + m_frame = nullptr; } - virtual bool shouldClearReferrerOnHTTPSToHTTPRedirect() const + bool shouldClearReferrerOnHTTPSToHTTPRedirect() const override { // FIXME: PingLoader finishes without a frame, but it should use its document's referrer policy. if (!m_frame) return true; - return m_frame->document()->referrerPolicy() == ReferrerPolicyDefault; + return m_frame->document()->referrerPolicy() == ReferrerPolicy::Default; } protected: @@ -52,11 +51,9 @@ protected: Frame* frame() const { return m_frame; } private: - virtual bool isValid() const override { return m_frame; } + bool isValid() const override { return m_frame; } Frame* m_frame; }; } - -#endif // FrameNetworkingContext_h diff --git a/Source/WebCore/loader/HistoryController.cpp b/Source/WebCore/loader/HistoryController.cpp index ffd9d32c1..417446ef7 100644 --- a/Source/WebCore/loader/HistoryController.cpp +++ b/Source/WebCore/loader/HistoryController.cpp @@ -12,7 +12,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. * @@ -41,22 +41,21 @@ #include "FrameTree.h" #include "FrameView.h" #include "HistoryItem.h" +#include "LinkHash.h" #include "Logging.h" #include "MainFrame.h" #include "Page.h" #include "PageCache.h" -#include "PageGroup.h" -#include "PlatformStrategies.h" #include "ScrollingCoordinator.h" -#include "Settings.h" -#include "VisitedLinkStrategy.h" +#include "SerializedScriptValue.h" +#include "VisitedLinkStore.h" #include <wtf/text/CString.h> namespace WebCore { -static inline void addVisitedLink(Page* page, const URL& url) +static inline void addVisitedLink(Page& page, const URL& url) { - platformStrategies()->visitedLinkStrategy()->addVisitedLink(page, visitedLinkHash(url.string())); + page.visitedLinkStore().addVisitedLink(page, visitedLinkHash(url.string())); } HistoryController::HistoryController(Frame& frame) @@ -72,20 +71,33 @@ HistoryController::~HistoryController() void HistoryController::saveScrollPositionAndViewStateToItem(HistoryItem* item) { - if (!item || !m_frame.view()) + FrameView* frameView = m_frame.view(); + if (!item || !frameView) return; - if (m_frame.document()->inPageCache()) - item->setScrollPoint(m_frame.view()->cachedScrollPosition()); + if (m_frame.document()->pageCacheState() != Document::NotInPageCache) + item->setScrollPosition(frameView->cachedScrollPosition()); else - item->setScrollPoint(m_frame.view()->scrollPosition()); + item->setScrollPosition(frameView->scrollPosition()); + +#if PLATFORM(IOS) + item->setExposedContentRect(frameView->exposedContentRect()); + item->setUnobscuredContentRect(frameView->unobscuredContentRect()); +#endif Page* page = m_frame.page(); - if (page && m_frame.isMainFrame()) - item->setPageScaleFactor(page->pageScaleFactor()); + if (page && m_frame.isMainFrame()) { + item->setPageScaleFactor(page->pageScaleFactor() / page->viewScaleFactor()); +#if PLATFORM(IOS) + item->setObscuredInset(page->obscuredInset()); +#endif + } // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client. - m_frame.loader().client().saveViewStateToItem(item); + m_frame.loader().client().saveViewStateToItem(*item); + + // Notify clients that the HistoryItem has changed. + item->notifyChanged(); } void HistoryController::clearScrollPositionAndViewState() @@ -93,7 +105,7 @@ void HistoryController::clearScrollPositionAndViewState() if (!m_currentItem) return; - m_currentItem->clearScrollPoint(); + m_currentItem->clearScrollPosition(); m_currentItem->setPageScaleFactor(0); } @@ -110,7 +122,7 @@ void HistoryController::clearScrollPositionAndViewState() */ void HistoryController::restoreScrollPositionAndViewState() { - if (!m_frame.loader().stateMachine()->committedFirstRealDocumentLoad()) + if (!m_frame.loader().stateMachine().committedFirstRealDocumentLoad()) return; ASSERT(m_currentItem); @@ -122,31 +134,44 @@ void HistoryController::restoreScrollPositionAndViewState() // so there *is* no scroll or view state to restore! if (!m_currentItem) return; - - // FIXME: It would be great to work out a way to put this code in WebCore instead of calling - // through to the client. It's currently used only for the PDF view on Mac. - m_frame.loader().client().restoreViewState(); - // Don't restore scroll point on iOS as FrameLoaderClient::restoreViewState() does that. -#if !PLATFORM(IOS) + FrameView* view = m_frame.view(); + // FIXME: There is some scrolling related work that needs to happen whenever a page goes into the // page cache and similar work that needs to occur when it comes out. This is where we do the work // that needs to happen when we exit, and the work that needs to happen when we enter is in // Document::setIsInPageCache(bool). It would be nice if there was more symmetry in these spots. // https://bugs.webkit.org/show_bug.cgi?id=98698 - if (FrameView* view = m_frame.view()) { + if (view) { Page* page = m_frame.page(); if (page && m_frame.isMainFrame()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingCoordinator->frameViewRootLayerDidChange(view); + scrollingCoordinator->frameViewRootLayerDidChange(*view); } + } + + // FIXME: It would be great to work out a way to put this code in WebCore instead of calling + // through to the client. + m_frame.loader().client().restoreViewState(); - if (!view->wasScrolledByUser()) { - if (page && m_frame.isMainFrame() && m_currentItem->pageScaleFactor()) - page->setPageScaleFactor(m_currentItem->pageScaleFactor(), m_currentItem->scrollPoint()); - else - view->setScrollPosition(m_currentItem->scrollPoint()); +#if !PLATFORM(IOS) + // Don't restore scroll point on iOS as FrameLoaderClient::restoreViewState() does that. + if (view && !view->wasScrolledByUser()) { + Page* page = m_frame.page(); + auto desiredScrollPosition = m_currentItem->scrollPosition(); + LOG(Scrolling, "HistoryController::restoreScrollPositionAndViewState scrolling to %d,%d", desiredScrollPosition.x(), desiredScrollPosition.y()); + if (page && m_frame.isMainFrame() && m_currentItem->pageScaleFactor()) + page->setPageScaleFactor(m_currentItem->pageScaleFactor() * page->viewScaleFactor(), desiredScrollPosition); + else + view->setScrollPosition(desiredScrollPosition); + + // If the scroll position doesn't have to be clamped, consider it successfully restored. + if (m_frame.isMainFrame()) { + auto adjustedDesiredScrollPosition = view->adjustScrollPositionWithinRange(desiredScrollPosition); + if (desiredScrollPosition == adjustedDesiredScrollPosition) + m_frame.loader().client().didRestoreScrollPosition(); } + } #endif } @@ -160,7 +185,7 @@ void HistoryController::saveDocumentState() { // FIXME: Reading this bit of FrameLoader state here is unfortunate. I need to study // this more to see if we can remove this dependency. - if (m_frame.loader().stateMachine()->creatingInitialEmptyDocument()) + if (m_frame.loader().stateMachine().creatingInitialEmptyDocument()) return; // For a standard page load, we will have a previous item set, which will be used to @@ -178,12 +203,14 @@ void HistoryController::saveDocumentState() if (!item) return; - Document* document = m_frame.document(); - ASSERT(document); - - if (item->isCurrentDocument(document) && document->hasLivingRenderTree()) { + ASSERT(m_frame.document()); + Document& document = *m_frame.document(); + if (item->isCurrentDocument(document) && document.hasLivingRenderTree()) { + if (DocumentLoader* documentLoader = document.loader()) + item->setShouldOpenExternalURLsPolicy(documentLoader->shouldOpenExternalURLsPolicyToPropagate()); + LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame.tree().uniqueName().string().utf8().data(), item); - item->setDocumentState(document->formElementsState()); + item->setDocumentState(document.formElementsState()); } } @@ -200,18 +227,18 @@ void HistoryController::saveDocumentAndScrollState() void HistoryController::restoreDocumentState() { switch (m_frame.loader().loadType()) { - case FrameLoadTypeReload: - case FrameLoadTypeReloadFromOrigin: - case FrameLoadTypeSame: - case FrameLoadTypeReplace: - // Not restoring the document state. - return; - case FrameLoadTypeBack: - case FrameLoadTypeForward: - case FrameLoadTypeIndexedBackForward: - case FrameLoadTypeRedirectWithLockedBackForwardList: - case FrameLoadTypeStandard: - break; + case FrameLoadType::Reload: + case FrameLoadType::ReloadFromOrigin: + case FrameLoadType::Same: + case FrameLoadType::Replace: + // Not restoring the document state. + return; + case FrameLoadType::Back: + case FrameLoadType::Forward: + case FrameLoadType::IndexedBackForward: + case FrameLoadType::RedirectWithLockedBackForwardList: + case FrameLoadType::Standard: + break; } if (!m_currentItem) @@ -221,17 +248,21 @@ void HistoryController::restoreDocumentState() if (m_frame.loader().documentLoader()->isClientRedirect()) return; + m_frame.loader().documentLoader()->setShouldOpenExternalURLsPolicy(m_currentItem->shouldOpenExternalURLsPolicy()); + LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame.tree().uniqueName().string().utf8().data(), m_currentItem.get()); m_frame.document()->setStateForNewFormElements(m_currentItem->documentState()); } void HistoryController::invalidateCurrentItemCachedPage() { - // When we are pre-commit, the currentItem is where any page cache data resides. - if (!pageCache()->get(currentItem())) + if (!currentItem()) return; - std::unique_ptr<CachedPage> cachedPage = pageCache()->take(currentItem()); + // When we are pre-commit, the currentItem is where any page cache data resides. + std::unique_ptr<CachedPage> cachedPage = PageCache::singleton().take(*currentItem(), m_frame.page()); + if (!cachedPage) + return; // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach // Somehow the PageState object is not properly updated, and is holding onto a stale document. @@ -239,12 +270,12 @@ void HistoryController::invalidateCurrentItemCachedPage() ASSERT(cachedPage->document() == m_frame.document()); if (cachedPage->document() == m_frame.document()) { - cachedPage->document()->setInPageCache(false); + cachedPage->document()->setPageCacheState(Document::NotInPageCache); cachedPage->clear(); } } -bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) const +bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem& targetItem) const { if (!m_currentItem) return false; @@ -258,7 +289,7 @@ bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) // Main funnel for navigating to a previous location (back/forward, non-search snap-back) // This includes recursion to handle loading into framesets properly -void HistoryController::goToItem(HistoryItem* targetItem, FrameLoadType type) +void HistoryController::goToItem(HistoryItem& targetItem, FrameLoadType type) { ASSERT(!m_frame.tree().parent()); @@ -269,26 +300,27 @@ void HistoryController::goToItem(HistoryItem* targetItem, FrameLoadType type) Page* page = m_frame.page(); if (!page) return; - if (!m_frame.loader().client().shouldGoToHistoryItem(targetItem)) + if (!m_frame.loader().client().shouldGoToHistoryItem(&targetItem)) return; if (m_defersLoading) { - m_deferredItem = targetItem; + m_deferredItem = &targetItem; m_deferredFrameLoadType = type; return; } // Set the BF cursor before commit, which lets the user quickly click back/forward again. - // - plus, it only makes sense for the top level of the operation through the frametree, + // - plus, it only makes sense for the top level of the operation through the frame tree, // as opposed to happening for some/one of the page commits that might happen soon RefPtr<HistoryItem> currentItem = page->backForward().currentItem(); - page->backForward().setCurrentItem(targetItem); + page->backForward().setCurrentItem(&targetItem); m_frame.loader().client().updateGlobalHistoryItemForPage(); // First set the provisional item of any frames that are not actually navigating. // This must be done before trying to navigate the desired frame, because some // navigations can commit immediately (such as about:blank). We must be sure that // all frames have provisional items set before the commit. - recursiveSetProvisionalItem(targetItem, currentItem.get(), type); + recursiveSetProvisionalItem(targetItem, currentItem.get()); + // Now that all other frames have provisional items, do the actual navigation. recursiveGoToItem(targetItem, currentItem.get(), type); } @@ -297,17 +329,14 @@ void HistoryController::setDefersLoading(bool defer) { m_defersLoading = defer; if (!defer && m_deferredItem) { - goToItem(m_deferredItem.get(), m_deferredFrameLoadType); - m_deferredItem = 0; + goToItem(*m_deferredItem, m_deferredFrameLoadType); + m_deferredItem = nullptr; } } void HistoryController::updateForBackForwardNavigation() { -#if !LOG_DISABLED - if (m_frame.loader().documentLoader()) - LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", m_frame.loader().documentLoader()->title().string().utf8().data()); -#endif + LOG(History, "HistoryController %p updateForBackForwardNavigation: Updating History for back/forward navigation in frame %p (main frame %d) %s", this, &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader() ? m_frame.loader().documentLoader()->url().string().utf8().data() : ""); // Must grab the current scroll position before disturbing it if (!m_frameLoadComplete) @@ -320,16 +349,16 @@ void HistoryController::updateForBackForwardNavigation() void HistoryController::updateForReload() { -#if !LOG_DISABLED - if (m_frame.loader().documentLoader()) - LOG(History, "WebCoreHistory: Updating History for reload in frame %s", m_frame.loader().documentLoader()->title().string().utf8().data()); -#endif + LOG(History, "HistoryController %p updateForReload: Updating History for reload in frame %p (main frame %d) %s", this, &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader() ? m_frame.loader().documentLoader()->url().string().utf8().data() : ""); if (m_currentItem) { - pageCache()->remove(m_currentItem.get()); + PageCache::singleton().remove(*m_currentItem); - if (m_frame.loader().loadType() == FrameLoadTypeReload || m_frame.loader().loadType() == FrameLoadTypeReloadFromOrigin) + if (m_frame.loader().loadType() == FrameLoadType::Reload || m_frame.loader().loadType() == FrameLoadType::ReloadFromOrigin) saveScrollPositionAndViewStateToItem(m_currentItem.get()); + + // Rebuild the history item tree when reloading as trying to re-associate everything is too error-prone. + m_currentItem->clearChildren(); } // When reloading the page, we may end up redirecting to a different URL @@ -345,11 +374,11 @@ void HistoryController::updateForReload() void HistoryController::updateForStandardLoad(HistoryUpdateType updateType) { - LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", m_frame.loader().documentLoader()->url().string().ascii().data()); + LOG(History, "HistoryController %p updateForStandardLoad: Updating History for standard load in frame %p (main frame %d) %s", this, &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader()->url().string().ascii().data()); FrameLoader& frameLoader = m_frame.loader(); - bool needPrivacy = m_frame.settings().privateBrowsingEnabled(); + bool needPrivacy = m_frame.page()->usesEphemeralSession(); const URL& historyURL = frameLoader.documentLoader()->urlForHistory(); if (!frameLoader.documentLoader()->isClientRedirect()) { @@ -372,7 +401,7 @@ void HistoryController::updateForStandardLoad(HistoryUpdateType updateType) if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame.page()) - addVisitedLink(page, historyURL); + addVisitedLink(*page, historyURL); if (!frameLoader.documentLoader()->didCreateGlobalHistoryEntry() && frameLoader.documentLoader()->unreachableURL().isEmpty() && !m_frame.document()->url().isEmpty()) frameLoader.client().updateGlobalHistoryRedirectLinks(); @@ -381,12 +410,9 @@ void HistoryController::updateForStandardLoad(HistoryUpdateType updateType) void HistoryController::updateForRedirectWithLockedBackForwardList() { -#if !LOG_DISABLED - if (m_frame.loader().documentLoader()) - LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", m_frame.loader().documentLoader()->title().string().utf8().data()); -#endif + LOG(History, "HistoryController %p updateForRedirectWithLockedBackForwardList: Updating History for redirect load in frame %p (main frame %d) %s", this, &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader() ? m_frame.loader().documentLoader()->url().string().utf8().data() : ""); - bool needPrivacy = m_frame.settings().privateBrowsingEnabled(); + bool needPrivacy = m_frame.page()->usesEphemeralSession(); const URL& historyURL = m_frame.loader().documentLoader()->urlForHistory(); if (m_frame.loader().documentLoader()->isClientRedirect()) { @@ -413,7 +439,7 @@ void HistoryController::updateForRedirectWithLockedBackForwardList() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame.page()) - addVisitedLink(page, historyURL); + addVisitedLink(*page, historyURL); if (!m_frame.loader().documentLoader()->didCreateGlobalHistoryEntry() && m_frame.loader().documentLoader()->unreachableURL().isEmpty() && !m_frame.document()->url().isEmpty()) m_frame.loader().client().updateGlobalHistoryRedirectLinks(); @@ -422,34 +448,29 @@ void HistoryController::updateForRedirectWithLockedBackForwardList() void HistoryController::updateForClientRedirect() { -#if !LOG_DISABLED - if (m_frame.loader().documentLoader()) - LOG(History, "WebCoreHistory: Updating History for client redirect in frame %s", m_frame.loader().documentLoader()->title().string().utf8().data()); -#endif + LOG(History, "HistoryController %p updateForClientRedirect: Updating History for client redirect in frame %p (main frame %d) %s", this, &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader() ? m_frame.loader().documentLoader()->url().string().utf8().data() : ""); // Clear out form data so we don't try to restore it into the incoming page. Must happen after // webcore has closed the URL and saved away the form state. if (m_currentItem) { m_currentItem->clearDocumentState(); - m_currentItem->clearScrollPoint(); + m_currentItem->clearScrollPosition(); } - bool needPrivacy = m_frame.settings().privateBrowsingEnabled(); + bool needPrivacy = m_frame.page()->usesEphemeralSession(); const URL& historyURL = m_frame.loader().documentLoader()->urlForHistory(); if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame.page()) - addVisitedLink(page, historyURL); + addVisitedLink(*page, historyURL); } } void HistoryController::updateForCommit() { FrameLoader& frameLoader = m_frame.loader(); -#if !LOG_DISABLED - if (frameLoader.documentLoader()) - LOG(History, "WebCoreHistory: Updating History for commit in frame %s", frameLoader.documentLoader()->title().string().utf8().data()); -#endif + LOG(History, "HistoryController %p updateForCommit: Updating History for commit in frame %p (main frame %d) %s", this, &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader() ? m_frame.loader().documentLoader()->url().string().utf8().data() : ""); + FrameLoadType type = frameLoader.loadType(); if (isBackForwardLoadType(type) || isReplaceLoadTypeWithProvisionalItem(type) @@ -458,11 +479,14 @@ void HistoryController::updateForCommit() // the provisional item for restoring state. // Note previousItem must be set before we close the URL, which will // happen when the data source is made non-provisional below - m_frameLoadComplete = false; - m_previousItem = m_currentItem; + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=146842 + // We should always have a provisional item when committing, but we sometimes don't. + // Not having one leads to us not having a m_currentItem later, which is also a terrible known issue. + // We should get to the bottom of this. ASSERT(m_provisionalItem); - m_currentItem = m_provisionalItem; - m_provisionalItem = 0; + setCurrentItem(m_provisionalItem.get()); + m_provisionalItem = nullptr; // Tell all other frames in the tree to commit their provisional items and // restore their scroll position. We'll avoid this frame (which has already @@ -473,14 +497,14 @@ void HistoryController::updateForCommit() bool HistoryController::isReplaceLoadTypeWithProvisionalItem(FrameLoadType type) { - // Going back to an error page in a subframe can trigger a FrameLoadTypeReplace + // Going back to an error page in a subframe can trigger a FrameLoadType::Replace // while m_provisionalItem is set, so we need to commit it. - return type == FrameLoadTypeReplace && m_provisionalItem; + return type == FrameLoadType::Replace && m_provisionalItem; } bool HistoryController::isReloadTypeWithProvisionalItem(FrameLoadType type) { - return (type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && m_provisionalItem; + return (type == FrameLoadType::Reload || type == FrameLoadType::ReloadFromOrigin) && m_provisionalItem; } void HistoryController::recursiveUpdateForCommit() @@ -493,7 +517,7 @@ void HistoryController::recursiveUpdateForCommit() // For each frame that already had the content the item requested (based on // (a matching URL and frame tree snapshot), just restore the scroll position. // Save form state (works from currentItem, since m_frameLoadComplete is true) - if (m_currentItem && itemsAreClones(m_currentItem.get(), m_provisionalItem.get())) { + if (m_currentItem && itemsAreClones(*m_currentItem, m_provisionalItem.get())) { ASSERT(m_frameLoadComplete); saveDocumentState(); saveScrollPositionAndViewStateToItem(m_currentItem.get()); @@ -502,10 +526,8 @@ void HistoryController::recursiveUpdateForCommit() view->setWasScrolledByUser(false); // Now commit the provisional item - m_frameLoadComplete = false; - m_previousItem = m_currentItem; - m_currentItem = m_provisionalItem; - m_provisionalItem = 0; + setCurrentItem(m_provisionalItem.get()); + m_provisionalItem = nullptr; // Restore form state (works from currentItem) restoreDocumentState(); @@ -524,14 +546,14 @@ void HistoryController::updateForSameDocumentNavigation() if (m_frame.document()->url().isEmpty()) return; - if (m_frame.settings().privateBrowsingEnabled()) + if (m_frame.page()->usesEphemeralSession()) return; Page* page = m_frame.page(); if (!page) return; - addVisitedLink(page, m_frame.document()->url()); + addVisitedLink(*page, m_frame.document()->url()); m_frame.mainFrame().loader().history().recursiveUpdateForSameDocumentNavigation(); if (m_currentItem) { @@ -549,14 +571,12 @@ void HistoryController::recursiveUpdateForSameDocumentNavigation() // The provisional item may represent a different pending navigation. // Don't commit it if it isn't a same document navigation. - if (m_currentItem && !m_currentItem->shouldDoSameDocumentNavigationTo(m_provisionalItem.get())) + if (m_currentItem && !m_currentItem->shouldDoSameDocumentNavigationTo(*m_provisionalItem)) return; // Commit the provisional item. - m_frameLoadComplete = false; - m_previousItem = m_currentItem; - m_currentItem = m_provisionalItem; - m_provisionalItem = 0; + setCurrentItem(m_provisionalItem.get()); + m_provisionalItem = nullptr; // Iterate over the rest of the tree. for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) @@ -580,9 +600,9 @@ void HistoryController::setCurrentItem(HistoryItem* item) void HistoryController::setCurrentItemTitle(const StringWithDirection& title) { + // FIXME: This ignores the title's direction. if (m_currentItem) - // FIXME: make use of title.direction() as well. - m_currentItem->setTitle(title.string()); + m_currentItem->setTitle(title.string); } bool HistoryController::currentItemShouldBeReplaced() const @@ -591,7 +611,14 @@ bool HistoryController::currentItemShouldBeReplaced() const // "If the browsing context's session history contains only one Document, // and that was the about:blank Document created when the browsing context // was created, then the navigation must be done with replacement enabled." - return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->urlString(), blankURL()); + return m_currentItem && !m_previousItem && equalIgnoringASCIICase(m_currentItem->urlString(), blankURL()); +} + +void HistoryController::clearPreviousItem() +{ + m_previousItem = nullptr; + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->loader().history().clearPreviousItem(); } void HistoryController::setProvisionalItem(HistoryItem* item) @@ -599,7 +626,7 @@ void HistoryController::setProvisionalItem(HistoryItem* item) m_provisionalItem = item; } -void HistoryController::initializeItem(HistoryItem* item) +void HistoryController::initializeItem(HistoryItem& item) { DocumentLoader* documentLoader = m_frame.loader().documentLoader(); ASSERT(documentLoader); @@ -627,40 +654,37 @@ void HistoryController::initializeItem(HistoryItem* item) if (originalURL.isEmpty()) originalURL = blankURL(); - Frame* parentFrame = m_frame.tree().parent(); - String parent = parentFrame ? parentFrame->tree().uniqueName() : ""; StringWithDirection title = documentLoader->title(); - item->setURL(url); - item->setTarget(m_frame.tree().uniqueName()); - item->setParent(parent); - // FIXME: should store title directionality in history as well. - item->setTitle(title.string()); - item->setOriginalURLString(originalURL.string()); + item.setURL(url); + item.setTarget(m_frame.tree().uniqueName()); + // FIXME: Should store the title direction as well. + item.setTitle(title.string); + item.setOriginalURLString(originalURL.string()); if (!unreachableURL.isEmpty() || documentLoader->response().httpStatusCode() >= 400) - item->setLastVisitWasFailure(true); + item.setLastVisitWasFailure(true); + + item.setShouldOpenExternalURLsPolicy(documentLoader->shouldOpenExternalURLsPolicyToPropagate()); // Save form state if this is a POST - item->setFormInfoFromRequest(documentLoader->request()); + item.setFormInfoFromRequest(documentLoader->request()); } -PassRefPtr<HistoryItem> HistoryController::createItem() +Ref<HistoryItem> HistoryController::createItem() { - RefPtr<HistoryItem> item = HistoryItem::create(); - initializeItem(item.get()); + Ref<HistoryItem> item = HistoryItem::create(); + initializeItem(item); // Set the item for which we will save document state - m_frameLoadComplete = false; - m_previousItem = m_currentItem; - m_currentItem = item; + setCurrentItem(item.ptr()); - return item.release(); + return item; } -PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame& targetFrame, bool clipAtTarget) +Ref<HistoryItem> HistoryController::createItemTree(Frame& targetFrame, bool clipAtTarget) { - RefPtr<HistoryItem> bfItem = createItem(); + Ref<HistoryItem> bfItem = createItem(); if (!m_frameLoadComplete) saveScrollPositionAndViewStateToItem(m_previousItem.get()); @@ -700,54 +724,47 @@ PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame& targetFrame, bo // tracking whether each frame already has the content the item requests. If there is // a match, we set the provisional item and recurse. Otherwise we will reload that // frame and all its kids in recursiveGoToItem. -void HistoryController::recursiveSetProvisionalItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type) +void HistoryController::recursiveSetProvisionalItem(HistoryItem& item, HistoryItem* fromItem) { - ASSERT(item); + if (!itemsAreClones(item, fromItem)) + return; - if (itemsAreClones(item, fromItem)) { - // Set provisional item, which will be committed in recursiveUpdateForCommit. - m_provisionalItem = item; + // Set provisional item, which will be committed in recursiveUpdateForCommit. + m_provisionalItem = &item; - const HistoryItemVector& childItems = item->children(); + for (auto& childItem : item.children()) { + const String& childFrameName = childItem->target(); - int size = childItems.size(); + HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); + ASSERT(fromChildItem); + Frame* childFrame = m_frame.tree().child(childFrameName); + ASSERT(childFrame); - for (int i = 0; i < size; ++i) { - String childFrameName = childItems[i]->target(); - HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); - ASSERT(fromChildItem); - Frame* childFrame = m_frame.tree().child(childFrameName); - ASSERT(childFrame); - childFrame->loader().history().recursiveSetProvisionalItem(childItems[i].get(), fromChildItem, type); - } + childFrame->loader().history().recursiveSetProvisionalItem(childItem, fromChildItem); } } // We now traverse the frame tree and item tree a second time, loading frames that // do have the content the item requests. -void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type) -{ - ASSERT(item); - - if (itemsAreClones(item, fromItem)) { - // Just iterate over the rest, looking for frames to navigate. - const HistoryItemVector& childItems = item->children(); - - int size = childItems.size(); - for (int i = 0; i < size; ++i) { - String childFrameName = childItems[i]->target(); - HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); - ASSERT(fromChildItem); - Frame* childFrame = m_frame.tree().child(childFrameName); - ASSERT(childFrame); - childFrame->loader().history().recursiveGoToItem(childItems[i].get(), fromChildItem, type); - } - } else { +void HistoryController::recursiveGoToItem(HistoryItem& item, HistoryItem* fromItem, FrameLoadType type) +{ + if (!itemsAreClones(item, fromItem)) { m_frame.loader().loadItem(item, type); + return; + } + + // Just iterate over the rest, looking for frames to navigate. + for (auto& childItem : item.children()) { + const String& childFrameName = childItem->target(); + + HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); + ASSERT(fromChildItem); + if (Frame* childFrame = m_frame.tree().child(childFrameName)) + childFrame->loader().history().recursiveGoToItem(childItem, fromChildItem, type); } } -bool HistoryController::itemsAreClones(HistoryItem* item1, HistoryItem* item2) const +bool HistoryController::itemsAreClones(HistoryItem& item1, HistoryItem* item2) const { // If the item we're going to is a clone of the item we're at, then we do // not need to load it again. The current frame tree and the frame tree @@ -756,11 +773,10 @@ bool HistoryController::itemsAreClones(HistoryItem* item1, HistoryItem* item2) c // a reload. Thus, if item1 and item2 are the same, we need to create a // new document and should not consider them clones. // (See http://webkit.org/b/35532 for details.) - return item1 - && item2 - && item1 != item2 - && item1->itemSequenceNumber() == item2->itemSequenceNumber() - && currentFramesMatchItem(item1) + return item2 + && &item1 != item2 + && item1.itemSequenceNumber() == item2->itemSequenceNumber() + && currentFramesMatchItem(&item1) && item2->hasSameFrames(item1); } @@ -770,13 +786,12 @@ bool HistoryController::currentFramesMatchItem(HistoryItem* item) const if ((!m_frame.tree().uniqueName().isEmpty() || !item->target().isEmpty()) && m_frame.tree().uniqueName() != item->target()) return false; - const HistoryItemVector& childItems = item->children(); + const auto& childItems = item->children(); if (childItems.size() != m_frame.tree().childCount()) return false; - unsigned size = childItems.size(); - for (unsigned i = 0; i < size; ++i) { - if (!m_frame.tree().child(childItems[i]->target())) + for (auto& item : childItems) { + if (!m_frame.tree().child(item->target())) return false; } @@ -799,9 +814,10 @@ void HistoryController::updateBackForwardListClippedAtTarget(bool doClip) FrameLoader& frameLoader = m_frame.mainFrame().loader(); - RefPtr<HistoryItem> topItem = frameLoader.history().createItemTree(m_frame, doClip); - LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", topItem.get(), m_frame.loader().documentLoader()->url().string().ascii().data()); - page->backForward().addItem(topItem.release()); + Ref<HistoryItem> topItem = frameLoader.history().createItemTree(m_frame, doClip); + LOG(History, "HistoryController %p updateBackForwardListClippedAtTarget: Adding backforward item %p in frame %p (main frame %d) %s", this, topItem.ptr(), &m_frame, m_frame.isMainFrame(), m_frame.loader().documentLoader()->url().string().utf8().data()); + + page->backForward().addItem(WTFMove(topItem)); } void HistoryController::updateCurrentItem() @@ -821,7 +837,7 @@ void HistoryController::updateCurrentItem() // dependent on the document. bool isTargetItem = m_currentItem->isTargetItem(); m_currentItem->reset(); - initializeItem(m_currentItem.get()); + initializeItem(*m_currentItem); m_currentItem->setIsTargetItem(isTargetItem); } else { // Even if the final URL didn't change, the form data may have changed. @@ -829,7 +845,7 @@ void HistoryController::updateCurrentItem() } } -void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString) +void HistoryController::pushState(RefPtr<SerializedScriptValue>&& stateObject, const String& title, const String& urlString) { if (!m_currentItem) return; @@ -838,40 +854,44 @@ void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject, ASSERT(page); // Get a HistoryItem tree for the current frame tree. - RefPtr<HistoryItem> topItem = m_frame.mainFrame().loader().history().createItemTree(m_frame, false); + Ref<HistoryItem> topItem = m_frame.mainFrame().loader().history().createItemTree(m_frame, false); // Override data in the current item (created by createItemTree) to reflect // the pushState() arguments. m_currentItem->setTitle(title); - m_currentItem->setStateObject(stateObject); + m_currentItem->setStateObject(WTFMove(stateObject)); m_currentItem->setURLString(urlString); - page->backForward().addItem(topItem.release()); + LOG(History, "HistoryController %p pushState: Adding top item %p, setting url of current item %p to %s", this, topItem.ptr(), m_currentItem.get(), urlString.ascii().data()); + + page->backForward().addItem(WTFMove(topItem)); - if (m_frame.settings().privateBrowsingEnabled()) + if (m_frame.page()->usesEphemeralSession()) return; - addVisitedLink(page, URL(ParsedURLString, urlString)); + addVisitedLink(*page, URL(ParsedURLString, urlString)); m_frame.loader().client().updateGlobalHistory(); } -void HistoryController::replaceState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString) +void HistoryController::replaceState(RefPtr<SerializedScriptValue>&& stateObject, const String& title, const String& urlString) { if (!m_currentItem) return; + LOG(History, "HistoryController %p replaceState: Setting url of current item %p to %s", this, m_currentItem.get(), urlString.ascii().data()); + if (!urlString.isEmpty()) m_currentItem->setURLString(urlString); m_currentItem->setTitle(title); - m_currentItem->setStateObject(stateObject); - m_currentItem->setFormData(0); + m_currentItem->setStateObject(WTFMove(stateObject)); + m_currentItem->setFormData(nullptr); m_currentItem->setFormContentType(String()); - if (m_frame.settings().privateBrowsingEnabled()) + if (m_frame.page()->usesEphemeralSession()) return; ASSERT(m_frame.page()); - addVisitedLink(m_frame.page(), URL(ParsedURLString, urlString)); + addVisitedLink(*m_frame.page(), URL(ParsedURLString, urlString)); m_frame.loader().client().updateGlobalHistory(); } diff --git a/Source/WebCore/loader/HistoryController.h b/Source/WebCore/loader/HistoryController.h index 5d6c01cb6..3d25e289b 100644 --- a/Source/WebCore/loader/HistoryController.h +++ b/Source/WebCore/loader/HistoryController.h @@ -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. * @@ -27,11 +27,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HistoryController_h -#define HistoryController_h +#pragma once #include "FrameLoaderTypes.h" -#include "SerializedScriptValue.h" #include <wtf/Noncopyable.h> #include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> @@ -41,24 +39,26 @@ namespace WebCore { class Frame; class HistoryItem; class SerializedScriptValue; -class StringWithDirection; + +struct StringWithDirection; class HistoryController { WTF_MAKE_NONCOPYABLE(HistoryController); + WTF_MAKE_FAST_ALLOCATED; public: enum HistoryUpdateType { UpdateAll, UpdateAllExceptBackForwardList }; explicit HistoryController(Frame&); ~HistoryController(); - void saveScrollPositionAndViewStateToItem(HistoryItem*); + WEBCORE_EXPORT void saveScrollPositionAndViewStateToItem(HistoryItem*); void clearScrollPositionAndViewState(); - void restoreScrollPositionAndViewState(); + WEBCORE_EXPORT void restoreScrollPositionAndViewState(); void updateBackForwardListForFragmentScroll(); void saveDocumentState(); - void saveDocumentAndScrollState(); + WEBCORE_EXPORT void saveDocumentAndScrollState(); void restoreDocumentState(); void invalidateCurrentItemCachedPage(); @@ -76,34 +76,35 @@ public: void setCurrentItem(HistoryItem*); void setCurrentItemTitle(const StringWithDirection&); bool currentItemShouldBeReplaced() const; - void replaceCurrentItem(HistoryItem*); + WEBCORE_EXPORT void replaceCurrentItem(HistoryItem*); HistoryItem* previousItem() const { return m_previousItem.get(); } + void clearPreviousItem(); HistoryItem* provisionalItem() const { return m_provisionalItem.get(); } void setProvisionalItem(HistoryItem*); - void pushState(PassRefPtr<SerializedScriptValue>, const String& title, const String& url); - void replaceState(PassRefPtr<SerializedScriptValue>, const String& title, const String& url); + void pushState(RefPtr<SerializedScriptValue>&&, const String& title, const String& url); + void replaceState(RefPtr<SerializedScriptValue>&&, const String& title, const String& url); void setDefersLoading(bool); private: friend class Page; - bool shouldStopLoadingForHistoryItem(HistoryItem*) const; - void goToItem(HistoryItem*, FrameLoadType); + bool shouldStopLoadingForHistoryItem(HistoryItem&) const; + void goToItem(HistoryItem&, FrameLoadType); - void initializeItem(HistoryItem*); - PassRefPtr<HistoryItem> createItem(); - PassRefPtr<HistoryItem> createItemTree(Frame& targetFrame, bool clipAtTarget); + void initializeItem(HistoryItem&); + Ref<HistoryItem> createItem(); + Ref<HistoryItem> createItemTree(Frame& targetFrame, bool clipAtTarget); - void recursiveSetProvisionalItem(HistoryItem*, HistoryItem*, FrameLoadType); - void recursiveGoToItem(HistoryItem*, HistoryItem*, FrameLoadType); + void recursiveSetProvisionalItem(HistoryItem&, HistoryItem*); + void recursiveGoToItem(HistoryItem&, HistoryItem*, FrameLoadType); bool isReplaceLoadTypeWithProvisionalItem(FrameLoadType); bool isReloadTypeWithProvisionalItem(FrameLoadType); void recursiveUpdateForCommit(); void recursiveUpdateForSameDocumentNavigation(); - bool itemsAreClones(HistoryItem*, HistoryItem*) const; + bool itemsAreClones(HistoryItem&, HistoryItem*) const; bool currentFramesMatchItem(HistoryItem*) const; void updateBackForwardListClippedAtTarget(bool doClip); void updateCurrentItem(); @@ -122,5 +123,3 @@ private: }; } // namespace WebCore - -#endif // HistoryController_h diff --git a/Source/WebCore/loader/ImageLoader.cpp b/Source/WebCore/loader/ImageLoader.cpp index 13c32ffa8..b992343ff 100644 --- a/Source/WebCore/loader/ImageLoader.cpp +++ b/Source/WebCore/loader/ImageLoader.cpp @@ -29,18 +29,17 @@ #include "Document.h" #include "Element.h" #include "Event.h" +#include "EventNames.h" #include "EventSender.h" #include "Frame.h" #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "HTMLParserIdioms.h" +#include "Page.h" #include "RenderImage.h" -#include "ScriptCallStack.h" -#include "SecurityOrigin.h" - -#if ENABLE(SVG) #include "RenderSVGImage.h" -#endif +#include <wtf/NeverDestroyed.h> + #if ENABLE(VIDEO) #include "RenderVideo.h" #endif @@ -55,8 +54,7 @@ template<> struct ValueCheck<WebCore::ImageLoader*> { { if (!p) return; - ASSERT(p->element()); - ValueCheck<WebCore::Element*>::checkConsistency(p->element()); + ValueCheck<WebCore::Element*>::checkConsistency(&p->element()); } }; @@ -67,32 +65,32 @@ namespace WebCore { static ImageEventSender& beforeLoadEventSender() { - DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent)); + static NeverDestroyed<ImageEventSender> sender(eventNames().beforeloadEvent); return sender; } static ImageEventSender& loadEventSender() { - DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent)); + static NeverDestroyed<ImageEventSender> sender(eventNames().loadEvent); return sender; } static ImageEventSender& errorEventSender() { - DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().errorEvent)); + static NeverDestroyed<ImageEventSender> sender(eventNames().errorEvent); return sender; } static inline bool pageIsBeingDismissed(Document& document) { Frame* frame = document.frame(); - return frame && frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal; + return frame && frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None; } -ImageLoader::ImageLoader(Element* element) +ImageLoader::ImageLoader(Element& element) : m_element(element) - , m_image(0) - , m_derefElementTimer(this, &ImageLoader::timerFired) + , m_image(nullptr) + , m_derefElementTimer(*this, &ImageLoader::timerFired) , m_hasPendingBeforeLoadEvent(false) , m_hasPendingLoadEvent(false) , m_hasPendingErrorEvent(false) @@ -105,58 +103,51 @@ ImageLoader::ImageLoader(Element* element) ImageLoader::~ImageLoader() { if (m_image) - m_image->removeClient(this); + m_image->removeClient(*this); - ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(this)); + ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(*this)); if (m_hasPendingBeforeLoadEvent) - beforeLoadEventSender().cancelEvent(this); + beforeLoadEventSender().cancelEvent(*this); - ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this)); + ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(*this)); if (m_hasPendingLoadEvent) - loadEventSender().cancelEvent(this); + loadEventSender().cancelEvent(*this); - ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this)); + ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(*this)); if (m_hasPendingErrorEvent) - errorEventSender().cancelEvent(this); - - // If the ImageLoader is being destroyed but it is still protecting its image-loading Element, - // remove that protection here. - if (m_elementIsProtected) - m_element->deref(); + errorEventSender().cancelEvent(*this); } -void ImageLoader::setImage(CachedImage* newImage) +void ImageLoader::clearImage() { - setImageWithoutConsideringPendingLoadEvent(newImage); + clearImageWithoutConsideringPendingLoadEvent(); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. updatedHasPendingEvent(); } -void ImageLoader::setImageWithoutConsideringPendingLoadEvent(CachedImage* newImage) +void ImageLoader::clearImageWithoutConsideringPendingLoadEvent() { ASSERT(m_failedLoadURL.isEmpty()); CachedImage* oldImage = m_image.get(); - if (newImage != oldImage) { - m_image = newImage; + if (oldImage) { + m_image = nullptr; if (m_hasPendingBeforeLoadEvent) { - beforeLoadEventSender().cancelEvent(this); + beforeLoadEventSender().cancelEvent(*this); m_hasPendingBeforeLoadEvent = false; } if (m_hasPendingLoadEvent) { - loadEventSender().cancelEvent(this); + loadEventSender().cancelEvent(*this); m_hasPendingLoadEvent = false; } if (m_hasPendingErrorEvent) { - errorEventSender().cancelEvent(this); + errorEventSender().cancelEvent(*this); m_hasPendingErrorEvent = false; } m_imageComplete = true; - if (newImage) - newImage->addClient(this); if (oldImage) - oldImage->removeClient(this); + oldImage->removeClient(*this); } if (RenderImageResource* imageResource = renderImageResource()) @@ -165,40 +156,42 @@ void ImageLoader::setImageWithoutConsideringPendingLoadEvent(CachedImage* newIma void ImageLoader::updateFromElement() { - // If we're not making renderers for the page, then don't load images. We don't want to slow + // If we're not making renderers for the page, then don't load images. We don't want to slow // down the raw HTML parsing case by loading images we don't intend to display. - Document& document = m_element->document(); + Document& document = element().document(); if (!document.hasLivingRenderTree()) return; - AtomicString attr = m_element->imageSourceURL(); + AtomicString attr = element().imageSourceURL(); - if (attr == m_failedLoadURL) + // Avoid loading a URL we already failed to load. + if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL) return; // Do not load any image if the 'src' attribute is missing or if it is // an empty string. - CachedResourceHandle<CachedImage> newImage = 0; + CachedResourceHandle<CachedImage> newImage = nullptr; if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) { - CachedResourceRequest request(ResourceRequest(document.completeURL(sourceURI(attr)))); + ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); + options.contentSecurityPolicyImposition = element().isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck; + options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set; + + CachedResourceRequest request(ResourceRequest(document.completeURL(sourceURI(attr))), options); request.setInitiator(element()); - String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr); - if (!crossOriginMode.isNull()) { - StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials; - updateRequestForAccessControl(request.mutableResourceRequest(), document.securityOrigin(), allowCredentials); - } + request.setAsPotentiallyCrossOrigin(element().attributeWithoutSynchronization(HTMLNames::crossoriginAttr), document); if (m_loadManually) { - bool autoLoadOtherImages = document.cachedResourceLoader()->autoLoadImages(); - document.cachedResourceLoader()->setAutoLoadImages(false); - newImage = new CachedImage(request.resourceRequest()); + bool autoLoadOtherImages = document.cachedResourceLoader().autoLoadImages(); + document.cachedResourceLoader().setAutoLoadImages(false); + newImage = new CachedImage(WTFMove(request), m_element.document().page()->sessionID()); + newImage->setStatus(CachedResource::Pending); newImage->setLoading(true); - newImage->setOwningCachedResourceLoader(document.cachedResourceLoader()); - document.cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage.get()); - document.cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages); + newImage->setOwningCachedResourceLoader(&document.cachedResourceLoader()); + document.cachedResourceLoader().m_documentResources.set(newImage->url(), newImage.get()); + document.cachedResourceLoader().setAutoLoadImages(autoLoadOtherImages); } else - newImage = document.cachedResourceLoader()->requestImage(request); + newImage = document.cachedResourceLoader().requestImage(WTFMove(request)); // If we do not have an image here, it means that a cross-site // violation occurred, or that the image was blocked via Content @@ -207,24 +200,24 @@ void ImageLoader::updateFromElement() if (!newImage && !pageIsBeingDismissed(document)) { m_failedLoadURL = attr; m_hasPendingErrorEvent = true; - errorEventSender().dispatchEventSoon(this); + errorEventSender().dispatchEventSoon(*this); } else clearFailedLoadURL(); } else if (!attr.isNull()) { // Fire an error event if the url is empty. m_failedLoadURL = attr; m_hasPendingErrorEvent = true; - errorEventSender().dispatchEventSoon(this); + errorEventSender().dispatchEventSoon(*this); } - + CachedImage* oldImage = m_image.get(); if (newImage != oldImage) { if (m_hasPendingBeforeLoadEvent) { - beforeLoadEventSender().cancelEvent(this); + beforeLoadEventSender().cancelEvent(*this); m_hasPendingBeforeLoadEvent = false; } if (m_hasPendingLoadEvent) { - loadEventSender().cancelEvent(this); + loadEventSender().cancelEvent(*this); m_hasPendingLoadEvent = false; } @@ -233,7 +226,7 @@ void ImageLoader::updateFromElement() // this load and we should not cancel the event. // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. if (m_hasPendingErrorEvent && newImage) { - errorEventSender().cancelEvent(this); + errorEventSender().cancelEvent(*this); m_hasPendingErrorEvent = false; } @@ -247,17 +240,19 @@ void ImageLoader::updateFromElement() if (!document.hasListenerType(Document::BEFORELOAD_LISTENER)) dispatchPendingBeforeLoadEvent(); else - beforeLoadEventSender().dispatchEventSoon(this); + beforeLoadEventSender().dispatchEventSoon(*this); } else updateRenderer(); // If newImage is cached, addClient() will result in the load event // being queued to fire. Ensure this happens after beforeload is // dispatched. - newImage->addClient(this); + newImage->addClient(*this); + } + if (oldImage) { + oldImage->removeClient(*this); + updateRenderer(); } - if (oldImage) - oldImage->removeClient(this); } if (RenderImageResource* imageResource = renderImageResource()) @@ -274,10 +269,10 @@ void ImageLoader::updateFromElementIgnoringPreviousError() updateFromElement(); } -void ImageLoader::notifyFinished(CachedResource* resource) +void ImageLoader::notifyFinished(CachedResource& resource) { ASSERT(m_failedLoadURL.isEmpty()); - ASSERT(resource == m_image.get()); + ASSERT_UNUSED(resource, &resource == m_image.get()); m_imageComplete = true; if (!hasPendingBeforeLoadEvent()) @@ -286,17 +281,14 @@ void ImageLoader::notifyFinished(CachedResource* resource) if (!m_hasPendingLoadEvent) return; - if (m_element->fastHasAttribute(HTMLNames::crossoriginAttr) - && !m_element->document().securityOrigin()->canRequest(image()->response().url()) - && !resource->passesAccessControlCheck(m_element->document().securityOrigin())) { - - setImageWithoutConsideringPendingLoadEvent(0); + if (m_image->resourceError().isAccessControl()) { + clearImageWithoutConsideringPendingLoadEvent(); m_hasPendingErrorEvent = true; - errorEventSender().dispatchEventSoon(this); + errorEventSender().dispatchEventSoon(*this); - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin image load denied by Cross-Origin Resource Sharing policy."))); - m_element->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, consoleMessage); + static NeverDestroyed<String> consoleMessage(ASCIILiteral("Cross-origin image load denied by Cross-Origin Resource Sharing policy.")); + element().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage); ASSERT(!m_hasPendingLoadEvent); @@ -306,7 +298,7 @@ void ImageLoader::notifyFinished(CachedResource* resource) return; } - if (resource->wasCanceled()) { + if (m_image->wasCanceled()) { m_hasPendingLoadEvent = false; // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. @@ -314,28 +306,26 @@ void ImageLoader::notifyFinished(CachedResource* resource) return; } - loadEventSender().dispatchEventSoon(this); + loadEventSender().dispatchEventSoon(*this); } RenderImageResource* ImageLoader::renderImageResource() { - auto renderer = m_element->renderer(); + auto* renderer = element().renderer(); if (!renderer) return nullptr; // We don't return style generated image because it doesn't belong to the ImageLoader. // See <https://bugs.webkit.org/show_bug.cgi?id=42840> - if (renderer->isRenderImage() && !toRenderImage(*renderer).isGeneratedContent()) - return &toRenderImage(*renderer).imageResource(); + if (is<RenderImage>(*renderer) && !downcast<RenderImage>(*renderer).isGeneratedContent()) + return &downcast<RenderImage>(*renderer).imageResource(); -#if ENABLE(SVG) - if (renderer->isSVGImage()) - return &toRenderSVGImage(renderer)->imageResource(); -#endif + if (is<RenderSVGImage>(*renderer)) + return &downcast<RenderSVGImage>(*renderer).imageResource(); #if ENABLE(VIDEO) - if (renderer->isVideo()) - return &toRenderVideo(*renderer).imageResource(); + if (is<RenderVideo>(*renderer)) + return &downcast<RenderVideo>(*renderer).imageResource(); #endif return nullptr; @@ -349,7 +339,7 @@ void ImageLoader::updateRenderer() return; // Only update the renderer if it doesn't have an image or if what we have - // is a complete image. This prevents flickering in the case where a dynamic + // is a complete image. This prevents flickering in the case where a dynamic // change is happening between two images. CachedImage* cachedImage = imageResource->cachedImage(); if (m_image != cachedImage && (m_imageComplete || !cachedImage)) @@ -371,16 +361,16 @@ void ImageLoader::updatedHasPendingEvent() if (m_derefElementTimer.isActive()) m_derefElementTimer.stop(); else - m_element->ref(); + m_protectedElement = &element(); } else { ASSERT(!m_derefElementTimer.isActive()); m_derefElementTimer.startOneShot(0); } } -void ImageLoader::timerFired(Timer<ImageLoader>&) +void ImageLoader::timerFired() { - m_element->deref(); + m_protectedElement = nullptr; } void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender) @@ -401,23 +391,28 @@ void ImageLoader::dispatchPendingBeforeLoadEvent() return; if (!m_image) return; - if (!m_element->document().hasLivingRenderTree()) + if (!element().document().hasLivingRenderTree()) return; m_hasPendingBeforeLoadEvent = false; - if (m_element->dispatchBeforeLoadEvent(m_image->url())) { + Ref<Document> originalDocument = element().document(); + if (element().dispatchBeforeLoadEvent(m_image->url())) { + bool didEventListenerDisconnectThisElement = !element().isConnected() || &element().document() != originalDocument.ptr(); + if (didEventListenerDisconnectThisElement) + return; + updateRenderer(); return; } if (m_image) { - m_image->removeClient(this); - m_image = 0; + m_image->removeClient(*this); + m_image = nullptr; } - loadEventSender().cancelEvent(this); + loadEventSender().cancelEvent(*this); m_hasPendingLoadEvent = false; - if (isHTMLObjectElement(m_element)) - toHTMLObjectElement(m_element)->renderFallbackContent(); + if (is<HTMLObjectElement>(element())) + downcast<HTMLObjectElement>(element()).renderFallbackContent(); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. @@ -431,7 +426,7 @@ void ImageLoader::dispatchPendingLoadEvent() if (!m_image) return; m_hasPendingLoadEvent = false; - if (m_element->document().hasLivingRenderTree()) + if (element().document().hasLivingRenderTree()) dispatchLoadEvent(); // Only consider updating the protection ref-count of the Element immediately before returning @@ -444,8 +439,8 @@ void ImageLoader::dispatchPendingErrorEvent() if (!m_hasPendingErrorEvent) return; m_hasPendingErrorEvent = false; - if (m_element->document().hasLivingRenderTree()) - m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false)); + if (element().document().hasLivingRenderTree()) + element().dispatchEvent(Event::create(eventNames().errorEvent, false, false)); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. @@ -470,12 +465,12 @@ void ImageLoader::dispatchPendingErrorEvents() void ImageLoader::elementDidMoveToNewDocument() { clearFailedLoadURL(); - setImage(0); + clearImage(); } inline void ImageLoader::clearFailedLoadURL() { - m_failedLoadURL = AtomicString(); + m_failedLoadURL = nullAtom; } } diff --git a/Source/WebCore/loader/ImageLoader.h b/Source/WebCore/loader/ImageLoader.h index d76fa3a85..7717667f6 100644 --- a/Source/WebCore/loader/ImageLoader.h +++ b/Source/WebCore/loader/ImageLoader.h @@ -20,8 +20,7 @@ * */ -#ifndef ImageLoader_h -#define ImageLoader_h +#pragma once #include "CachedImageClient.h" #include "CachedResourceHandle.h" @@ -38,8 +37,8 @@ template<typename T> class EventSender; typedef EventSender<ImageLoader> ImageEventSender; class ImageLoader : public CachedImageClient { + WTF_MAKE_FAST_ALLOCATED; public: - explicit ImageLoader(Element*); virtual ~ImageLoader(); // This function should be called when the element is attached to a document; starts @@ -52,11 +51,13 @@ public: void elementDidMoveToNewDocument(); - Element* element() const { return m_element; } + Element& element() { return m_element; } + const Element& element() const { return m_element; } + bool imageComplete() const { return m_imageComplete; } CachedImage* image() const { return m_image.get(); } - void setImage(CachedImage*); // Cancels pending beforeload and load events, and doesn't dispatch new ones. + void clearImage(); // Cancels pending beforeload and load events, and doesn't dispatch new ones. void setLoadManually(bool loadManually) { m_loadManually = loadManually; } @@ -70,7 +71,8 @@ public: static void dispatchPendingErrorEvents(); protected: - virtual void notifyFinished(CachedResource*) override; + explicit ImageLoader(Element&); + void notifyFinished(CachedResource&) override; private: virtual void dispatchLoadEvent() = 0; @@ -85,14 +87,15 @@ private: RenderImageResource* renderImageResource(); void updateRenderer(); - void setImageWithoutConsideringPendingLoadEvent(CachedImage*); + void clearImageWithoutConsideringPendingLoadEvent(); void clearFailedLoadURL(); - void timerFired(Timer<ImageLoader>&); + void timerFired(); - Element* m_element; + Element& m_element; CachedResourceHandle<CachedImage> m_image; - Timer<ImageLoader> m_derefElementTimer; + Timer m_derefElementTimer; + RefPtr<Element> m_protectedElement; AtomicString m_failedLoadURL; bool m_hasPendingBeforeLoadEvent : 1; bool m_hasPendingLoadEvent : 1; @@ -103,5 +106,3 @@ private: }; } - -#endif diff --git a/Source/WebCore/loader/LinkHeader.cpp b/Source/WebCore/loader/LinkHeader.cpp new file mode 100644 index 000000000..e2546d688 --- /dev/null +++ b/Source/WebCore/loader/LinkHeader.cpp @@ -0,0 +1,365 @@ +/* + * Copyright 2015 The Chromium Authors. All rights reserved. + * Copyright (C) 2016 Akamai Technologies 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "LinkHeader.h" + +#include "ParsingUtilities.h" + +namespace WebCore { + +// LWSP definition in https://www.ietf.org/rfc/rfc0822.txt +template <typename CharacterType> +static bool isSpaceOrTab(CharacterType chr) +{ + return (chr == ' ') || (chr == '\t'); +} + +template <typename CharacterType> +static bool isNotURLTerminatingChar(CharacterType chr) +{ + return (chr != '>'); +} + +template <typename CharacterType> +static bool isValidParameterNameChar(CharacterType chr) +{ + // A separator, CTL or '%', '*' or '\'' means the char is not valid. + // Definition as attr-char at https://tools.ietf.org/html/rfc5987 + // CTL and separators are defined in https://tools.ietf.org/html/rfc2616#section-2.2 + // Valid chars are alpha-numeric and any of !#$&+-.^_`|~" + if ((chr >= '^' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || (chr >= '!' && chr <= '$') || chr == '&' || chr == '+' || chr == '-' || chr == '.') + return true; + return false; +} + +template <typename CharacterType> +static bool isParameterValueEnd(CharacterType chr) +{ + return chr == ';' || chr == ','; +} + +template <typename CharacterType> +static bool isParameterValueChar(CharacterType chr) +{ + return !isSpaceOrTab(chr) && !isParameterValueEnd(chr); +} + +// Verify that the parameter is a link-extension which according to spec doesn't have to have a value. +static bool isExtensionParameter(LinkHeader::LinkParameterName name) +{ + return name >= LinkHeader::LinkParameterUnknown; +} + +// Before: +// +// <cat.jpg>; rel=preload +// ^ ^ +// position end +// +// After (if successful: otherwise the method returns false) +// +// <cat.jpg>; rel=preload +// ^ ^ +// position end +template <typename CharacterType> +static std::optional<String> findURLBoundaries(CharacterType*& position, CharacterType* const end) +{ + ASSERT(position <= end); + skipWhile<CharacterType, isSpaceOrTab>(position, end); + if (!skipExactly<CharacterType>(position, end, '<')) + return std::nullopt; + skipWhile<CharacterType, isSpaceOrTab>(position, end); + + CharacterType* urlStart = position; + skipWhile<CharacterType, isNotURLTerminatingChar>(position, end); + CharacterType* urlEnd = position; + skipUntil<CharacterType>(position, end, '>'); + if (!skipExactly<CharacterType>(position, end, '>')) + return std::nullopt; + + return String(urlStart, urlEnd - urlStart); +} + +template <typename CharacterType> +static bool invalidParameterDelimiter(CharacterType*& position, CharacterType* const end) +{ + ASSERT(position <= end); + return (!skipExactly<CharacterType>(position, end, ';') && (position < end) && (*position != ',')); +} + +template <typename CharacterType> +static bool validFieldEnd(CharacterType*& position, CharacterType* const end) +{ + ASSERT(position <= end); + return (position == end || *position == ','); +} + +// Before: +// +// <cat.jpg>; rel=preload +// ^ ^ +// position end +// +// After (if successful: otherwise the method returns false, and modifies the isValid boolean accordingly) +// +// <cat.jpg>; rel=preload +// ^ ^ +// position end +template <typename CharacterType> +static bool parseParameterDelimiter(CharacterType*& position, CharacterType* const end, bool& isValid) +{ + ASSERT(position <= end); + isValid = true; + skipWhile<CharacterType, isSpaceOrTab>(position, end); + if (invalidParameterDelimiter(position, end)) { + isValid = false; + return false; + } + skipWhile<CharacterType, isSpaceOrTab>(position, end); + if (validFieldEnd(position, end)) + return false; + return true; +} + +static LinkHeader::LinkParameterName paramterNameFromString(String name) +{ + if (equalLettersIgnoringASCIICase(name, "rel")) + return LinkHeader::LinkParameterRel; + if (equalLettersIgnoringASCIICase(name, "anchor")) + return LinkHeader::LinkParameterAnchor; + if (equalLettersIgnoringASCIICase(name, "crossorigin")) + return LinkHeader::LinkParameterCrossOrigin; + if (equalLettersIgnoringASCIICase(name, "title")) + return LinkHeader::LinkParameterTitle; + if (equalLettersIgnoringASCIICase(name, "media")) + return LinkHeader::LinkParameterMedia; + if (equalLettersIgnoringASCIICase(name, "type")) + return LinkHeader::LinkParameterType; + if (equalLettersIgnoringASCIICase(name, "rev")) + return LinkHeader::LinkParameterRev; + if (equalLettersIgnoringASCIICase(name, "hreflang")) + return LinkHeader::LinkParameterHreflang; + if (equalLettersIgnoringASCIICase(name, "as")) + return LinkHeader::LinkParameterAs; + return LinkHeader::LinkParameterUnknown; +} + +// Before: +// +// <cat.jpg>; rel=preload +// ^ ^ +// position end +// +// After (if successful: otherwise the method returns false) +// +// <cat.jpg>; rel=preload +// ^ ^ +// position end +template <typename CharacterType> +static bool parseParameterName(CharacterType*& position, CharacterType* const end, LinkHeader::LinkParameterName& name) +{ + ASSERT(position <= end); + CharacterType* nameStart = position; + skipWhile<CharacterType, isValidParameterNameChar>(position, end); + CharacterType* nameEnd = position; + skipWhile<CharacterType, isSpaceOrTab>(position, end); + bool hasEqual = skipExactly<CharacterType>(position, end, '='); + skipWhile<CharacterType, isSpaceOrTab>(position, end); + name = paramterNameFromString(String(nameStart, nameEnd - nameStart)); + if (hasEqual) + return true; + bool validParameterValueEnd = (position == end) || isParameterValueEnd(*position); + return validParameterValueEnd && isExtensionParameter(name); +} + +// Before: +// +// <cat.jpg>; rel="preload"; type="image/jpeg"; +// ^ ^ +// position end +// +// After (if the parameter starts with a quote, otherwise the method returns false) +// +// <cat.jpg>; rel="preload"; type="image/jpeg"; +// ^ ^ +// position end +template <typename CharacterType> +static bool skipQuotesIfNeeded(CharacterType*& position, CharacterType* const end, bool& completeQuotes) +{ + ASSERT(position <= end); + unsigned char quote; + if (skipExactly<CharacterType>(position, end, '\'')) + quote = '\''; + else if (skipExactly<CharacterType>(position, end, '"')) + quote = '"'; + else + return false; + + while (!completeQuotes && position < end) { + skipUntil(position, end, static_cast<CharacterType>(quote)); + if (*(position - 1) != '\\') + completeQuotes = true; + completeQuotes = skipExactly(position, end, static_cast<CharacterType>(quote)) && completeQuotes; + } + return true; +} + +// Before: +// +// <cat.jpg>; rel=preload; foo=bar +// ^ ^ +// position end +// +// After (if successful: otherwise the method returns false) +// +// <cat.jpg>; rel=preload; foo=bar +// ^ ^ +// position end +template <typename CharacterType> +static bool parseParameterValue(CharacterType*& position, CharacterType* const end, String& value) +{ + ASSERT(position <= end); + CharacterType* valueStart = position; + CharacterType* valueEnd = position; + bool completeQuotes = false; + bool hasQuotes = skipQuotesIfNeeded(position, end, completeQuotes); + if (!hasQuotes) + skipWhile<CharacterType, isParameterValueChar>(position, end); + valueEnd = position; + skipWhile<CharacterType, isSpaceOrTab>(position, end); + if ((!completeQuotes && valueStart == valueEnd) || (position != end && !isParameterValueEnd(*position))) { + value = String(""); + return false; + } + if (hasQuotes) + ++valueStart; + if (completeQuotes) + --valueEnd; + ASSERT(valueEnd >= valueStart); + value = String(valueStart, valueEnd - valueStart); + return !hasQuotes || completeQuotes; +} + +void LinkHeader::setValue(LinkParameterName name, String value) +{ + switch (name) { + case LinkParameterRel: + if (!m_rel) + m_rel = value; + break; + case LinkParameterAnchor: + m_isValid = false; + break; + case LinkParameterCrossOrigin: + m_crossOrigin = value; + break; + case LinkParameterAs: + m_as = value; + break; + case LinkParameterType: + m_mimeType = value; + break; + case LinkParameterMedia: + m_media = value; + break; + case LinkParameterTitle: + case LinkParameterRev: + case LinkParameterHreflang: + case LinkParameterUnknown: + // These parameters are not yet supported, so they are currently ignored. + break; + } + // FIXME: Add support for more header parameters as neccessary. +} + +template <typename CharacterType> +static void findNextHeader(CharacterType*& position, CharacterType* const end) +{ + ASSERT(position <= end); + skipUntil<CharacterType>(position, end, ','); + skipExactly<CharacterType>(position, end, ','); +} + +template <typename CharacterType> +LinkHeader::LinkHeader(CharacterType*& position, CharacterType* const end) +{ + ASSERT(position <= end); + auto urlResult = findURLBoundaries(position, end); + if (urlResult == std::nullopt) { + m_isValid = false; + findNextHeader(position, end); + return; + } + m_url = urlResult.value(); + + while (m_isValid && position < end) { + if (!parseParameterDelimiter(position, end, m_isValid)) { + findNextHeader(position, end); + return; + } + + LinkParameterName parameterName; + if (!parseParameterName(position, end, parameterName)) { + findNextHeader(position, end); + m_isValid = false; + return; + } + + String parameterValue; + if (!parseParameterValue(position, end, parameterValue) && !isExtensionParameter(parameterName)) { + findNextHeader(position, end); + m_isValid = false; + return; + } + + setValue(parameterName, parameterValue); + } + findNextHeader(position, end); +} + +LinkHeaderSet::LinkHeaderSet(const String& header) +{ + if (header.isNull()) + return; + + if (header.is8Bit()) + init(header.characters8(), header.length()); + else + init(header.characters16(), header.length()); +} + +template <typename CharacterType> +void LinkHeaderSet::init(CharacterType* headerValue, size_t length) +{ + CharacterType* position = headerValue; + CharacterType* const end = headerValue + length; + while (position < end) + m_headerSet.append(LinkHeader(position, end)); +} + +} // namespace WebCore + diff --git a/Source/WebCore/loader/LinkHeader.h b/Source/WebCore/loader/LinkHeader.h new file mode 100644 index 000000000..8e3547419 --- /dev/null +++ b/Source/WebCore/loader/LinkHeader.h @@ -0,0 +1,87 @@ +/* + * Copyright 2015 The Chromium Authors. All rights reserved. + * Copyright (C) 2016 Akamai Technologies 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +#pragma once + +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class LinkHeader { +public: + template <typename CharacterType> + LinkHeader(CharacterType*& position, CharacterType* const end); + + const String& url() const { return m_url; } + const String& rel() const { return m_rel; } + const String& as() const { return m_as; } + const String& mimeType() const { return m_mimeType; } + const String& media() const { return m_media; } + const String& crossOrigin() const { return m_crossOrigin; } + bool valid() const { return m_isValid; } + + enum LinkParameterName { + LinkParameterRel, + LinkParameterAnchor, + LinkParameterTitle, + LinkParameterMedia, + LinkParameterType, + LinkParameterRev, + LinkParameterHreflang, + // Beyond this point, only link-extension parameters + LinkParameterUnknown, + LinkParameterCrossOrigin, + LinkParameterAs, + }; + +private: + void setValue(LinkParameterName, String value); + + String m_url; + String m_rel; + String m_as; + String m_mimeType; + String m_media; + String m_crossOrigin; + bool m_isValid { true }; +}; + +class LinkHeaderSet { +public: + LinkHeaderSet(const String& header); + + Vector<LinkHeader>::const_iterator begin() const { return m_headerSet.begin(); } + Vector<LinkHeader>::const_iterator end() const { return m_headerSet.end(); } + +private: + template <typename CharacterType> + void init(CharacterType* headerValue, size_t length); + + Vector<LinkHeader> m_headerSet; +}; + +} // namespace WebCore + diff --git a/Source/WebCore/loader/LinkLoader.cpp b/Source/WebCore/loader/LinkLoader.cpp index e63bbf0c2..9b53ce87d 100644 --- a/Source/WebCore/loader/LinkLoader.cpp +++ b/Source/WebCore/loader/LinkLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -37,101 +38,208 @@ #include "CachedResourceLoader.h" #include "CachedResourceRequest.h" #include "ContainerNode.h" -#include "DNS.h" +#include "CrossOriginAccessControl.h" #include "Document.h" #include "Frame.h" +#include "FrameLoaderClient.h" #include "FrameView.h" +#include "LinkHeader.h" +#include "LinkPreloadResourceClients.h" #include "LinkRelAttribute.h" +#include "RuntimeEnabledFeatures.h" #include "Settings.h" #include "StyleResolver.h" namespace WebCore { -LinkLoader::LinkLoader(LinkLoaderClient* client) +LinkLoader::LinkLoader(LinkLoaderClient& client) : m_client(client) - , m_linkLoadTimer(this, &LinkLoader::linkLoadTimerFired) - , m_linkLoadingErrorTimer(this, &LinkLoader::linkLoadingErrorTimerFired) + , m_weakPtrFactory(this) { } LinkLoader::~LinkLoader() { if (m_cachedLinkResource) - m_cachedLinkResource->removeClient(this); + m_cachedLinkResource->removeClient(*this); + if (m_preloadResourceClient) + m_preloadResourceClient->clear(); } -void LinkLoader::linkLoadTimerFired(Timer<LinkLoader>& timer) +void LinkLoader::triggerEvents(const CachedResource& resource) { - ASSERT_UNUSED(timer, &timer == &m_linkLoadTimer); - m_client->linkLoaded(); + if (resource.errorOccurred()) + m_client.linkLoadingErrored(); + else + m_client.linkLoaded(); } -void LinkLoader::linkLoadingErrorTimerFired(Timer<LinkLoader>& timer) +void LinkLoader::notifyFinished(CachedResource& resource) { - ASSERT_UNUSED(timer, &timer == &m_linkLoadingErrorTimer); - m_client->linkLoadingErrored(); + ASSERT_UNUSED(resource, m_cachedLinkResource.get() == &resource); + + triggerEvents(*m_cachedLinkResource); + + m_cachedLinkResource->removeClient(*this); + m_cachedLinkResource = nullptr; } -void LinkLoader::notifyFinished(CachedResource* resource) +void LinkLoader::loadLinksFromHeader(const String& headerValue, const URL& baseURL, Document& document) { - ASSERT_UNUSED(resource, m_cachedLinkResource.get() == resource); + if (headerValue.isEmpty()) + return; + LinkHeaderSet headerSet(headerValue); + for (auto& header : headerSet) { + if (!header.valid() || header.url().isEmpty() || header.rel().isEmpty()) + continue; + + LinkRelAttribute relAttribute(header.rel()); + URL url(baseURL, header.url()); + // Sanity check to avoid re-entrancy here. + if (equalIgnoringFragmentIdentifier(url, baseURL)) + continue; + preloadIfNeeded(relAttribute, url, document, header.as(), header.crossOrigin(), nullptr, nullptr); + } +} - if (m_cachedLinkResource->errorOccurred()) - m_linkLoadingErrorTimer.startOneShot(0); - else - m_linkLoadTimer.startOneShot(0); +std::optional<CachedResource::Type> LinkLoader::resourceTypeFromAsAttribute(const String& as) +{ + if (as.isEmpty()) + return CachedResource::RawResource; + if (equalLettersIgnoringASCIICase(as, "image")) + return CachedResource::ImageResource; + if (equalLettersIgnoringASCIICase(as, "script")) + return CachedResource::Script; + if (equalLettersIgnoringASCIICase(as, "style")) + return CachedResource::CSSStyleSheet; + if (equalLettersIgnoringASCIICase(as, "media")) + return CachedResource::MediaResource; + if (equalLettersIgnoringASCIICase(as, "font")) + return CachedResource::FontResource; +#if ENABLE(VIDEO_TRACK) + if (equalLettersIgnoringASCIICase(as, "track")) + return CachedResource::TextTrackResource; +#endif + return std::nullopt; +} - m_cachedLinkResource->removeClient(this); - m_cachedLinkResource = 0; +static std::unique_ptr<LinkPreloadResourceClient> createLinkPreloadResourceClient(CachedResource& resource, LinkLoader& loader, CachedResource::Type type) +{ + switch (type) { + case CachedResource::ImageResource: + return LinkPreloadImageResourceClient::create(loader, static_cast<CachedImage&>(resource)); + case CachedResource::Script: + return LinkPreloadScriptResourceClient::create(loader, static_cast<CachedScript&>(resource)); + case CachedResource::CSSStyleSheet: + return LinkPreloadStyleResourceClient::create(loader, static_cast<CachedCSSStyleSheet&>(resource)); + case CachedResource::FontResource: + return LinkPreloadFontResourceClient::create(loader, static_cast<CachedFont&>(resource)); + case CachedResource::MediaResource: +#if ENABLE(VIDEO_TRACK) + case CachedResource::TextTrackResource: +#endif + case CachedResource::RawResource: + return LinkPreloadRawResourceClient::create(loader, static_cast<CachedRawResource&>(resource)); + case CachedResource::MainResource: +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif + case CachedResource::SVGDocumentResource: +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkSubresource: + case CachedResource::LinkPrefetch: +#endif + // None of these values is currently supported as an `as` value. + ASSERT_NOT_REACHED(); + } + return nullptr; } -bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const String& type, - const String& sizes, const URL& href, Document* document) +std::unique_ptr<LinkPreloadResourceClient> LinkLoader::preloadIfNeeded(const LinkRelAttribute& relAttribute, const URL& href, Document& document, const String& as, const String& crossOriginMode, LinkLoader* loader, LinkLoaderClient* client) { - // We'll record this URL per document, even if we later only use it in top level frames - if (relAttribute.m_iconType != InvalidIcon && href.isValid() && !href.isEmpty()) { - if (!m_client->shouldLoadLink()) - return false; - document->addIconURL(href.string(), type, sizes, relAttribute.m_iconType); + if (!document.loader() || !relAttribute.isLinkPreload) + return nullptr; + + ASSERT(RuntimeEnabledFeatures::sharedFeatures().linkPreloadEnabled()); + if (!href.isValid()) { + document.addConsoleMessage(MessageSource::Other, MessageLevel::Error, String("<link rel=preload> has an invalid `href` value")); + return nullptr; } + auto type = LinkLoader::resourceTypeFromAsAttribute(as); + if (!type) { + document.addConsoleMessage(MessageSource::Other, MessageLevel::Error, String("<link rel=preload> must have a valid `as` value")); + if (client) + client->linkLoadingErrored(); + return nullptr; + } + + ResourceRequest resourceRequest(document.completeURL(href)); + resourceRequest.setIgnoreForRequestCount(true); + CachedResourceRequest linkRequest(WTFMove(resourceRequest), CachedResourceLoader::defaultCachedResourceOptions(), CachedResource::defaultPriorityForResourceType(type.value())); + linkRequest.setInitiator("link"); + linkRequest.setIsLinkPreload(); + + linkRequest.setAsPotentiallyCrossOrigin(crossOriginMode, document); + CachedResourceHandle<CachedResource> cachedLinkResource = document.cachedResourceLoader().preload(type.value(), WTFMove(linkRequest)); + + if (cachedLinkResource && loader) + return createLinkPreloadResourceClient(*cachedLinkResource, *loader, type.value()); + return nullptr; +} + +void LinkLoader::cancelLoad() +{ + if (m_preloadResourceClient) + m_preloadResourceClient->clear(); +} - if (relAttribute.m_isDNSPrefetch) { - Settings* settings = document->settings(); +bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const URL& href, const String& as, const String& crossOrigin, Document& document) +{ + if (relAttribute.isDNSPrefetch) { // FIXME: The href attribute of the link element can be in "//hostname" form, and we shouldn't attempt // to complete that as URL <https://bugs.webkit.org/show_bug.cgi?id=48857>. - if (settings && settings->dnsPrefetchingEnabled() && href.isValid() && !href.isEmpty()) - prefetchDNS(href.host()); + if (document.settings().dnsPrefetchingEnabled() && href.isValid() && !href.isEmpty() && document.frame()) + document.frame()->loader().client().prefetchDNS(href.host()); + } + + if (m_client.shouldLoadLink()) { + auto resourceClient = preloadIfNeeded(relAttribute, href, document, as, crossOrigin, this, &m_client); + if (resourceClient) + m_preloadResourceClient = WTFMove(resourceClient); + else if (m_preloadResourceClient) + m_preloadResourceClient->clear(); } #if ENABLE(LINK_PREFETCH) - if ((relAttribute.m_isLinkPrefetch || relAttribute.m_isLinkSubresource) && href.isValid() && document->frame()) { - if (!m_client->shouldLoadLink()) + if ((relAttribute.isLinkPrefetch || relAttribute.isLinkSubresource) && href.isValid() && document.frame()) { + if (!m_client.shouldLoadLink()) return false; - ResourceLoadPriority priority = ResourceLoadPriorityUnresolved; + + std::optional<ResourceLoadPriority> priority; CachedResource::Type type = CachedResource::LinkPrefetch; - // We only make one request to the cachedresourcelodaer if multiple rel types are - // specified, - if (relAttribute.m_isLinkSubresource) { - priority = ResourceLoadPriorityLow; + if (relAttribute.isLinkSubresource) { + // We only make one request to the cached resource loader if multiple rel types are specified; + // this is the higher priority, which should overwrite the lower priority. + priority = ResourceLoadPriority::Low; type = CachedResource::LinkSubresource; } - CachedResourceRequest linkRequest(ResourceRequest(document->completeURL(href)), priority); - + if (m_cachedLinkResource) { - m_cachedLinkResource->removeClient(this); - m_cachedLinkResource = 0; + m_cachedLinkResource->removeClient(*this); + m_cachedLinkResource = nullptr; } - m_cachedLinkResource = document->cachedResourceLoader()->requestLinkResource(type, linkRequest); + ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); + options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck; + m_cachedLinkResource = document.cachedResourceLoader().requestLinkResource(type, CachedResourceRequest(ResourceRequest(document.completeURL(href)), options, priority)); if (m_cachedLinkResource) - m_cachedLinkResource->addClient(this); + m_cachedLinkResource->addClient(*this); } #endif return true; } -void LinkLoader::released() -{ -} - } diff --git a/Source/WebCore/loader/LinkLoader.h b/Source/WebCore/loader/LinkLoader.h index 9b5d8cb76..9c714729d 100644 --- a/Source/WebCore/loader/LinkLoader.h +++ b/Source/WebCore/loader/LinkLoader.h @@ -29,46 +29,44 @@ * */ -#ifndef LinkLoader_h -#define LinkLoader_h +#pragma once +#include "CachedResource.h" #include "CachedResourceClient.h" #include "CachedResourceHandle.h" #include "LinkLoaderClient.h" -#include "Timer.h" -#include <wtf/RefPtr.h> +#include <wtf/WeakPtr.h> namespace WebCore { class Document; class URL; -struct LinkRelAttribute; +class LinkPreloadResourceClient; -// The LinkLoader can load link rel types icon, dns-prefetch, subresource and prefetch. -class LinkLoader : public CachedResourceClient { +struct LinkRelAttribute; +class LinkLoader : private CachedResourceClient { public: - explicit LinkLoader(LinkLoaderClient*); + explicit LinkLoader(LinkLoaderClient&); virtual ~LinkLoader(); - // from CachedResourceClient - virtual void notifyFinished(CachedResource*); + bool loadLink(const LinkRelAttribute&, const URL&, const String& as, const String& crossOrigin, Document&); + static std::optional<CachedResource::Type> resourceTypeFromAsAttribute(const String& as); + static void loadLinksFromHeader(const String& headerValue, const URL& baseURL, Document&); - void released(); - bool loadLink(const LinkRelAttribute&, const String& type, const String& sizes, const URL&, Document*); + WeakPtr<LinkLoader> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } + void triggerEvents(const CachedResource&); + void cancelLoad(); private: - void linkLoadTimerFired(Timer<LinkLoader>&); - void linkLoadingErrorTimerFired(Timer<LinkLoader>&); - - LinkLoaderClient* m_client; + void notifyFinished(CachedResource&) override; + static std::unique_ptr<LinkPreloadResourceClient> preloadIfNeeded(const LinkRelAttribute&, const URL& href, Document&, const String& as, const String& crossOriginMode, LinkLoader*, LinkLoaderClient*); + LinkLoaderClient& m_client; CachedResourceHandle<CachedResource> m_cachedLinkResource; - Timer<LinkLoader> m_linkLoadTimer; - Timer<LinkLoader> m_linkLoadingErrorTimer; + std::unique_ptr<LinkPreloadResourceClient> m_preloadResourceClient; + WeakPtrFactory<LinkLoader> m_weakPtrFactory; }; - -} -#endif +} diff --git a/Source/WebCore/loader/LinkLoaderClient.h b/Source/WebCore/loader/LinkLoaderClient.h index 4350ee78c..f2f18e238 100644 --- a/Source/WebCore/loader/LinkLoaderClient.h +++ b/Source/WebCore/loader/LinkLoaderClient.h @@ -28,8 +28,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#ifndef LinkLoaderClient_h -#define LinkLoaderClient_h + +#pragma once namespace WebCore { @@ -44,6 +44,4 @@ public: // There is no notification for cancellation. }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/LinkPreloadResourceClients.cpp b/Source/WebCore/loader/LinkPreloadResourceClients.cpp new file mode 100644 index 000000000..1cd744300 --- /dev/null +++ b/Source/WebCore/loader/LinkPreloadResourceClients.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2016 The Chromium Authors. All rights reserved. + * Copyright (C) 2016 Akamai Technologies 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "LinkPreloadResourceClients.h" + +#include "LinkLoader.h" + +namespace WebCore { + +LinkPreloadResourceClient::LinkPreloadResourceClient(LinkLoader& loader, CachedResource& resource) +{ + m_loader = loader.createWeakPtr(); + m_resource = &resource; +} + +void LinkPreloadResourceClient::triggerEvents(const CachedResource& resource) +{ + if (m_loader) + m_loader->triggerEvents(resource); +} + +} diff --git a/Source/WebCore/loader/LinkPreloadResourceClients.h b/Source/WebCore/loader/LinkPreloadResourceClients.h new file mode 100644 index 000000000..2c3a7a4dd --- /dev/null +++ b/Source/WebCore/loader/LinkPreloadResourceClients.h @@ -0,0 +1,193 @@ +/* + * Copyright 2016 The Chromium Authors. All rights reserved. + * Copyright (C) 2016 Akamai Technologies 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +#pragma once + +#include "CachedCSSStyleSheet.h" +#include "CachedFont.h" +#include "CachedFontClient.h" +#include "CachedImage.h" +#include "CachedImageClient.h" +#include "CachedRawResource.h" +#include "CachedResourceLoader.h" +#include "CachedScript.h" +#include "CachedStyleSheetClient.h" + +#include <wtf/WeakPtr.h> + +namespace WebCore { + +class LinkLoader; + +class LinkPreloadResourceClient { +public: + virtual ~LinkPreloadResourceClient() { } + + void triggerEvents(const CachedResource&); + + virtual void clear() = 0; + +protected: + + LinkPreloadResourceClient(LinkLoader&, CachedResource&); + + void addResource(CachedResourceClient& client) + { + m_resource->addClient(client); + } + + void clearResource(CachedResourceClient& client) + { + if (m_resource) { + m_resource->cancelLoad(); + m_resource->removeClient(client); + } + m_resource = nullptr; + } + + CachedResource* ownedResource() { return m_resource.get(); } + +private: + WeakPtr<LinkLoader> m_loader; + CachedResourceHandle<CachedResource> m_resource; +}; + +class LinkPreloadScriptResourceClient: public LinkPreloadResourceClient, CachedResourceClient { +public: + static std::unique_ptr<LinkPreloadScriptResourceClient> create(LinkLoader& loader, CachedScript& resource) + { + return std::unique_ptr<LinkPreloadScriptResourceClient>(new LinkPreloadScriptResourceClient(loader, resource)); + } + + virtual ~LinkPreloadScriptResourceClient() { } + + + void notifyFinished(CachedResource& resource) override { triggerEvents(resource); } + + void clear() override { clearResource(*this); } + +private: + LinkPreloadScriptResourceClient(LinkLoader& loader, CachedScript& resource) + : LinkPreloadResourceClient(loader, resource) + { + addResource(*this); + } +}; + +class LinkPreloadStyleResourceClient: public LinkPreloadResourceClient, public CachedStyleSheetClient { +public: + static std::unique_ptr<LinkPreloadStyleResourceClient> create(LinkLoader& loader, CachedCSSStyleSheet& resource) + { + return std::unique_ptr<LinkPreloadStyleResourceClient>(new LinkPreloadStyleResourceClient(loader, resource)); + } + + virtual ~LinkPreloadStyleResourceClient() { } + + void setCSSStyleSheet(const String&, const URL&, const String&, const CachedCSSStyleSheet* resource) override + { + ASSERT(resource); + ASSERT(ownedResource() == resource); + triggerEvents(*resource); + } + + void clear() override { clearResource(*this); } + +private: + LinkPreloadStyleResourceClient(LinkLoader& loader, CachedCSSStyleSheet& resource) + : LinkPreloadResourceClient(loader, resource) + { + addResource(*this); + } +}; + +class LinkPreloadImageResourceClient: public LinkPreloadResourceClient, public CachedImageClient { +public: + static std::unique_ptr<LinkPreloadImageResourceClient> create(LinkLoader& loader, CachedImage& resource) + { + return std::unique_ptr<LinkPreloadImageResourceClient>(new LinkPreloadImageResourceClient(loader, resource)); + } + + virtual ~LinkPreloadImageResourceClient() { } + + void notifyFinished(CachedResource& resource) override { triggerEvents(resource); } + + void clear() override { clearResource(*this); } + +private: + LinkPreloadImageResourceClient(LinkLoader& loader, CachedImage& resource) + : LinkPreloadResourceClient(loader, dynamic_cast<CachedResource&>(resource)) + { + addResource(*this); + } +}; + +class LinkPreloadFontResourceClient: public LinkPreloadResourceClient, public CachedFontClient { +public: + static std::unique_ptr<LinkPreloadFontResourceClient> create(LinkLoader& loader, CachedFont& resource) + { + return std::unique_ptr<LinkPreloadFontResourceClient>(new LinkPreloadFontResourceClient(loader, resource)); + } + + virtual ~LinkPreloadFontResourceClient() { } + + void fontLoaded(CachedFont& resource) override + { + ASSERT(ownedResource() == &resource); + triggerEvents(resource); + } + + void clear() override { clearResource(*this); } + +private: + LinkPreloadFontResourceClient(LinkLoader& loader, CachedFont& resource) + : LinkPreloadResourceClient(loader, resource) + { + addResource(*this); + } +}; + +class LinkPreloadRawResourceClient: public LinkPreloadResourceClient, public CachedRawResourceClient { +public: + static std::unique_ptr<LinkPreloadRawResourceClient> create(LinkLoader& loader, CachedRawResource& resource) + { + return std::unique_ptr<LinkPreloadRawResourceClient>(new LinkPreloadRawResourceClient(loader, resource)); + } + + virtual ~LinkPreloadRawResourceClient() { } + + void notifyFinished(CachedResource& resource) override { triggerEvents(resource); } + + void clear() override { clearResource(*this); } + +private: + LinkPreloadRawResourceClient(LinkLoader& loader, CachedRawResource& resource) + : LinkPreloadResourceClient(loader, resource) + { + addResource(*this); + } +}; + +} diff --git a/Source/WebCore/loader/DocumentLoadTiming.cpp b/Source/WebCore/loader/LoadTiming.cpp index 3a02b2310..feb0a9b4e 100644 --- a/Source/WebCore/loader/DocumentLoadTiming.cpp +++ b/Source/WebCore/loader/LoadTiming.cpp @@ -24,73 +24,72 @@ */ #include "config.h" -#include "DocumentLoadTiming.h" +#include "LoadTiming.h" #include "Document.h" #include "DocumentLoader.h" #include "Frame.h" -#include "Page.h" #include "SecurityOrigin.h" #include <wtf/CurrentTime.h> #include <wtf/RefPtr.h> namespace WebCore { -DocumentLoadTiming::DocumentLoadTiming() - : m_referenceMonotonicTime(0.0) - , m_referenceWallTime(0.0) - , m_navigationStart(0.0) - , m_unloadEventStart(0.0) - , m_unloadEventEnd(0.0) - , m_redirectStart(0.0) - , m_redirectEnd(0.0) - , m_redirectCount(0) - , m_fetchStart(0.0) - , m_responseEnd(0.0) - , m_loadEventStart(0.0) - , m_loadEventEnd(0.0) - , m_hasCrossOriginRedirect(false) - , m_hasSameOriginAsPreviousDocument(false) +Seconds LoadTiming::secondsSinceStartTime(MonotonicTime timeStamp) const { -} - -double DocumentLoadTiming::monotonicTimeToZeroBasedDocumentTime(double monotonicTime) const -{ - if (!monotonicTime) - return 0.0; - return monotonicTime - m_referenceMonotonicTime; -} + if (!timeStamp) + return Seconds(0); -double DocumentLoadTiming::monotonicTimeToPseudoWallTime(double monotonicTime) const -{ - if (!monotonicTime) - return 0.0; - return m_referenceWallTime + monotonicTime - m_referenceMonotonicTime; + return timeStamp - m_referenceMonotonicTime; } -void DocumentLoadTiming::markNavigationStart() +WallTime LoadTiming::monotonicTimeToPseudoWallTime(MonotonicTime timeStamp) const { - ASSERT(!m_navigationStart && !m_referenceMonotonicTime && !m_referenceWallTime); + if (!timeStamp) + return WallTime::fromRawSeconds(0); - m_navigationStart = m_referenceMonotonicTime = monotonicallyIncreasingTime(); - m_referenceWallTime = currentTime(); + return m_referenceWallTime + (timeStamp - m_referenceMonotonicTime); } -void DocumentLoadTiming::setNavigationStart(double navigationStart) +void LoadTiming::markStartTime() { - ASSERT(m_referenceMonotonicTime && m_referenceWallTime); - m_navigationStart = navigationStart; + ASSERT(!m_startTime && !m_referenceMonotonicTime && !m_referenceWallTime); + + m_referenceMonotonicTime = m_startTime = MonotonicTime::now(); + m_referenceWallTime = WallTime::now(); } -void DocumentLoadTiming::addRedirect(const URL& redirectingUrl, const URL& redirectedUrl) +void LoadTiming::addRedirect(const URL& redirectingUrl, const URL& redirectedUrl) { m_redirectCount++; if (!m_redirectStart) m_redirectStart = m_fetchStart; - m_redirectEnd = m_fetchStart = monotonicallyIncreasingTime(); + m_redirectEnd = m_fetchStart = MonotonicTime::now(); // Check if the redirected url is allowed to access the redirecting url's timing information. - RefPtr<SecurityOrigin> redirectedSecurityOrigin = SecurityOrigin::create(redirectedUrl); - m_hasCrossOriginRedirect = !redirectedSecurityOrigin->canRequest(redirectingUrl); + Ref<SecurityOrigin> redirectedSecurityOrigin(SecurityOrigin::create(redirectedUrl)); + m_hasCrossOriginRedirect = !redirectedSecurityOrigin.get().canRequest(redirectingUrl); +} + +LoadTiming LoadTiming::isolatedCopy() const +{ + LoadTiming result; + + result.m_referenceWallTime = m_referenceWallTime; + result.m_referenceMonotonicTime = m_referenceMonotonicTime; + result.m_startTime = m_startTime; + result.m_unloadEventStart = m_unloadEventStart; + result.m_unloadEventEnd = m_unloadEventEnd; + result.m_redirectStart = m_redirectStart; + result.m_redirectEnd = m_redirectEnd; + result.m_fetchStart = m_fetchStart; + result.m_responseEnd = m_responseEnd; + result.m_loadEventStart = m_loadEventStart; + result.m_loadEventEnd = m_loadEventEnd; + result.m_redirectCount = m_redirectCount; + result.m_hasCrossOriginRedirect = m_hasCrossOriginRedirect; + result.m_hasSameOriginAsPreviousDocument = m_hasSameOriginAsPreviousDocument; + + return result; } } // namespace WebCore diff --git a/Source/WebCore/loader/LoadTiming.h b/Source/WebCore/loader/LoadTiming.h new file mode 100644 index 000000000..23c41e27a --- /dev/null +++ b/Source/WebCore/loader/LoadTiming.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 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 GOOGLE 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. + */ + +#pragma once + +#include <wtf/CurrentTime.h> +#include <wtf/MonotonicTime.h> +#include <wtf/WallTime.h> + +namespace WebCore { + +class URL; + +class LoadTiming { +public: + Seconds secondsSinceStartTime(MonotonicTime) const; + WallTime monotonicTimeToPseudoWallTime(MonotonicTime) const; + + void markStartTime(); + void addRedirect(const URL& redirectingUrl, const URL& redirectedUrl); + + void markStartTimeAndFetchStart() { markStartTime(); m_fetchStart = m_startTime; } + + void markUnloadEventStart() { m_unloadEventStart = MonotonicTime::now(); } + void markUnloadEventEnd() { m_unloadEventEnd = MonotonicTime::now(); } + void markRedirectStart() { m_redirectStart = MonotonicTime::now(); } + void markRedirectEnd() { m_redirectEnd = MonotonicTime::now(); } + void markFetchStart() { m_fetchStart = MonotonicTime::now(); } + void setResponseEnd(MonotonicTime time) { m_responseEnd = time; } + void markLoadEventStart() { m_loadEventStart = MonotonicTime::now(); } + void markLoadEventEnd() { m_loadEventEnd = MonotonicTime::now(); } + + void setHasSameOriginAsPreviousDocument(bool value) { m_hasSameOriginAsPreviousDocument = value; } + + MonotonicTime startTime() const { return m_startTime; } + MonotonicTime unloadEventStart() const { return m_unloadEventStart; } + MonotonicTime unloadEventEnd() const { return m_unloadEventEnd; } + MonotonicTime redirectStart() const { return m_redirectStart; } + MonotonicTime redirectEnd() const { return m_redirectEnd; } + MonotonicTime fetchStart() const { return m_fetchStart; } + MonotonicTime responseEnd() const { return m_responseEnd; } + MonotonicTime loadEventStart() const { return m_loadEventStart; } + MonotonicTime loadEventEnd() const { return m_loadEventEnd; } + short redirectCount() const { return m_redirectCount; } + bool hasCrossOriginRedirect() const { return m_hasCrossOriginRedirect; } + bool hasSameOriginAsPreviousDocument() const { return m_hasSameOriginAsPreviousDocument; } + + MonotonicTime referenceMonotonicTime() const { return m_referenceMonotonicTime; } + WallTime referenceWallTime() const { return m_referenceWallTime; } + + LoadTiming isolatedCopy() const; + +private: + WallTime m_referenceWallTime; + MonotonicTime m_referenceMonotonicTime; + MonotonicTime m_startTime; + MonotonicTime m_unloadEventStart; + MonotonicTime m_unloadEventEnd; + MonotonicTime m_redirectStart; + MonotonicTime m_redirectEnd; + MonotonicTime m_fetchStart; + MonotonicTime m_responseEnd; + MonotonicTime m_loadEventStart; + MonotonicTime m_loadEventEnd; + short m_redirectCount { 0 }; + bool m_hasCrossOriginRedirect { false }; + bool m_hasSameOriginAsPreviousDocument { false }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/LoaderStrategy.cpp b/Source/WebCore/loader/LoaderStrategy.cpp index fc4400772..75aae4285 100644 --- a/Source/WebCore/loader/LoaderStrategy.cpp +++ b/Source/WebCore/loader/LoaderStrategy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,28 +26,14 @@ #include "config.h" #include "LoaderStrategy.h" -#include "BlobRegistryImpl.h" -#include "ResourceHandle.h" -#include "ResourceLoadScheduler.h" +#include "PlatformStrategies.h" namespace WebCore { -ResourceLoadScheduler* LoaderStrategy::resourceLoadScheduler() +LoaderStrategy::~LoaderStrategy() { - return WebCore::resourceLoadScheduler(); } -void LoaderStrategy::loadResourceSynchronously(NetworkingContext* context, unsigned long, const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data) -{ - ResourceHandle::loadResourceSynchronously(context, request, storedCredentials, error, response, data); -} - -#if ENABLE(BLOB) -BlobRegistry* LoaderStrategy::createBlobRegistry() -{ - return new BlobRegistryImpl; -} -#endif +} // namespace WebCore -} // namespace WebCore diff --git a/Source/WebCore/loader/LoaderStrategy.h b/Source/WebCore/loader/LoaderStrategy.h index 0e625d35a..8121f0e77 100644 --- a/Source/WebCore/loader/LoaderStrategy.h +++ b/Source/WebCore/loader/LoaderStrategy.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,37 +23,48 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef LoaderStrategy_h -#define LoaderStrategy_h +#pragma once #include "ResourceHandleTypes.h" +#include "ResourceLoadPriority.h" +#include "ResourceLoaderOptions.h" +#include <wtf/SHA1.h> #include <wtf/Vector.h> namespace WebCore { -class BlobRegistry; +class CachedResource; +class Frame; +class NetscapePlugInStreamLoader; +class NetscapePlugInStreamLoaderClient; class NetworkingContext; class ResourceError; -class ResourceLoadScheduler; +class ResourceLoader; class ResourceRequest; class ResourceResponse; +class SharedBuffer; +class SubresourceLoader; +class URL; -class LoaderStrategy { +class WEBCORE_EXPORT LoaderStrategy { public: - virtual ResourceLoadScheduler* resourceLoadScheduler(); + virtual RefPtr<SubresourceLoader> loadResource(Frame&, CachedResource&, const ResourceRequest&, const ResourceLoaderOptions&) = 0; + virtual void loadResourceSynchronously(NetworkingContext*, unsigned long identifier, const ResourceRequest&, StoredCredentials, ClientCredentialPolicy, ResourceError&, ResourceResponse&, Vector<char>& data) = 0; - virtual void loadResourceSynchronously(NetworkingContext*, unsigned long identifier, const ResourceRequest&, StoredCredentials, ClientCredentialPolicy, ResourceError&, ResourceResponse&, Vector<char>& data); + virtual void remove(ResourceLoader*) = 0; + virtual void setDefersLoading(ResourceLoader*, bool) = 0; + virtual void crossOriginRedirectReceived(ResourceLoader*, const URL& redirectURL) = 0; -#if ENABLE(BLOB) - virtual BlobRegistry* createBlobRegistry(); -#endif + virtual void servePendingRequests(ResourceLoadPriority minimumPriority = ResourceLoadPriority::VeryLow) = 0; + virtual void suspendPendingRequests() = 0; + virtual void resumePendingRequests() = 0; + + virtual void createPingHandle(NetworkingContext*, ResourceRequest&, bool shouldUseCredentialStorage, bool shouldFollowRedirects) = 0; + + virtual void storeDerivedDataToCache(const SHA1::Digest& bodyKey, const String& type, const String& partition, WebCore::SharedBuffer&) = 0; protected: - virtual ~LoaderStrategy() - { - } + virtual ~LoaderStrategy(); }; } // namespace WebCore - -#endif // LoaderStrategy_h diff --git a/Source/WebCore/loader/MediaResourceLoader.cpp b/Source/WebCore/loader/MediaResourceLoader.cpp new file mode 100644 index 000000000..ac708d6bd --- /dev/null +++ b/Source/WebCore/loader/MediaResourceLoader.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2014 Igalia S.L + * Copyright (C) 2016 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "MediaResourceLoader.h" + +#if ENABLE(VIDEO) + +#include "CachedRawResource.h" +#include "CachedResourceLoader.h" +#include "CachedResourceRequest.h" +#include "CrossOriginAccessControl.h" +#include "Document.h" +#include "SecurityOrigin.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +MediaResourceLoader::MediaResourceLoader(Document& document, const String& crossOriginMode) + : ContextDestructionObserver(&document) + , m_document(&document) + , m_crossOriginMode(crossOriginMode) +{ +} + +MediaResourceLoader::~MediaResourceLoader() +{ + ASSERT(m_resources.isEmpty()); +} + +void MediaResourceLoader::contextDestroyed() +{ + ContextDestructionObserver::contextDestroyed(); + m_document = nullptr; +} + +RefPtr<PlatformMediaResource> MediaResourceLoader::requestResource(ResourceRequest&& request, LoadOptions options) +{ + if (!m_document) + return nullptr; + + DataBufferingPolicy bufferingPolicy = options & LoadOption::BufferData ? WebCore::BufferData : WebCore::DoNotBufferData; + auto cachingPolicy = options & LoadOption::DisallowCaching ? CachingPolicy::DisallowCaching : CachingPolicy::AllowCaching; + + request.setRequester(ResourceRequest::Requester::Media); +#if HAVE(AVFOUNDATION_LOADER_DELEGATE) && PLATFORM(MAC) + // FIXME: Workaround for <rdar://problem/26071607>. We are not able to do CORS checking on 304 responses because they are usually missing the headers we need. + if (!m_crossOriginMode.isNull()) + request.makeUnconditional(); +#endif + + // FIXME: Skip Content Security Policy check if the element that initiated this request is in a user-agent shadow tree. See <https://bugs.webkit.org/show_bug.cgi?id=155505>. + CachedResourceRequest cacheRequest(WTFMove(request), ResourceLoaderOptions(SendCallbacks, DoNotSniffContent, bufferingPolicy, AllowStoredCredentials, ClientCredentialPolicy::MayAskClientForCredentials, FetchOptions::Credentials::Include, DoSecurityCheck, FetchOptions::Mode::NoCors, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, cachingPolicy)); + cacheRequest.setAsPotentiallyCrossOrigin(m_crossOriginMode, *m_document); + + CachedResourceHandle<CachedRawResource> resource = m_document->cachedResourceLoader().requestMedia(WTFMove(cacheRequest)); + if (!resource) + return nullptr; + + Ref<MediaResource> mediaResource = MediaResource::create(*this, resource); + m_resources.add(mediaResource.ptr()); + + return WTFMove(mediaResource); +} + +void MediaResourceLoader::removeResource(MediaResource& mediaResource) +{ + ASSERT(m_resources.contains(&mediaResource)); + m_resources.remove(&mediaResource); +} + +Ref<MediaResource> MediaResource::create(MediaResourceLoader& loader, CachedResourceHandle<CachedRawResource> resource) +{ + return adoptRef(*new MediaResource(loader, resource)); +} + +MediaResource::MediaResource(MediaResourceLoader& loader, CachedResourceHandle<CachedRawResource> resource) + : m_loader(loader) + , m_resource(resource) +{ + ASSERT(resource); + resource->addClient(*this); +} + +MediaResource::~MediaResource() +{ + stop(); + m_loader->removeResource(*this); +} + +void MediaResource::stop() +{ + if (!m_resource) + return; + + m_resource->removeClient(*this); + m_resource = nullptr; +} + +void MediaResource::setDefersLoading(bool defersLoading) +{ + if (m_resource) + m_resource->setDefersLoading(defersLoading); +} + +void MediaResource::responseReceived(CachedResource& resource, const ResourceResponse& response) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + + if (!m_loader->document()) + return; + + RefPtr<MediaResource> protectedThis(this); + if (m_resource->resourceError().isAccessControl()) { + static NeverDestroyed<const String> consoleMessage("Cross-origin media resource load denied by Cross-Origin Resource Sharing policy."); + m_loader->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage.get()); + m_didPassAccessControlCheck = false; + if (m_client) + m_client->accessControlCheckFailed(*this, ResourceError(errorDomainWebKitInternal, 0, response.url(), consoleMessage.get())); + stop(); + return; + } + + m_didPassAccessControlCheck = m_resource->options().mode == FetchOptions::Mode::Cors; + if (m_client) + m_client->responseReceived(*this, response); +} + +bool MediaResource::shouldCacheResponse(CachedResource& resource, const ResourceResponse& response) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + + RefPtr<MediaResource> protectedThis(this); + if (m_client) + return m_client->shouldCacheResponse(*this, response); + return true; +} + +void MediaResource::redirectReceived(CachedResource& resource, ResourceRequest& request, const ResourceResponse& response) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + + RefPtr<MediaResource> protectedThis(this); + if (m_client) + m_client->redirectReceived(*this, request, response); +} + +void MediaResource::dataSent(CachedResource& resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + + RefPtr<MediaResource> protectedThis(this); + if (m_client) + m_client->dataSent(*this, bytesSent, totalBytesToBeSent); +} + +void MediaResource::dataReceived(CachedResource& resource, const char* data, int dataLength) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + + RefPtr<MediaResource> protectedThis(this); + if (m_client) + m_client->dataReceived(*this, data, dataLength); +} + +void MediaResource::notifyFinished(CachedResource& resource) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + + RefPtr<MediaResource> protectedThis(this); + if (m_client) { + if (m_resource->loadFailedOrCanceled()) + m_client->loadFailed(*this, m_resource->resourceError()); + else + m_client->loadFinished(*this); + } + stop(); +} + +#if USE(SOUP) +char* MediaResource::getOrCreateReadBuffer(CachedResource& resource, size_t requestedSize, size_t& actualSize) +{ + ASSERT_UNUSED(resource, &resource == m_resource); + return m_client ? m_client->getOrCreateReadBuffer(*this, requestedSize, actualSize) : nullptr; +} +#endif + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/loader/MediaResourceLoader.h b/Source/WebCore/loader/MediaResourceLoader.h new file mode 100644 index 000000000..9485d0ef1 --- /dev/null +++ b/Source/WebCore/loader/MediaResourceLoader.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 Igalia S.L + * + * 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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. + */ + +#pragma once + +#if ENABLE(VIDEO) + +#include "CachedRawResourceClient.h" +#include "CachedResourceHandle.h" +#include "ContextDestructionObserver.h" +#include "PlatformMediaResourceLoader.h" +#include <wtf/HashSet.h> +#include <wtf/Ref.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class CachedRawResource; +class Document; +class MediaResource; + +class MediaResourceLoader final : public PlatformMediaResourceLoader, public ContextDestructionObserver { +public: + WEBCORE_EXPORT MediaResourceLoader(Document&, const String& crossOriginMode); + WEBCORE_EXPORT virtual ~MediaResourceLoader(); + + RefPtr<PlatformMediaResource> requestResource(ResourceRequest&&, LoadOptions) final; + void removeResource(MediaResource&); + + Document* document() { return m_document; } + const String& crossOriginMode() const { return m_crossOriginMode; } + +private: + void contextDestroyed() override; + + Document* m_document; + String m_crossOriginMode; + HashSet<MediaResource*> m_resources; +}; + +class MediaResource : public PlatformMediaResource, CachedRawResourceClient { +public: + static Ref<MediaResource> create(MediaResourceLoader&, CachedResourceHandle<CachedRawResource>); + virtual ~MediaResource(); + + // PlatformMediaResource + void stop() override; + void setDefersLoading(bool) override; + bool didPassAccessControlCheck() const override { return m_didPassAccessControlCheck; } + + // CachedRawResourceClient + void responseReceived(CachedResource&, const ResourceResponse&) override; + void redirectReceived(CachedResource&, ResourceRequest&, const ResourceResponse&) override; + bool shouldCacheResponse(CachedResource&, const ResourceResponse&) override; + void dataSent(CachedResource&, unsigned long long, unsigned long long) override; + void dataReceived(CachedResource&, const char*, int) override; + void notifyFinished(CachedResource&) override; +#if USE(SOUP) + char* getOrCreateReadBuffer(CachedResource&, size_t /*requestedSize*/, size_t& /*actualSize*/) override; +#endif + +private: + MediaResource(MediaResourceLoader&, CachedResourceHandle<CachedRawResource>); + Ref<MediaResourceLoader> m_loader; + bool m_didPassAccessControlCheck { false }; + CachedResourceHandle<CachedRawResource> m_resource; +}; + +} // namespace WebCore + +#endif // ENABLE(VIDEO) diff --git a/Source/WebCore/loader/MixedContentChecker.cpp b/Source/WebCore/loader/MixedContentChecker.cpp index e507aa62d..46081a722 100644 --- a/Source/WebCore/loader/MixedContentChecker.cpp +++ b/Source/WebCore/loader/MixedContentChecker.cpp @@ -6,13 +6,13 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * documentation and/or other materials provided with the distribution. + * 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. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -29,13 +29,11 @@ #include "config.h" #include "MixedContentChecker.h" -#include "Console.h" -#include "DOMWindow.h" +#include "ContentSecurityPolicy.h" #include "Document.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" -#include "SchemeRegistry.h" #include "SecurityOrigin.h" #include "Settings.h" #include <wtf/text/CString.h> @@ -54,47 +52,78 @@ FrameLoaderClient& MixedContentChecker::client() const } // static -bool MixedContentChecker::isMixedContent(SecurityOrigin* securityOrigin, const URL& url) +bool MixedContentChecker::isMixedContent(SecurityOrigin& securityOrigin, const URL& url) { - if (securityOrigin->protocol() != "https") + if (securityOrigin.protocol() != "https") return false; // We only care about HTTPS security origins. // We're in a secure context, so |url| is mixed content if it's insecure. return !SecurityOrigin::isSecure(url); } -bool MixedContentChecker::canDisplayInsecureContent(SecurityOrigin* securityOrigin, const URL& url) const +bool MixedContentChecker::canDisplayInsecureContent(SecurityOrigin& securityOrigin, ContentType type, const URL& url, AlwaysDisplayInNonStrictMode alwaysDisplayInNonStrictMode) const { if (!isMixedContent(securityOrigin, url)) return true; - bool allowed = client().allowDisplayingInsecureContent(m_frame.settings().allowDisplayOfInsecureContent(), securityOrigin, url); - logWarning(allowed, "displayed", url); + if (!m_frame.document()->contentSecurityPolicy()->allowRunningOrDisplayingInsecureContent(url)) + return false; - if (allowed) + bool isStrictMode = m_frame.document()->isStrictMixedContentMode(); + if (!isStrictMode && alwaysDisplayInNonStrictMode == AlwaysDisplayInNonStrictMode::Yes) + return true; + + bool allowed = !isStrictMode && (m_frame.settings().allowDisplayOfInsecureContent() || type == ContentType::ActiveCanWarn) && !m_frame.document()->geolocationAccessed(); + logWarning(allowed, "display", url); + + if (allowed) { + m_frame.document()->setFoundMixedContent(); client().didDisplayInsecureContent(); + } return allowed; } -bool MixedContentChecker::canRunInsecureContent(SecurityOrigin* securityOrigin, const URL& url) const +bool MixedContentChecker::canRunInsecureContent(SecurityOrigin& securityOrigin, const URL& url) const { if (!isMixedContent(securityOrigin, url)) return true; - bool allowed = client().allowRunningInsecureContent(m_frame.settings().allowRunningOfInsecureContent(), securityOrigin, url); - logWarning(allowed, "ran", url); + if (!m_frame.document()->contentSecurityPolicy()->allowRunningOrDisplayingInsecureContent(url)) + return false; - if (allowed) + bool allowed = !m_frame.document()->isStrictMixedContentMode() && m_frame.settings().allowRunningOfInsecureContent() && !m_frame.document()->geolocationAccessed(); + logWarning(allowed, "run", url); + + if (allowed) { + m_frame.document()->setFoundMixedContent(); client().didRunInsecureContent(securityOrigin, url); + } return allowed; } +void MixedContentChecker::checkFormForMixedContent(SecurityOrigin& securityOrigin, const URL& url) const +{ + // Unconditionally allow javascript: URLs as form actions as some pages do this and it does not introduce + // a mixed content issue. + if (protocolIsJavaScript(url)) + return; + + if (!isMixedContent(securityOrigin, url)) + return; + + String message = makeString("The page at ", m_frame.document()->url().stringCenterEllipsizedToLength(), " contains a form which targets an insecure URL ", url.stringCenterEllipsizedToLength(), ".\n"); + m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Warning, message); + + client().didDisplayInsecureContent(); +} + void MixedContentChecker::logWarning(bool allowed, const String& action, const URL& target) const { - String message = makeString((allowed ? "" : "[blocked] "), "The page at ", m_frame.document()->url().stringCenterEllipsizedToLength(), " ", action, " insecure content from ", target.stringCenterEllipsizedToLength(), ".\n"); - m_frame.document()->addConsoleMessage(SecurityMessageSource, WarningMessageLevel, message); + const char* errorString = allowed ? " was allowed to " : " was not allowed to "; + String message = makeString((allowed ? String() : "[blocked] "), "The page at ", m_frame.document()->url().stringCenterEllipsizedToLength(), errorString, action, " insecure content from ", target.stringCenterEllipsizedToLength(), ".\n"); + m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Warning, message); } } // namespace WebCore diff --git a/Source/WebCore/loader/MixedContentChecker.h b/Source/WebCore/loader/MixedContentChecker.h index bff9c795f..a26a85797 100644 --- a/Source/WebCore/loader/MixedContentChecker.h +++ b/Source/WebCore/loader/MixedContentChecker.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MixedContentChecker_h -#define MixedContentChecker_h +#pragma once #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -44,11 +43,22 @@ class SecurityOrigin; class MixedContentChecker { WTF_MAKE_NONCOPYABLE(MixedContentChecker); public: + enum class ContentType { + Active, + ActiveCanWarn, + }; + MixedContentChecker(Frame&); - bool canDisplayInsecureContent(SecurityOrigin*, const URL&) const; - bool canRunInsecureContent(SecurityOrigin*, const URL&) const; - static bool isMixedContent(SecurityOrigin*, const URL&); + enum class AlwaysDisplayInNonStrictMode { + No, + Yes, + }; + + bool canDisplayInsecureContent(SecurityOrigin&, ContentType, const URL&, AlwaysDisplayInNonStrictMode = AlwaysDisplayInNonStrictMode::No) const; + bool canRunInsecureContent(SecurityOrigin&, const URL&) const; + void checkFormForMixedContent(SecurityOrigin&, const URL&) const; + static bool isMixedContent(SecurityOrigin&, const URL&); private: // FIXME: This should probably have a separate client from FrameLoader. @@ -60,6 +70,3 @@ private: }; } // namespace WebCore - -#endif // MixedContentChecker_h - diff --git a/Source/WebCore/loader/NavigationAction.cpp b/Source/WebCore/loader/NavigationAction.cpp index e840fb2a9..7ea86196c 100644 --- a/Source/WebCore/loader/NavigationAction.cpp +++ b/Source/WebCore/loader/NavigationAction.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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,59 +31,93 @@ #include "Event.h" #include "FrameLoader.h" +#include "ScriptController.h" namespace WebCore { static NavigationType navigationType(FrameLoadType frameLoadType, bool isFormSubmission, bool haveEvent) { if (isFormSubmission) - return NavigationTypeFormSubmitted; + return NavigationType::FormSubmitted; if (haveEvent) - return NavigationTypeLinkClicked; - if (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin) - return NavigationTypeReload; + return NavigationType::LinkClicked; + if (frameLoadType == FrameLoadType::Reload || frameLoadType == FrameLoadType::ReloadFromOrigin) + return NavigationType::Reload; if (isBackForwardLoadType(frameLoadType)) - return NavigationTypeBackForward; - return NavigationTypeOther; + return NavigationType::BackForward; + return NavigationType::Other; +} + +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, NavigationType type, Event* event, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, const AtomicString& downloadAttribute) + : m_resourceRequest(resourceRequest) + , m_type(type) + , m_event(event) + , m_userGestureToken(UserGestureIndicator::currentUserGesture()) + , m_shouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicy) + , m_downloadAttribute(downloadAttribute) +{ } NavigationAction::NavigationAction() - : m_type(NavigationTypeOther) + : NavigationAction(ResourceRequest(), NavigationType::Other, nullptr, ShouldOpenExternalURLsPolicy::ShouldNotAllow, nullAtom) { } NavigationAction::NavigationAction(const ResourceRequest& resourceRequest) - : m_resourceRequest(resourceRequest) - , m_type(NavigationTypeOther) + : NavigationAction(resourceRequest, NavigationType::Other, nullptr, ShouldOpenExternalURLsPolicy::ShouldNotAllow, nullAtom) +{ +} + +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : NavigationAction(resourceRequest, NavigationType::Other, nullptr, shouldOpenExternalURLsPolicy, nullAtom) { } NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, NavigationType type) - : m_resourceRequest(resourceRequest) - , m_type(type) + : NavigationAction(resourceRequest, type, nullptr, ShouldOpenExternalURLsPolicy::ShouldNotAllow, nullAtom) { } -NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, FrameLoadType frameLoadType, - bool isFormSubmission) - : m_resourceRequest(resourceRequest) - , m_type(navigationType(frameLoadType, isFormSubmission, 0)) +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, NavigationType type, Event* event, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : NavigationAction(resourceRequest, type, event, shouldOpenExternalURLsPolicy, nullAtom) { } -NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, NavigationType type, PassRefPtr<Event> event) - : m_resourceRequest(resourceRequest) - , m_type(type) - , m_event(event) +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, FrameLoadType frameLoadType, bool isFormSubmission) + : NavigationAction(resourceRequest, navigationType(frameLoadType, isFormSubmission, 0), nullptr, ShouldOpenExternalURLsPolicy::ShouldNotAllow, nullAtom) { } -NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, FrameLoadType frameLoadType, - bool isFormSubmission, PassRefPtr<Event> event) - : m_resourceRequest(resourceRequest) - , m_type(navigationType(frameLoadType, isFormSubmission, event)) - , m_event(event) +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, NavigationType type, Event* event) + : NavigationAction(resourceRequest, type, event, ShouldOpenExternalURLsPolicy::ShouldNotAllow, nullAtom) +{ +} + +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, NavigationType type, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : NavigationAction(resourceRequest, type, nullptr, shouldOpenExternalURLsPolicy, nullAtom) +{ +} + +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, FrameLoadType frameLoadType, bool isFormSubmission, Event* event) + : NavigationAction(resourceRequest, navigationType(frameLoadType, isFormSubmission, event), event, ShouldOpenExternalURLsPolicy::ShouldNotAllow, nullAtom) +{ +} + +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, FrameLoadType frameLoadType, bool isFormSubmission, Event* event, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) + : NavigationAction(resourceRequest, navigationType(frameLoadType, isFormSubmission, event), event, shouldOpenExternalURLsPolicy, nullAtom) +{ +} + +NavigationAction::NavigationAction(const ResourceRequest& resourceRequest, FrameLoadType frameLoadType, bool isFormSubmission, Event* event, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, const AtomicString& downloadAttribute) + : NavigationAction(resourceRequest, navigationType(frameLoadType, isFormSubmission, event), event, shouldOpenExternalURLsPolicy, downloadAttribute) +{ +} + +NavigationAction NavigationAction::copyWithShouldOpenExternalURLsPolicy(ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) const { + NavigationAction result(*this); + result.m_shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicy; + return result; } } diff --git a/Source/WebCore/loader/NavigationAction.h b/Source/WebCore/loader/NavigationAction.h index 5e21e0140..3702dc550 100644 --- a/Source/WebCore/loader/NavigationAction.h +++ b/Source/WebCore/loader/NavigationAction.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -26,40 +26,57 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NavigationAction_h -#define NavigationAction_h +#pragma once #include "Event.h" #include "FrameLoaderTypes.h" #include "URL.h" #include "ResourceRequest.h" +#include "UserGestureIndicator.h" #include <wtf/Forward.h> namespace WebCore { - class NavigationAction { - public: - NavigationAction(); - explicit NavigationAction(const ResourceRequest&); - NavigationAction(const ResourceRequest&, NavigationType); - NavigationAction(const ResourceRequest&, FrameLoadType, bool isFormSubmission); - NavigationAction(const ResourceRequest&, NavigationType, PassRefPtr<Event>); - NavigationAction(const ResourceRequest&, FrameLoadType, bool isFormSubmission, PassRefPtr<Event>); +class NavigationAction { +public: + WEBCORE_EXPORT NavigationAction(); + WEBCORE_EXPORT explicit NavigationAction(const ResourceRequest&); + WEBCORE_EXPORT NavigationAction(const ResourceRequest&, NavigationType); + WEBCORE_EXPORT NavigationAction(const ResourceRequest&, FrameLoadType, bool isFormSubmission); - bool isEmpty() const { return m_resourceRequest.url().isEmpty(); } + NavigationAction(const ResourceRequest&, ShouldOpenExternalURLsPolicy); + NavigationAction(const ResourceRequest&, NavigationType, Event*); + NavigationAction(const ResourceRequest&, NavigationType, Event*, ShouldOpenExternalURLsPolicy); + NavigationAction(const ResourceRequest&, NavigationType, Event*, ShouldOpenExternalURLsPolicy, const AtomicString& downloadAttribute); + NavigationAction(const ResourceRequest&, NavigationType, ShouldOpenExternalURLsPolicy); + NavigationAction(const ResourceRequest&, FrameLoadType, bool isFormSubmission, Event*); + NavigationAction(const ResourceRequest&, FrameLoadType, bool isFormSubmission, Event*, ShouldOpenExternalURLsPolicy); + NavigationAction(const ResourceRequest&, FrameLoadType, bool isFormSubmission, Event*, ShouldOpenExternalURLsPolicy, const AtomicString& downloadAttribute); - URL url() const { return m_resourceRequest.url(); } - const ResourceRequest& resourceRequest() const { return m_resourceRequest; } + NavigationAction copyWithShouldOpenExternalURLsPolicy(ShouldOpenExternalURLsPolicy) const; - NavigationType type() const { return m_type; } - const Event* event() const { return m_event.get(); } + bool isEmpty() const { return m_resourceRequest.url().isEmpty(); } - private: - ResourceRequest m_resourceRequest; - NavigationType m_type; - RefPtr<Event> m_event; - }; + URL url() const { return m_resourceRequest.url(); } + const ResourceRequest& resourceRequest() const { return m_resourceRequest; } -} + NavigationType type() const { return m_type; } + const Event* event() const { return m_event.get(); } -#endif + bool processingUserGesture() const { return m_userGestureToken ? m_userGestureToken->processingUserGesture() : false; } + RefPtr<UserGestureToken> userGestureToken() const { return m_userGestureToken; } + + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy() const { return m_shouldOpenExternalURLsPolicy; } + + const AtomicString& downloadAttribute() const { return m_downloadAttribute; } + +private: + ResourceRequest m_resourceRequest; + NavigationType m_type { NavigationType::Other }; + RefPtr<Event> m_event; + RefPtr<UserGestureToken> m_userGestureToken; + ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow }; + AtomicString m_downloadAttribute; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/NavigationScheduler.cpp b/Source/WebCore/loader/NavigationScheduler.cpp index 7a6351639..0f0290c11 100644 --- a/Source/WebCore/loader/NavigationScheduler.cpp +++ b/Source/WebCore/loader/NavigationScheduler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2009 Adam Barth. All rights reserved. @@ -13,7 +13,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. * @@ -46,6 +46,7 @@ #include "HTMLFrameOwnerElement.h" #include "HistoryItem.h" #include "InspectorInstrumentation.h" +#include "Logging.h" #include "Page.h" #include "ScriptController.h" #include "UserGestureIndicator.h" @@ -54,18 +55,28 @@ namespace WebCore { -unsigned NavigationDisablerForBeforeUnload::s_navigationDisableCount = 0; +unsigned NavigationDisabler::s_navigationDisableCount = 0; class ScheduledNavigation { WTF_MAKE_NONCOPYABLE(ScheduledNavigation); WTF_MAKE_FAST_ALLOCATED; public: - ScheduledNavigation(double delay, bool lockHistory, bool lockBackForwardList, bool wasDuringLoad, bool isLocationChange) + ScheduledNavigation(double delay, LockHistory lockHistory, LockBackForwardList lockBackForwardList, bool wasDuringLoad, bool isLocationChange) : m_delay(delay) , m_lockHistory(lockHistory) , m_lockBackForwardList(lockBackForwardList) , m_wasDuringLoad(wasDuringLoad) , m_isLocationChange(isLocationChange) - , m_wasUserGesture(ScriptController::processingUserGesture()) + , m_userGestureToForward(UserGestureIndicator::currentUserGesture()) + { + } + ScheduledNavigation(double delay, LockHistory lockHistory, LockBackForwardList lockBackForwardList, bool wasDuringLoad, bool isLocationChange, ShouldOpenExternalURLsPolicy externalURLPolicy) + : m_delay(delay) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_wasDuringLoad(wasDuringLoad) + , m_isLocationChange(isLocationChange) + , m_userGestureToForward(UserGestureIndicator::currentUserGesture()) + , m_shouldOpenExternalURLsPolicy(externalURLPolicy) { } virtual ~ScheduledNavigation() { } @@ -73,56 +84,61 @@ public: virtual void fire(Frame&) = 0; virtual bool shouldStartTimer(Frame&) { return true; } - virtual void didStartTimer(Frame&, Timer<NavigationScheduler>&) { } + virtual void didStartTimer(Frame&, Timer&) { } virtual void didStopTimer(Frame&, bool /* newLoadInProgress */) { } double delay() const { return m_delay; } - bool lockHistory() const { return m_lockHistory; } - bool lockBackForwardList() const { return m_lockBackForwardList; } + LockHistory lockHistory() const { return m_lockHistory; } + LockBackForwardList lockBackForwardList() const { return m_lockBackForwardList; } bool wasDuringLoad() const { return m_wasDuringLoad; } bool isLocationChange() const { return m_isLocationChange; } - bool wasUserGesture() const { return m_wasUserGesture; } + UserGestureToken* userGestureToForward() const { return m_userGestureToForward.get(); } protected: - void clearUserGesture() { m_wasUserGesture = false; } + void clearUserGesture() { m_userGestureToForward = nullptr; } + ShouldOpenExternalURLsPolicy shouldOpenExternalURLs() const { return m_shouldOpenExternalURLsPolicy; } private: double m_delay; - bool m_lockHistory; - bool m_lockBackForwardList; + LockHistory m_lockHistory; + LockBackForwardList m_lockBackForwardList; bool m_wasDuringLoad; bool m_isLocationChange; - bool m_wasUserGesture; + RefPtr<UserGestureToken> m_userGestureToForward; + ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow }; }; class ScheduledURLNavigation : public ScheduledNavigation { protected: - ScheduledURLNavigation(double delay, SecurityOrigin* securityOrigin, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool duringLoad, bool isLocationChange) - : ScheduledNavigation(delay, lockHistory, lockBackForwardList, duringLoad, isLocationChange) + ScheduledURLNavigation(Document& initiatingDocument, double delay, SecurityOrigin* securityOrigin, const URL& url, const String& referrer, LockHistory lockHistory, LockBackForwardList lockBackForwardList, bool duringLoad, bool isLocationChange) + : ScheduledNavigation(delay, lockHistory, lockBackForwardList, duringLoad, isLocationChange, initiatingDocument.shouldOpenExternalURLsPolicyToPropagate()) , m_securityOrigin(securityOrigin) , m_url(url) , m_referrer(referrer) - , m_haveToldClient(false) { } - virtual void fire(Frame& frame) override + void fire(Frame& frame) override { - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); - frame.loader().changeLocation(m_securityOrigin.get(), URL(ParsedURLString, m_url), m_referrer, lockHistory(), lockBackForwardList(), false); + UserGestureIndicator gestureIndicator(userGestureToForward()); + + ResourceRequest resourceRequest(m_url, m_referrer, UseProtocolCachePolicy); + FrameLoadRequest frameRequest(*m_securityOrigin, resourceRequest, "_self", lockHistory(), lockBackForwardList(), MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, shouldOpenExternalURLs()); + + frame.loader().changeLocation(frameRequest); } - virtual void didStartTimer(Frame& frame, Timer<NavigationScheduler>& timer) override + void didStartTimer(Frame& frame, Timer& timer) override { if (m_haveToldClient) return; m_haveToldClient = true; - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); - frame.loader().clientRedirected(URL(ParsedURLString, m_url), delay(), currentTime() + timer.nextFireInterval(), lockBackForwardList()); + UserGestureIndicator gestureIndicator(userGestureToForward()); + frame.loader().clientRedirected(m_url, delay(), currentTime() + timer.nextFireInterval(), lockBackForwardList()); } - virtual void didStopTimer(Frame& frame, bool newLoadInProgress) override + void didStopTimer(Frame& frame, bool newLoadInProgress) override { if (!m_haveToldClient) return; @@ -137,73 +153,88 @@ protected: } SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } - String url() const { return m_url; } + const URL& url() const { return m_url; } String referrer() const { return m_referrer; } private: RefPtr<SecurityOrigin> m_securityOrigin; - String m_url; + URL m_url; String m_referrer; - bool m_haveToldClient; + bool m_haveToldClient { false }; }; class ScheduledRedirect : public ScheduledURLNavigation { public: - ScheduledRedirect(double delay, SecurityOrigin* securityOrigin, const String& url, bool lockHistory, bool lockBackForwardList) - : ScheduledURLNavigation(delay, securityOrigin, url, String(), lockHistory, lockBackForwardList, false, false) + ScheduledRedirect(Document& initiatingDocument, double delay, SecurityOrigin* securityOrigin, const URL& url, LockHistory lockHistory, LockBackForwardList lockBackForwardList) + : ScheduledURLNavigation(initiatingDocument, delay, securityOrigin, url, String(), lockHistory, lockBackForwardList, false, false) { clearUserGesture(); } - virtual bool shouldStartTimer(Frame& frame) override + bool shouldStartTimer(Frame& frame) override { return frame.loader().allAncestorsAreComplete(); } - virtual void fire(Frame& frame) override + void fire(Frame& frame) override { - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); - bool refresh = equalIgnoringFragmentIdentifier(frame.document()->url(), URL(ParsedURLString, url())); - frame.loader().changeLocation(securityOrigin(), URL(ParsedURLString, url()), referrer(), lockHistory(), lockBackForwardList(), refresh); + UserGestureIndicator gestureIndicator(userGestureToForward()); + bool refresh = equalIgnoringFragmentIdentifier(frame.document()->url(), url()); + ResourceRequest resourceRequest(url(), referrer(), refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy); + FrameLoadRequest frameRequest(*securityOrigin(), resourceRequest, "_self", lockHistory(), lockBackForwardList(), MaybeSendReferrer, AllowNavigationToInvalidURL::No, NewFrameOpenerPolicy::Allow, shouldOpenExternalURLs()); + + frame.loader().changeLocation(frameRequest); } }; class ScheduledLocationChange : public ScheduledURLNavigation { public: - ScheduledLocationChange(SecurityOrigin* securityOrigin, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool duringLoad) - : ScheduledURLNavigation(0.0, securityOrigin, url, referrer, lockHistory, lockBackForwardList, duringLoad, true) { } + ScheduledLocationChange(Document& initiatingDocument, SecurityOrigin* securityOrigin, const URL& url, const String& referrer, LockHistory lockHistory, LockBackForwardList lockBackForwardList, bool duringLoad) + : ScheduledURLNavigation(initiatingDocument, 0.0, securityOrigin, url, referrer, lockHistory, lockBackForwardList, duringLoad, true) { } + + void fire(Frame& frame) override + { + UserGestureIndicator gestureIndicator(userGestureToForward()); + + ResourceRequest resourceRequest(url(), referrer(), UseProtocolCachePolicy); + FrameLoadRequest frameRequest(*securityOrigin(), resourceRequest, "_self", lockHistory(), lockBackForwardList(), MaybeSendReferrer, AllowNavigationToInvalidURL::No, NewFrameOpenerPolicy::Allow, shouldOpenExternalURLs()); + frame.loader().changeLocation(frameRequest); + } }; class ScheduledRefresh : public ScheduledURLNavigation { public: - ScheduledRefresh(SecurityOrigin* securityOrigin, const String& url, const String& referrer) - : ScheduledURLNavigation(0.0, securityOrigin, url, referrer, true, true, false, true) + ScheduledRefresh(Document& initiatingDocument, SecurityOrigin* securityOrigin, const URL& url, const String& referrer) + : ScheduledURLNavigation(initiatingDocument, 0.0, securityOrigin, url, referrer, LockHistory::Yes, LockBackForwardList::Yes, false, true) { } - virtual void fire(Frame& frame) override + void fire(Frame& frame) override { - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); - frame.loader().changeLocation(securityOrigin(), URL(ParsedURLString, url()), referrer(), lockHistory(), lockBackForwardList(), true); + UserGestureIndicator gestureIndicator(userGestureToForward()); + + ResourceRequest resourceRequest(url(), referrer(), ReloadIgnoringCacheData); + FrameLoadRequest frameRequest(*securityOrigin(), resourceRequest, "_self", lockHistory(), lockBackForwardList(), MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, shouldOpenExternalURLs()); + frame.loader().changeLocation(frameRequest); } }; class ScheduledHistoryNavigation : public ScheduledNavigation { public: explicit ScheduledHistoryNavigation(int historySteps) - : ScheduledNavigation(0, false, false, false, true) + : ScheduledNavigation(0, LockHistory::No, LockBackForwardList::No, false, true) , m_historySteps(historySteps) { } - virtual void fire(Frame& frame) override + void fire(Frame& frame) override { - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + UserGestureIndicator gestureIndicator(userGestureToForward()); if (!m_historySteps) { // Special case for go(0) from a frame -> reload only the frame // To follow Firefox and IE's behavior, history reload can only navigate the self frame. - frame.loader().urlSelected(frame.document()->url(), "_self", 0, lockHistory(), lockBackForwardList(), MaybeSendReferrer); + frame.loader().urlSelected(frame.document()->url(), "_self", 0, lockHistory(), lockBackForwardList(), MaybeSendReferrer, shouldOpenExternalURLs()); return; } @@ -218,41 +249,39 @@ private: class ScheduledFormSubmission : public ScheduledNavigation { public: - ScheduledFormSubmission(PassRefPtr<FormSubmission> submission, bool lockBackForwardList, bool duringLoad) - : ScheduledNavigation(0, submission->lockHistory(), lockBackForwardList, duringLoad, true) - , m_submission(submission) - , m_haveToldClient(false) + ScheduledFormSubmission(Ref<FormSubmission>&& submission, LockBackForwardList lockBackForwardList, bool duringLoad) + : ScheduledNavigation(0, submission->lockHistory(), lockBackForwardList, duringLoad, true, submission->state().sourceDocument().shouldOpenExternalURLsPolicyToPropagate()) + , m_submission(WTFMove(submission)) { - ASSERT(m_submission->state()); } - virtual void fire(Frame& frame) override + void fire(Frame& frame) override { - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + UserGestureIndicator gestureIndicator(userGestureToForward()); // The submitForm function will find a target frame before using the redirection timer. // Now that the timer has fired, we need to repeat the security check which normally is done when // selecting a target, in case conditions have changed. Other code paths avoid this by targeting // without leaving a time window. If we fail the check just silently drop the form submission. - Document* requestingDocument = m_submission->state()->sourceDocument(); - if (!requestingDocument->canNavigate(&frame)) + auto& requestingDocument = m_submission->state().sourceDocument(); + if (!requestingDocument.canNavigate(&frame)) return; - FrameLoadRequest frameRequest(requestingDocument->securityOrigin()); + FrameLoadRequest frameRequest(requestingDocument.securityOrigin(), lockHistory(), lockBackForwardList(), MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, shouldOpenExternalURLs()); m_submission->populateFrameLoadRequest(frameRequest); - frame.loader().loadFrameRequest(frameRequest, lockHistory(), lockBackForwardList(), m_submission->event(), m_submission->state(), MaybeSendReferrer); + frame.loader().loadFrameRequest(frameRequest, m_submission->event(), &m_submission->state()); } - - virtual void didStartTimer(Frame& frame, Timer<NavigationScheduler>& timer) override + + void didStartTimer(Frame& frame, Timer& timer) override { if (m_haveToldClient) return; m_haveToldClient = true; - UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + UserGestureIndicator gestureIndicator(userGestureToForward()); frame.loader().clientRedirected(m_submission->requestURL(), delay(), currentTime() + timer.nextFireInterval(), lockBackForwardList()); } - virtual void didStopTimer(Frame& frame, bool newLoadInProgress) override + void didStopTimer(Frame& frame, bool newLoadInProgress) override { if (!m_haveToldClient) return; @@ -267,13 +296,38 @@ public: } private: - RefPtr<FormSubmission> m_submission; - bool m_haveToldClient; + Ref<FormSubmission> m_submission; + bool m_haveToldClient { false }; +}; + +class ScheduledPageBlock final : public ScheduledNavigation { +public: + ScheduledPageBlock(Document& originDocument) + : ScheduledNavigation(0, LockHistory::Yes, LockBackForwardList::Yes, false, false) + , m_originDocument(originDocument) + { + } + + void fire(Frame& frame) override + { + UserGestureIndicator gestureIndicator(userGestureToForward()); + + ResourceResponse replacementResponse(m_originDocument.url(), ASCIILiteral("text/plain"), 0, ASCIILiteral("UTF-8")); + SubstituteData replacementData(SharedBuffer::create(), m_originDocument.url(), replacementResponse, SubstituteData::SessionHistoryVisibility::Hidden); + + ResourceRequest resourceRequest(m_originDocument.url(), emptyString(), ReloadIgnoringCacheData); + FrameLoadRequest frameRequest(m_originDocument.securityOrigin(), resourceRequest, lockHistory(), lockBackForwardList(), MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, shouldOpenExternalURLs()); + frameRequest.setSubstituteData(replacementData); + frame.loader().load(frameRequest); + } + +private: + Document& m_originDocument; }; NavigationScheduler::NavigationScheduler(Frame& frame) : m_frame(frame) - , m_timer(this, &NavigationScheduler::timerFired) + , m_timer(*this, &NavigationScheduler::timerFired) { } @@ -304,12 +358,16 @@ inline bool NavigationScheduler::shouldScheduleNavigation() const return m_frame.page(); } -inline bool NavigationScheduler::shouldScheduleNavigation(const String& url) const +inline bool NavigationScheduler::shouldScheduleNavigation(const URL& url) const { - return shouldScheduleNavigation() && (protocolIsJavaScript(url) || NavigationDisablerForBeforeUnload::isNavigationAllowed()); + if (!shouldScheduleNavigation()) + return false; + if (protocolIsJavaScript(url)) + return true; + return NavigationDisabler::isNavigationAllowed(); } -void NavigationScheduler::scheduleRedirect(double delay, const String& url) +void NavigationScheduler::scheduleRedirect(Document& initiatingDocument, double delay, const URL& url) { if (!shouldScheduleNavigation(url)) return; @@ -319,16 +377,18 @@ void NavigationScheduler::scheduleRedirect(double delay, const String& url) return; // We want a new back/forward list item if the refresh timeout is > 1 second. - if (!m_redirect || delay <= m_redirect->delay()) - schedule(std::make_unique<ScheduledRedirect>(delay, m_frame.document()->securityOrigin(), url, true, delay <= 1)); + if (!m_redirect || delay <= m_redirect->delay()) { + auto lockBackForwardList = delay <= 1 ? LockBackForwardList::Yes : LockBackForwardList::No; + schedule(std::make_unique<ScheduledRedirect>(initiatingDocument, delay, &m_frame.document()->securityOrigin(), url, LockHistory::Yes, lockBackForwardList)); + } } -bool NavigationScheduler::mustLockBackForwardList(Frame& targetFrame) +LockBackForwardList NavigationScheduler::mustLockBackForwardList(Frame& targetFrame) { // Non-user navigation before the page has finished firing onload should not create a new back/forward item. // See https://webkit.org/b/42861 for the original motivation for this. - if (!ScriptController::processingUserGesture() && targetFrame.loader().documentLoader() && !targetFrame.loader().documentLoader()->wasOnloadHandled()) - return true; + if (!UserGestureIndicator::processingUserGesture() && targetFrame.loader().documentLoader() && !targetFrame.loader().documentLoader()->wasOnloadDispatched()) + return LockBackForwardList::Yes; // Navigation of a subframe during loading of an ancestor frame does not create a new back/forward item. // The definition of "during load" is any time before all handlers for the load event have been run. @@ -336,38 +396,38 @@ bool NavigationScheduler::mustLockBackForwardList(Frame& targetFrame) for (Frame* ancestor = targetFrame.tree().parent(); ancestor; ancestor = ancestor->tree().parent()) { Document* document = ancestor->document(); if (!ancestor->loader().isComplete() || (document && document->processingLoadEvent())) - return true; + return LockBackForwardList::Yes; } - return false; + return LockBackForwardList::No; } -void NavigationScheduler::scheduleLocationChange(SecurityOrigin* securityOrigin, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList) +void NavigationScheduler::scheduleLocationChange(Document& initiatingDocument, SecurityOrigin& securityOrigin, const URL& url, const String& referrer, LockHistory lockHistory, LockBackForwardList lockBackForwardList) { if (!shouldScheduleNavigation(url)) return; - if (url.isEmpty()) - return; - lockBackForwardList = lockBackForwardList || mustLockBackForwardList(m_frame); + if (lockBackForwardList == LockBackForwardList::No) + lockBackForwardList = mustLockBackForwardList(m_frame); FrameLoader& loader = m_frame.loader(); // If the URL we're going to navigate to is the same as the current one, except for the // fragment part, we don't need to schedule the location change. - URL parsedURL(ParsedURLString, url); - if (parsedURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(m_frame.document()->url(), parsedURL)) { - loader.changeLocation(securityOrigin, m_frame.document()->completeURL(url), referrer, lockHistory, lockBackForwardList); + if (url.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(m_frame.document()->url(), url)) { + ResourceRequest resourceRequest(m_frame.document()->completeURL(url), referrer, UseProtocolCachePolicy); + FrameLoadRequest frameRequest(securityOrigin, resourceRequest, "_self", lockHistory, lockBackForwardList, MaybeSendReferrer, AllowNavigationToInvalidURL::No, NewFrameOpenerPolicy::Allow, ReplaceDocumentIfJavaScriptURL, initiatingDocument.shouldOpenExternalURLsPolicyToPropagate()); + loader.changeLocation(frameRequest); return; } // Handle a location change of a page with no document as a special case. // This may happen when a frame changes the location of another frame. - bool duringLoad = !loader.stateMachine()->committedFirstRealDocumentLoad(); + bool duringLoad = !loader.stateMachine().committedFirstRealDocumentLoad(); - schedule(std::make_unique<ScheduledLocationChange>(securityOrigin, url, referrer, lockHistory, lockBackForwardList, duringLoad)); + schedule(std::make_unique<ScheduledLocationChange>(initiatingDocument, &securityOrigin, url, referrer, lockHistory, lockBackForwardList, duringLoad)); } -void NavigationScheduler::scheduleFormSubmission(PassRefPtr<FormSubmission> submission) +void NavigationScheduler::scheduleFormSubmission(Ref<FormSubmission>&& submission) { ASSERT(m_frame.page()); @@ -376,19 +436,21 @@ void NavigationScheduler::scheduleFormSubmission(PassRefPtr<FormSubmission> subm // Handle a location change of a page with no document as a special case. // This may happen when a frame changes the location of another frame. - bool duringLoad = !m_frame.loader().stateMachine()->committedFirstRealDocumentLoad(); + bool duringLoad = !m_frame.loader().stateMachine().committedFirstRealDocumentLoad(); // If this is a child frame and the form submission was triggered by a script, lock the back/forward list // to match IE and Opera. // See https://bugs.webkit.org/show_bug.cgi?id=32383 for the original motivation for this. - bool lockBackForwardList = mustLockBackForwardList(m_frame) - || (submission->state()->formSubmissionTrigger() == SubmittedByJavaScript - && m_frame.tree().parent() && !ScriptController::processingUserGesture()); - - schedule(std::make_unique<ScheduledFormSubmission>(submission, lockBackForwardList, duringLoad)); + LockBackForwardList lockBackForwardList = mustLockBackForwardList(m_frame); + if (lockBackForwardList == LockBackForwardList::No + && (submission->state().formSubmissionTrigger() == SubmittedByJavaScript && m_frame.tree().parent() && !UserGestureIndicator::processingUserGesture())) { + lockBackForwardList = LockBackForwardList::Yes; + } + + schedule(std::make_unique<ScheduledFormSubmission>(WTFMove(submission), lockBackForwardList, duringLoad)); } -void NavigationScheduler::scheduleRefresh() +void NavigationScheduler::scheduleRefresh(Document& initiatingDocument) { if (!shouldScheduleNavigation()) return; @@ -396,11 +458,12 @@ void NavigationScheduler::scheduleRefresh() if (url.isEmpty()) return; - schedule(std::make_unique<ScheduledRefresh>(m_frame.document()->securityOrigin(), url.string(), m_frame.loader().outgoingReferrer())); + schedule(std::make_unique<ScheduledRefresh>(initiatingDocument, &m_frame.document()->securityOrigin(), url, m_frame.loader().outgoingReferrer())); } void NavigationScheduler::scheduleHistoryNavigation(int steps) { + LOG(History, "NavigationScheduler %p scheduleHistoryNavigation(%d) - shouldSchedule %d", this, steps, shouldScheduleNavigation()); if (!shouldScheduleNavigation()) return; @@ -416,7 +479,13 @@ void NavigationScheduler::scheduleHistoryNavigation(int steps) schedule(std::make_unique<ScheduledHistoryNavigation>(steps)); } -void NavigationScheduler::timerFired(Timer<NavigationScheduler>&) +void NavigationScheduler::schedulePageBlock(Document& originDocument) +{ + if (shouldScheduleNavigation()) + schedule(std::make_unique<ScheduledPageBlock>(originDocument)); +} + +void NavigationScheduler::timerFired() { if (!m_frame.page()) return; @@ -427,7 +496,9 @@ void NavigationScheduler::timerFired(Timer<NavigationScheduler>&) Ref<Frame> protect(m_frame); - std::unique_ptr<ScheduledNavigation> redirect = std::move(m_redirect); + std::unique_ptr<ScheduledNavigation> redirect = WTFMove(m_redirect); + LOG(History, "NavigationScheduler %p timerFired - firing redirect %p", this, redirect.get()); + redirect->fire(m_frame); InspectorInstrumentation::frameClearedScheduledNavigation(m_frame); } @@ -448,7 +519,7 @@ void NavigationScheduler::schedule(std::unique_ptr<ScheduledNavigation> redirect } cancel(); - m_redirect = std::move(redirect); + m_redirect = WTFMove(redirect); if (!m_frame.loader().isComplete() && m_redirect->isLocationChange()) m_frame.loader().completed(); @@ -478,11 +549,13 @@ void NavigationScheduler::startTimer() void NavigationScheduler::cancel(bool newLoadInProgress) { + LOG(History, "NavigationScheduler %p cancel(newLoadInProgress=%d)", this, newLoadInProgress); + if (m_timer.isActive()) InspectorInstrumentation::frameClearedScheduledNavigation(m_frame); m_timer.stop(); - if (std::unique_ptr<ScheduledNavigation> redirect = std::move(m_redirect)) + if (std::unique_ptr<ScheduledNavigation> redirect = WTFMove(m_redirect)) redirect->didStopTimer(m_frame, newLoadInProgress); } diff --git a/Source/WebCore/loader/NavigationScheduler.h b/Source/WebCore/loader/NavigationScheduler.h index 78756d6cf..a2a0b94d9 100644 --- a/Source/WebCore/loader/NavigationScheduler.h +++ b/Source/WebCore/loader/NavigationScheduler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2009 Adam Barth. All rights reserved. * @@ -12,7 +12,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. * @@ -28,30 +28,28 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NavigationScheduler_h -#define NavigationScheduler_h +#pragma once +#include "FrameLoaderTypes.h" #include "Timer.h" #include <wtf/Forward.h> -#include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> namespace WebCore { +class Document; class FormSubmission; class Frame; class ScheduledNavigation; class SecurityOrigin; +class URL; -class NavigationDisablerForBeforeUnload { - WTF_MAKE_NONCOPYABLE(NavigationDisablerForBeforeUnload); - +class NavigationDisabler { public: - NavigationDisablerForBeforeUnload() + NavigationDisabler() { s_navigationDisableCount++; } - ~NavigationDisablerForBeforeUnload() + ~NavigationDisabler() { ASSERT(s_navigationDisableCount); s_navigationDisableCount--; @@ -63,8 +61,6 @@ private: }; class NavigationScheduler { - WTF_MAKE_NONCOPYABLE(NavigationScheduler); - public: explicit NavigationScheduler(Frame&); ~NavigationScheduler(); @@ -72,11 +68,12 @@ public: bool redirectScheduledDuringLoad(); bool locationChangePending(); - void scheduleRedirect(double delay, const String& url); - void scheduleLocationChange(SecurityOrigin*, const String& url, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true); - void scheduleFormSubmission(PassRefPtr<FormSubmission>); - void scheduleRefresh(); + void scheduleRedirect(Document& initiatingDocument, double delay, const URL&); + void scheduleLocationChange(Document& initiatingDocument, SecurityOrigin&, const URL&, const String& referrer, LockHistory = LockHistory::Yes, LockBackForwardList = LockBackForwardList::Yes); + void scheduleFormSubmission(Ref<FormSubmission>&&); + void scheduleRefresh(Document& initiatingDocument); void scheduleHistoryNavigation(int steps); + void schedulePageBlock(Document& originDocument); void startTimer(); @@ -85,18 +82,16 @@ public: private: bool shouldScheduleNavigation() const; - bool shouldScheduleNavigation(const String& url) const; + bool shouldScheduleNavigation(const URL&) const; - void timerFired(Timer<NavigationScheduler>&); + void timerFired(); void schedule(std::unique_ptr<ScheduledNavigation>); - static bool mustLockBackForwardList(Frame& targetFrame); + static LockBackForwardList mustLockBackForwardList(Frame& targetFrame); Frame& m_frame; - Timer<NavigationScheduler> m_timer; + Timer m_timer; std::unique_ptr<ScheduledNavigation> m_redirect; }; } // namespace WebCore - -#endif // NavigationScheduler_h diff --git a/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp b/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp index b8c4556fa..6b9a344ea 100644 --- a/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp +++ b/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -34,26 +34,34 @@ #include "FrameLoaderClient.h" #include <wtf/Ref.h> +#if ENABLE(CONTENT_EXTENSIONS) +#include "ResourceLoadInfo.h" +#endif + namespace WebCore { -NetscapePlugInStreamLoader::NetscapePlugInStreamLoader(Frame* frame, NetscapePlugInStreamLoaderClient* client) - : ResourceLoader(frame, ResourceLoaderOptions(SendCallbacks, SniffContent, DoNotBufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType)) - , m_client(client) +// FIXME: Skip Content Security Policy check when associated plugin element is in a user agent shadow tree. +// See <https://bugs.webkit.org/show_bug.cgi?id=146663>. +NetscapePlugInStreamLoader::NetscapePlugInStreamLoader(Frame& frame, NetscapePlugInStreamLoaderClient& client) + : ResourceLoader(frame, ResourceLoaderOptions(SendCallbacks, SniffContent, DoNotBufferData, AllowStoredCredentials, ClientCredentialPolicy::MayAskClientForCredentials, FetchOptions::Credentials::Include, SkipSecurityCheck, FetchOptions::Mode::NoCors, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, CachingPolicy::AllowCaching)) + , m_client(&client) { +#if ENABLE(CONTENT_EXTENSIONS) + m_resourceType = ResourceType::PlugInStream; +#endif } NetscapePlugInStreamLoader::~NetscapePlugInStreamLoader() { } -PassRefPtr<NetscapePlugInStreamLoader> NetscapePlugInStreamLoader::create(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request) +RefPtr<NetscapePlugInStreamLoader> NetscapePlugInStreamLoader::create(Frame& frame, NetscapePlugInStreamLoaderClient& client, const ResourceRequest& request) { - RefPtr<NetscapePlugInStreamLoader> loader(adoptRef(new NetscapePlugInStreamLoader(frame, client))); - loader->documentLoader()->addPlugInStreamLoader(loader.get()); + auto loader(adoptRef(new NetscapePlugInStreamLoader(frame, client))); if (!loader->init(request)) - return 0; + return nullptr; - return loader.release(); + return loader; } bool NetscapePlugInStreamLoader::isDone() const @@ -63,13 +71,38 @@ bool NetscapePlugInStreamLoader::isDone() const void NetscapePlugInStreamLoader::releaseResources() { - m_client = 0; + m_client = nullptr; ResourceLoader::releaseResources(); } +bool NetscapePlugInStreamLoader::init(const ResourceRequest& request) +{ + if (!ResourceLoader::init(request)) + return false; + + ASSERT(!reachedTerminalState()); + + m_documentLoader->addPlugInStreamLoader(*this); + m_isInitialized = true; + + return true; +} + +void NetscapePlugInStreamLoader::willSendRequest(ResourceRequest&& request, const ResourceResponse& redirectResponse, std::function<void(ResourceRequest&&)>&& callback) +{ + RefPtr<NetscapePlugInStreamLoader> protectedThis(this); + + m_client->willSendRequest(this, WTFMove(request), redirectResponse, [protectedThis, redirectResponse, callback](ResourceRequest request) { + if (!request.isNull()) + protectedThis->willSendRequestInternal(request, redirectResponse); + + callback(WTFMove(request)); + }); +} + void NetscapePlugInStreamLoader::didReceiveResponse(const ResourceResponse& response) { - Ref<NetscapePlugInStreamLoader> protect(*this); + Ref<NetscapePlugInStreamLoader> protectedThis(*this); m_client->didReceiveResponse(this, response); @@ -96,37 +129,39 @@ void NetscapePlugInStreamLoader::didReceiveResponse(const ResourceResponse& resp void NetscapePlugInStreamLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType) { - didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType); + didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType); } -void NetscapePlugInStreamLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType) +void NetscapePlugInStreamLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { - didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType); + didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType); } -void NetscapePlugInStreamLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType) +void NetscapePlugInStreamLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { - Ref<NetscapePlugInStreamLoader> protect(*this); + Ref<NetscapePlugInStreamLoader> protectedThis(*this); m_client->didReceiveData(this, buffer ? buffer->data() : data, buffer ? buffer->size() : length); - ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType); + ResourceLoader::didReceiveDataOrBuffer(data, length, WTFMove(buffer), encodedDataLength, dataPayloadType); } void NetscapePlugInStreamLoader::didFinishLoading(double finishTime) { - Ref<NetscapePlugInStreamLoader> protect(*this); + Ref<NetscapePlugInStreamLoader> protectedThis(*this); + + notifyDone(); - m_documentLoader->removePlugInStreamLoader(this); m_client->didFinishLoading(this); ResourceLoader::didFinishLoading(finishTime); } void NetscapePlugInStreamLoader::didFail(const ResourceError& error) { - Ref<NetscapePlugInStreamLoader> protect(*this); + Ref<NetscapePlugInStreamLoader> protectedThis(*this); + + notifyDone(); - m_documentLoader->removePlugInStreamLoader(this); m_client->didFail(this, error); ResourceLoader::didFail(error); } @@ -138,10 +173,16 @@ void NetscapePlugInStreamLoader::willCancel(const ResourceError& error) void NetscapePlugInStreamLoader::didCancel(const ResourceError&) { - // We need to remove the stream loader after the call to didFail, since didFail can - // spawn a new run loop and if the loader has been removed it won't be deferred when - // the document loader is asked to defer loading. - m_documentLoader->removePlugInStreamLoader(this); + notifyDone(); +} + +void NetscapePlugInStreamLoader::notifyDone() +{ + if (!m_isInitialized) + return; + + m_documentLoader->removePlugInStreamLoader(*this); } + } diff --git a/Source/WebCore/loader/NetscapePlugInStreamLoader.h b/Source/WebCore/loader/NetscapePlugInStreamLoader.h index be5ac8e01..1fe772b26 100644 --- a/Source/WebCore/loader/NetscapePlugInStreamLoader.h +++ b/Source/WebCore/loader/NetscapePlugInStreamLoader.h @@ -10,7 +10,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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NetscapePlugInStreamLoader_h -#define NetscapePlugInStreamLoader_h +#pragma once #include "ResourceLoader.h" #include <wtf/Forward.h> @@ -38,6 +37,7 @@ class NetscapePlugInStreamLoader; class NetscapePlugInStreamLoaderClient { public: + virtual void willSendRequest(NetscapePlugInStreamLoader*, ResourceRequest&&, const ResourceResponse& redirectResponse, std::function<void (ResourceRequest&&)>&&) = 0; virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&) = 0; virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int) = 0; virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&) = 0; @@ -48,32 +48,36 @@ protected: virtual ~NetscapePlugInStreamLoaderClient() { } }; -class NetscapePlugInStreamLoader : public ResourceLoader { +class NetscapePlugInStreamLoader final : public ResourceLoader { public: - static PassRefPtr<NetscapePlugInStreamLoader> create(Frame*, NetscapePlugInStreamLoaderClient*, const ResourceRequest&); + WEBCORE_EXPORT static RefPtr<NetscapePlugInStreamLoader> create(Frame&, NetscapePlugInStreamLoaderClient&, const ResourceRequest&); virtual ~NetscapePlugInStreamLoader(); - bool isDone() const; + WEBCORE_EXPORT bool isDone() const; private: - virtual void didReceiveResponse(const ResourceResponse&) override; - virtual void didReceiveData(const char*, unsigned, long long encodedDataLength, DataPayloadType) override; - virtual void didReceiveBuffer(PassRefPtr<SharedBuffer>, long long encodedDataLength, DataPayloadType) override; - virtual void didFinishLoading(double finishTime) override; - virtual void didFail(const ResourceError&) override; + bool init(const ResourceRequest&) override; - virtual void releaseResources() override; + void willSendRequest(ResourceRequest&&, const ResourceResponse& redirectResponse, std::function<void(ResourceRequest&&)>&& callback) override; + void didReceiveResponse(const ResourceResponse&) override; + void didReceiveData(const char*, unsigned, long long encodedDataLength, DataPayloadType) override; + void didReceiveBuffer(Ref<SharedBuffer>&&, long long encodedDataLength, DataPayloadType) override; + void didFinishLoading(double finishTime) override; + void didFail(const ResourceError&) override; - NetscapePlugInStreamLoader(Frame*, NetscapePlugInStreamLoaderClient*); + void releaseResources() override; - virtual void willCancel(const ResourceError&) override; - virtual void didCancel(const ResourceError&) override; + NetscapePlugInStreamLoader(Frame&, NetscapePlugInStreamLoaderClient&); - void didReceiveDataOrBuffer(const char*, int, PassRefPtr<SharedBuffer>, long long encodedDataLength, DataPayloadType); + void willCancel(const ResourceError&) override; + void didCancel(const ResourceError&) override; + + void didReceiveDataOrBuffer(const char*, int, RefPtr<SharedBuffer>&&, long long encodedDataLength, DataPayloadType); + + void notifyDone(); NetscapePlugInStreamLoaderClient* m_client; + bool m_isInitialized { false }; }; } - -#endif // NetscapePlugInStreamLoader_h diff --git a/Source/WebCore/loader/PingLoader.cpp b/Source/WebCore/loader/PingLoader.cpp index db7cde041..d5fff791f 100644 --- a/Source/WebCore/loader/PingLoader.cpp +++ b/Source/WebCore/loader/PingLoader.cpp @@ -1,5 +1,7 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015 Roopesh Chander (roop@roopc.net) + * Copyright (C) 2015-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -32,111 +34,166 @@ #include "config.h" #include "PingLoader.h" +#include "ContentSecurityPolicy.h" #include "Document.h" #include "FormData.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" +#include "HTTPHeaderNames.h" #include "InspectorInstrumentation.h" +#include "LoaderStrategy.h" #include "Page.h" +#include "PlatformStrategies.h" #include "ProgressTracker.h" #include "ResourceHandle.h" +#include "ResourceLoadInfo.h" #include "ResourceRequest.h" #include "ResourceResponse.h" #include "SecurityOrigin.h" #include "SecurityPolicy.h" +#include "UserContentController.h" #include <wtf/text/CString.h> namespace WebCore { -void PingLoader::loadImage(Frame* frame, const URL& url) +#if !ENABLE(CONTENT_EXTENSIONS) + +// Returns true if we should block the load. +static inline bool processContentExtensionRulesForLoad(const Frame&, ResourceRequest&, ResourceType) +{ + return false; +} + +#else + +// Returns true if we should block the load. +static bool processContentExtensionRulesForLoad(const Frame& frame, ResourceRequest& request, ResourceType resourceType) +{ + auto* documentLoader = frame.loader().documentLoader(); + if (!documentLoader) + return false; + auto* page = frame.page(); + if (!page) + return false; + auto status = page->userContentProvider().processContentExtensionRulesForLoad(request.url(), resourceType, *documentLoader); + applyBlockedStatusToRequest(status, request); + return status.blockedLoad; +} + +#endif + +void PingLoader::loadImage(Frame& frame, const URL& url) { - if (!frame->document()->securityOrigin()->canDisplay(url)) { - FrameLoader::reportLocalLoadFailed(frame, url); + ASSERT(frame.document()); + auto& document = *frame.document(); + + if (!document.securityOrigin().canDisplay(url)) { + FrameLoader::reportLocalLoadFailed(&frame, url); return; } ResourceRequest request(url); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); - String referrer = SecurityPolicy::generateReferrerHeader(frame->document()->referrerPolicy(), request.url(), frame->loader().outgoingReferrer()); + if (processContentExtensionRulesForLoad(frame, request, ResourceType::Image)) + return; + + document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load); + + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"); + String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), request.url(), frame.loader().outgoingReferrer()); if (!referrer.isEmpty()) request.setHTTPReferrer(referrer); - frame->loader().addExtraFieldsToSubresourceRequest(request); + frame.loader().addExtraFieldsToSubresourceRequest(request); - createPingLoader(frame, request); + startPingLoad(frame, request, ShouldFollowRedirects::Yes); } // http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing -void PingLoader::sendPing(Frame* frame, const URL& pingURL, const URL& destinationURL) +void PingLoader::sendPing(Frame& frame, const URL& pingURL, const URL& destinationURL) { + ASSERT(frame.document()); + + if (!pingURL.protocolIsInHTTPFamily()) + return; + ResourceRequest request(pingURL); + if (processContentExtensionRulesForLoad(frame, request, ResourceType::Raw)) + return; + + auto& document = *frame.document(); + document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load); + request.setHTTPMethod("POST"); request.setHTTPContentType("text/ping"); request.setHTTPBody(FormData::create("PING")); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); - frame->loader().addExtraFieldsToSubresourceRequest(request); - - SecurityOrigin* sourceOrigin = frame->document()->securityOrigin(); - RefPtr<SecurityOrigin> pingOrigin = SecurityOrigin::create(pingURL); - FrameLoader::addHTTPOriginIfNeeded(request, sourceOrigin->toString()); - request.setHTTPHeaderField("Ping-To", destinationURL); - if (!SecurityPolicy::shouldHideReferrer(pingURL, frame->loader().outgoingReferrer())) { - request.setHTTPHeaderField("Ping-From", frame->document()->url()); - if (!sourceOrigin->isSameSchemeHostPort(pingOrigin.get())) { - String referrer = SecurityPolicy::generateReferrerHeader(frame->document()->referrerPolicy(), pingURL, frame->loader().outgoingReferrer()); + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"); + frame.loader().addExtraFieldsToSubresourceRequest(request); + + auto& sourceOrigin = document.securityOrigin(); + FrameLoader::addHTTPOriginIfNeeded(request, sourceOrigin.toString()); + request.setHTTPHeaderField(HTTPHeaderName::PingTo, destinationURL); + if (!SecurityPolicy::shouldHideReferrer(pingURL, frame.loader().outgoingReferrer())) { + request.setHTTPHeaderField(HTTPHeaderName::PingFrom, document.url()); + if (!sourceOrigin.isSameSchemeHostPort(SecurityOrigin::create(pingURL).get())) { + String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), pingURL, frame.loader().outgoingReferrer()); if (!referrer.isEmpty()) request.setHTTPReferrer(referrer); } } - createPingLoader(frame, request); + startPingLoad(frame, request, ShouldFollowRedirects::Yes); } -void PingLoader::sendViolationReport(Frame* frame, const URL& reportURL, PassRefPtr<FormData> report) +void PingLoader::sendViolationReport(Frame& frame, const URL& reportURL, Ref<FormData>&& report, ViolationReportType reportType) { + ASSERT(frame.document()); + ResourceRequest request(reportURL); - request.setHTTPMethod("POST"); - request.setHTTPContentType("application/json"); - request.setHTTPBody(report); - request.setAllowCookies(frame->document()->securityOrigin()->isSameSchemeHostPort(SecurityOrigin::create(reportURL).get())); - frame->loader().addExtraFieldsToSubresourceRequest(request); + if (processContentExtensionRulesForLoad(frame, request, ResourceType::Raw)) + return; + + auto& document = *frame.document(); + document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load); + + request.setHTTPMethod(ASCIILiteral("POST")); + request.setHTTPBody(WTFMove(report)); + switch (reportType) { + case ViolationReportType::ContentSecurityPolicy: + request.setHTTPContentType(ASCIILiteral("application/csp-report")); + break; + case ViolationReportType::XSSAuditor: + request.setHTTPContentType(ASCIILiteral("application/json")); + break; + } - String referrer = SecurityPolicy::generateReferrerHeader(frame->document()->referrerPolicy(), reportURL, frame->loader().outgoingReferrer()); + bool removeCookies = true; + if (document.securityOrigin().isSameSchemeHostPort(SecurityOrigin::create(reportURL).get())) + removeCookies = false; + if (removeCookies) + request.setAllowCookies(false); + + frame.loader().addExtraFieldsToSubresourceRequest(request); + + String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), reportURL, frame.loader().outgoingReferrer()); if (!referrer.isEmpty()) request.setHTTPReferrer(referrer); - createPingLoader(frame, request); -} - -void PingLoader::createPingLoader(Frame* frame, ResourceRequest& request) -{ - // No need to free the PingLoader object or manage it via a smart pointer - it will kill itself as soon as it receives a response. - new PingLoader(frame, request); + startPingLoad(frame, request, ShouldFollowRedirects::No); } -PingLoader::PingLoader(Frame* frame, ResourceRequest& request) - : m_timeout(this, &PingLoader::timeoutTimerFired) +void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, ShouldFollowRedirects shouldFollowRedirects) { - unsigned long identifier = frame->page()->progress().createUniqueIdentifier(); + unsigned long identifier = frame.page()->progress().createUniqueIdentifier(); // FIXME: Why activeDocumentLoader? I would have expected documentLoader(). // Itseems like the PingLoader should be associated with the current // Document in the Frame, but the activeDocumentLoader will be associated // with the provisional DocumentLoader if there is a provisional // DocumentLoader. - m_shouldUseCredentialStorage = frame->loader().client().shouldUseCredentialStorage(frame->loader().activeDocumentLoader(), identifier); - m_handle = ResourceHandle::create(frame->loader().networkingContext(), request, this, false, false); - - InspectorInstrumentation::continueAfterPingLoader(frame, identifier, frame->loader().activeDocumentLoader(), request, ResourceResponse()); + bool shouldUseCredentialStorage = frame.loader().client().shouldUseCredentialStorage(frame.loader().activeDocumentLoader(), identifier); - // If the server never responds, FrameLoader won't be able to cancel this load and - // we'll sit here waiting forever. Set a very generous timeout, just in case. - m_timeout.startOneShot(60000); -} + InspectorInstrumentation::continueAfterPingLoader(frame, identifier, frame.loader().activeDocumentLoader(), request, ResourceResponse()); -PingLoader::~PingLoader() -{ - if (m_handle) - m_handle->cancel(); + platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, shouldUseCredentialStorage, shouldFollowRedirects == ShouldFollowRedirects::Yes); } } diff --git a/Source/WebCore/loader/PingLoader.h b/Source/WebCore/loader/PingLoader.h index 295e2f4f2..b29e8da8f 100644 --- a/Source/WebCore/loader/PingLoader.h +++ b/Source/WebCore/loader/PingLoader.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,53 +30,31 @@ * */ -#ifndef PingLoader_h -#define PingLoader_h +#pragma once -#include "ResourceHandleClient.h" -#include "Timer.h" -#include <wtf/Noncopyable.h> -#include <wtf/RefPtr.h> +#include <wtf/Ref.h> namespace WebCore { class FormData; class Frame; class URL; -class ResourceError; -class ResourceHandle; -class ResourceResponse; +class ResourceRequest; -// This class triggers asynchronous loads independent of Frame staying alive (i.e., auditing pingbacks). -// Since nothing depends on resources loaded through this class, we just want -// to allow the load to live long enough to ensure the message was actually sent. -// Therefore, as soon as a callback is received from the ResourceHandle, this class -// will cancel the load and delete itself. -class PingLoader : private ResourceHandleClient { - WTF_MAKE_NONCOPYABLE(PingLoader); WTF_MAKE_FAST_ALLOCATED; -public: - static void loadImage(Frame*, const URL& url); - static void sendPing(Frame*, const URL& pingURL, const URL& destinationURL); - static void sendViolationReport(Frame*, const URL& reportURL, PassRefPtr<FormData> report); +enum class ViolationReportType { + ContentSecurityPolicy, + XSSAuditor, +}; - virtual ~PingLoader(); +class PingLoader { +public: + static void loadImage(Frame&, const URL&); + static void sendPing(Frame&, const URL& pingURL, const URL& destinationURL); + static void sendViolationReport(Frame&, const URL& reportURL, Ref<FormData>&& report, ViolationReportType); private: - static void createPingLoader(Frame*, ResourceRequest&); - PingLoader(Frame*, ResourceRequest&); - - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) override { delete this; } - virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int) override { delete this; } - virtual void didFinishLoading(ResourceHandle*, double) override { delete this; } - virtual void didFail(ResourceHandle*, const ResourceError&) override { delete this; } - virtual bool shouldUseCredentialStorage(ResourceHandle*) override { return m_shouldUseCredentialStorage; } - void timeoutTimerFired(Timer<PingLoader>&) { delete this; } - - RefPtr<ResourceHandle> m_handle; - Timer<PingLoader> m_timeout; - bool m_shouldUseCredentialStorage; + enum class ShouldFollowRedirects { No, Yes }; + static void startPingLoad(Frame&, ResourceRequest&, ShouldFollowRedirects); }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/PolicyCallback.cpp b/Source/WebCore/loader/PolicyCallback.cpp index 6ffa8a8cd..f1f633102 100644 --- a/Source/WebCore/loader/PolicyCallback.cpp +++ b/Source/WebCore/loader/PolicyCallback.cpp @@ -12,7 +12,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. * @@ -38,14 +38,6 @@ namespace WebCore { -PolicyCallback::PolicyCallback() -{ -} - -PolicyCallback::~PolicyCallback() -{ -} - void PolicyCallback::clear() { clearRequest(); @@ -54,19 +46,19 @@ void PolicyCallback::clear() m_contentFunction = nullptr; } -void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> formState, +void PolicyCallback::set(const ResourceRequest& request, FormState* formState, NavigationPolicyDecisionFunction function) { m_request = request; m_formState = formState; m_frameName = String(); - m_navigationFunction = std::move(function); + m_navigationFunction = WTFMove(function); m_newWindowFunction = nullptr; m_contentFunction = nullptr; } -void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& navigationAction, NewWindowPolicyDecisionFunction function) +void PolicyCallback::set(const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& navigationAction, NewWindowPolicyDecisionFunction function) { m_request = request; m_formState = formState; @@ -74,7 +66,7 @@ void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> f m_navigationAction = navigationAction; m_navigationFunction = nullptr; - m_newWindowFunction = std::move(function); + m_newWindowFunction = WTFMove(function); m_contentFunction = nullptr; } @@ -86,7 +78,7 @@ void PolicyCallback::set(ContentPolicyDecisionFunction function) m_navigationFunction = nullptr; m_newWindowFunction = nullptr; - m_contentFunction = std::move(function); + m_contentFunction = WTFMove(function); } void PolicyCallback::call(bool shouldContinue) diff --git a/Source/WebCore/loader/PolicyCallback.h b/Source/WebCore/loader/PolicyCallback.h index c70edcc38..d8bd1cc9f 100644 --- a/Source/WebCore/loader/PolicyCallback.h +++ b/Source/WebCore/loader/PolicyCallback.h @@ -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. * @@ -27,8 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PolicyCallback_h -#define PolicyCallback_h +#pragma once #include "FrameLoaderTypes.h" #include "NavigationAction.h" @@ -41,18 +40,15 @@ namespace WebCore { class FormState; -typedef std::function<void (const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue)> NavigationPolicyDecisionFunction; -typedef std::function<void (const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, bool shouldContinue)> NewWindowPolicyDecisionFunction; -typedef std::function<void (PolicyAction)> ContentPolicyDecisionFunction; +using ContentPolicyDecisionFunction = std::function<void(PolicyAction)>; +using NavigationPolicyDecisionFunction = std::function<void(const ResourceRequest&, FormState*, bool shouldContinue)>; +using NewWindowPolicyDecisionFunction = std::function<void(const ResourceRequest&, FormState*, const String& frameName, const NavigationAction&, bool shouldContinue)>; class PolicyCallback { public: - PolicyCallback(); - ~PolicyCallback(); - void clear(); - void set(const ResourceRequest&, PassRefPtr<FormState>, NavigationPolicyDecisionFunction); - void set(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, NewWindowPolicyDecisionFunction); + void set(const ResourceRequest&, FormState*, NavigationPolicyDecisionFunction); + void set(const ResourceRequest&, FormState*, const String& frameName, const NavigationAction&, NewWindowPolicyDecisionFunction); void set(ContentPolicyDecisionFunction); const ResourceRequest& request() const { return m_request; } @@ -74,5 +70,3 @@ private: }; } // namespace WebCore - -#endif // PolicyCallback_h diff --git a/Source/WebCore/loader/PolicyChecker.cpp b/Source/WebCore/loader/PolicyChecker.cpp index 8ead413cc..a01ce2929 100644 --- a/Source/WebCore/loader/PolicyChecker.cpp +++ b/Source/WebCore/loader/PolicyChecker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * @@ -12,7 +12,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,45 +31,59 @@ #include "config.h" #include "PolicyChecker.h" +#include "ContentFilter.h" #include "ContentSecurityPolicy.h" #include "DOMWindow.h" #include "DocumentLoader.h" +#include "EventNames.h" #include "FormState.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "HTMLFormElement.h" #include "HTMLFrameOwnerElement.h" -#include "SecurityOrigin.h" +#include "HTMLPlugInElement.h" #if USE(QUICK_LOOK) #include "QuickLook.h" #endif -#if USE(CONTENT_FILTERING) -#include "ContentFilter.h" -#endif - namespace WebCore { +static bool isAllowedByContentSecurityPolicy(const URL& url, const Element* ownerElement, bool didReceiveRedirectResponse) +{ + if (!ownerElement) + return true; + // Elements in user agent show tree should load whatever the embedding document policy is. + if (ownerElement->isInUserAgentShadowTree()) + return true; + + auto redirectResponseReceived = didReceiveRedirectResponse ? ContentSecurityPolicy::RedirectResponseReceived::Yes : ContentSecurityPolicy::RedirectResponseReceived::No; + + ASSERT(ownerElement->document().contentSecurityPolicy()); + if (is<HTMLPlugInElement>(ownerElement)) + return ownerElement->document().contentSecurityPolicy()->allowObjectFromSource(url, redirectResponseReceived); + return ownerElement->document().contentSecurityPolicy()->allowChildFrameFromSource(url, redirectResponseReceived); +} + PolicyChecker::PolicyChecker(Frame& frame) : m_frame(frame) , m_delegateIsDecidingNavigationPolicy(false) , m_delegateIsHandlingUnimplementablePolicy(false) - , m_loadType(FrameLoadTypeStandard) + , m_loadType(FrameLoadType::Standard) { } -void PolicyChecker::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function) +void PolicyChecker::checkNavigationPolicy(const ResourceRequest& newRequest, bool didReceiveRedirectResponse, NavigationPolicyDecisionFunction function) { - checkNavigationPolicy(newRequest, m_frame.loader().activeDocumentLoader(), nullptr, std::move(function)); + checkNavigationPolicy(newRequest, didReceiveRedirectResponse, m_frame.loader().activeDocumentLoader(), nullptr, WTFMove(function)); } -void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader, PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function) +void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, bool didReceiveRedirectResponse, DocumentLoader* loader, FormState* formState, NavigationPolicyDecisionFunction function) { NavigationAction action = loader->triggeringAction(); if (action.isEmpty()) { - action = NavigationAction(request, NavigationTypeOther); + action = NavigationAction(request, NavigationType::Other, loader->shouldOpenExternalURLsPolicyToPropagate()); loader->setTriggeringAction(action); } @@ -83,23 +97,31 @@ void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, Docume // We are always willing to show alternate content for unreachable URLs; // treat it like a reload so it maintains the right state for b/f list. - if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) { + auto& substituteData = loader->substituteData(); + if (substituteData.isValid() && !substituteData.failingURL().isEmpty()) { + bool shouldContinue = true; +#if ENABLE(CONTENT_FILTERING) + shouldContinue = ContentFilter::continueAfterSubstituteDataRequest(*m_frame.loader().activeDocumentLoader(), substituteData); +#endif if (isBackForwardLoadType(m_loadType)) - m_loadType = FrameLoadTypeReload; - function(request, 0, true); + m_loadType = FrameLoadType::Reload; + function(request, 0, shouldContinue); return; } - // If we're loading content into a subframe, check against the parent's Content Security Policy - // and kill the load if that check fails. - if (m_frame.ownerElement() && !m_frame.ownerElement()->document().contentSecurityPolicy()->allowChildFrameFromSource(request.url())) { + if (!isAllowedByContentSecurityPolicy(request.url(), m_frame.ownerElement(), didReceiveRedirectResponse)) { + if (m_frame.ownerElement()) { + // Fire a load event (even though we were blocked by CSP) as timing attacks would otherwise + // reveal that the frame was blocked. This way, it looks like any other cross-origin page load. + m_frame.ownerElement()->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); + } function(request, 0, false); return; } loader->setLastCheckedRequest(request); - m_callback.set(request, formState.get(), std::move(function)); + m_callback.set(request, formState, WTFMove(function)); #if USE(QUICK_LOOK) // Always allow QuickLook-generated URLs based on the protocol scheme. @@ -109,31 +131,36 @@ void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, Docume } #endif -#if USE(CONTENT_FILTERING) - if (DocumentLoader* documentLoader = m_frame.loader().documentLoader()) { - if (documentLoader->handleContentFilterRequest(request)) { - continueAfterNavigationPolicy(PolicyIgnore); - return; - } +#if ENABLE(CONTENT_FILTERING) + if (m_contentFilterUnblockHandler.canHandleRequest(request)) { + RefPtr<Frame> frame { &m_frame }; + m_contentFilterUnblockHandler.requestUnblockAsync([frame](bool unblocked) { + if (unblocked) + frame->loader().reload(); + }); + continueAfterNavigationPolicy(PolicyIgnore); + return; } + m_contentFilterUnblockHandler = { }; #endif m_delegateIsDecidingNavigationPolicy = true; + m_suggestedFilename = action.downloadAttribute(); m_frame.loader().client().dispatchDecidePolicyForNavigationAction(action, request, formState, [this](PolicyAction action) { continueAfterNavigationPolicy(action); }); m_delegateIsDecidingNavigationPolicy = false; } -void PolicyChecker::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, NewWindowPolicyDecisionFunction function) +void PolicyChecker::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request, FormState* formState, const String& frameName, NewWindowPolicyDecisionFunction function) { if (m_frame.document() && m_frame.document()->isSandboxed(SandboxPopups)) return continueAfterNavigationPolicy(PolicyIgnore); - if (!DOMWindow::allowPopUp(&m_frame)) + if (!DOMWindow::allowPopUp(m_frame)) return continueAfterNavigationPolicy(PolicyIgnore); - m_callback.set(request, formState, frameName, action, std::move(function)); + m_callback.set(request, formState, frameName, action, WTFMove(function)); m_frame.loader().client().dispatchDecidePolicyForNewWindowAction(action, request, formState, frameName, [this](PolicyAction action) { continueAfterNewWindowPolicy(action); }); @@ -141,7 +168,7 @@ void PolicyChecker::checkNewWindowPolicy(const NavigationAction& action, const R void PolicyChecker::checkContentPolicy(const ResourceResponse& response, ContentPolicyDecisionFunction function) { - m_callback.set(std::move(function)); + m_callback.set(WTFMove(function)); m_frame.loader().client().dispatchDecidePolicyForResponse(response, m_frame.loader().activeDocumentLoader()->request(), [this](PolicyAction action) { continueAfterContentPolicy(action); }); @@ -187,7 +214,7 @@ void PolicyChecker::continueAfterNavigationPolicy(PolicyAction policy) case PolicyDownload: { ResourceRequest request = callback.request(); m_frame.loader().setOriginalURLForDownloadRequest(request); - m_frame.loader().client().startDownload(request); + m_frame.loader().client().startDownload(request, m_suggestedFilename); callback.clearRequest(); break; } diff --git a/Source/WebCore/loader/PolicyChecker.h b/Source/WebCore/loader/PolicyChecker.h index b1772e62e..16b0bed82 100644 --- a/Source/WebCore/loader/PolicyChecker.h +++ b/Source/WebCore/loader/PolicyChecker.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * 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. * @@ -27,15 +27,17 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PolicyChecker_h -#define PolicyChecker_h +#pragma once #include "FrameLoaderTypes.h" #include "PolicyCallback.h" #include "ResourceRequest.h" -#include <wtf/PassRefPtr.h> #include <wtf/text/WTFString.h> +#if ENABLE(CONTENT_FILTERING) +#include "ContentFilterUnblockHandler.h" +#endif + namespace WebCore { class DocumentLoader; @@ -47,12 +49,13 @@ class ResourceResponse; class PolicyChecker { WTF_MAKE_NONCOPYABLE(PolicyChecker); + WTF_MAKE_FAST_ALLOCATED; public: explicit PolicyChecker(Frame&); - void checkNavigationPolicy(const ResourceRequest&, DocumentLoader*, PassRefPtr<FormState>, NavigationPolicyDecisionFunction); - void checkNavigationPolicy(const ResourceRequest&, NavigationPolicyDecisionFunction); - void checkNewWindowPolicy(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, NewWindowPolicyDecisionFunction); + void checkNavigationPolicy(const ResourceRequest&, bool didReceiveRedirectResponse, DocumentLoader*, FormState*, NavigationPolicyDecisionFunction); + void checkNavigationPolicy(const ResourceRequest&, bool didReceiveRedirectResponse, NavigationPolicyDecisionFunction); + void checkNewWindowPolicy(const NavigationAction&, const ResourceRequest&, FormState*, const String& frameName, NewWindowPolicyDecisionFunction); void checkContentPolicy(const ResourceResponse&, ContentPolicyDecisionFunction); // FIXME: These are different. They could use better names. @@ -64,6 +67,8 @@ public: FrameLoadType loadType() const { return m_loadType; } void setLoadType(FrameLoadType loadType) { m_loadType = loadType; } + void setSuggestedFilename(const String& suggestedFilename) { m_suggestedFilename = suggestedFilename; } + bool delegateIsDecidingNavigationPolicy() const { return m_delegateIsDecidingNavigationPolicy; } bool delegateIsHandlingUnimplementablePolicy() const { return m_delegateIsHandlingUnimplementablePolicy; } @@ -74,6 +79,10 @@ public: // the heart to hack on all the platforms to make that happen right now. void continueLoadAfterWillSubmitForm(PolicyAction); +#if ENABLE(CONTENT_FILTERING) + void setContentFilterUnblockHandler(ContentFilterUnblockHandler unblockHandler) { m_contentFilterUnblockHandler = WTFMove(unblockHandler); } +#endif + private: void continueAfterNavigationPolicy(PolicyAction); void continueAfterNewWindowPolicy(PolicyAction); @@ -91,8 +100,11 @@ private: // on navigation action delegate callbacks. FrameLoadType m_loadType; PolicyCallback m_callback; + String m_suggestedFilename; + +#if ENABLE(CONTENT_FILTERING) + ContentFilterUnblockHandler m_contentFilterUnblockHandler; +#endif }; } // namespace WebCore - -#endif // PolicyChecker_h diff --git a/Source/WebCore/loader/ProgressTracker.cpp b/Source/WebCore/loader/ProgressTracker.cpp index 291c13643..fe0c7229e 100644 --- a/Source/WebCore/loader/ProgressTracker.cpp +++ b/Source/WebCore/loader/ProgressTracker.cpp @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -60,6 +60,8 @@ static const unsigned loadStalledHeartbeatCount = 4; // How many bytes are required between heartbeats to consider it progress. static const unsigned minumumBytesPerHeartbeatForProgress = 1024; +static const auto progressNotificationTimeInterval = 200ms; + struct ProgressItem { WTF_MAKE_NONCOPYABLE(ProgressItem); WTF_MAKE_FAST_ALLOCATED; public: @@ -80,16 +82,12 @@ ProgressTracker::ProgressTracker(ProgressTrackerClient& client) , m_totalPageAndResourceBytesToLoad(0) , m_totalBytesReceived(0) , m_lastNotifiedProgressValue(0) - , m_lastNotifiedProgressTime(0) - , m_progressNotificationInterval(0.02) - , m_progressNotificationTimeInterval(0.1) , m_finalProgressChangedSent(false) , m_progressValue(0) , m_numProgressTrackedFrames(0) - , m_progressHeartbeatTimer(this, &ProgressTracker::progressHeartbeatTimerFired) + , m_progressHeartbeatTimer(*this, &ProgressTracker::progressHeartbeatTimerFired) , m_heartbeatsWithNoProgress(0) , m_totalBytesReceivedBeforePreviousHeartbeat(0) - , m_mainLoadCompletionTimeStamp(0) , m_isMainLoad(false) { } @@ -112,10 +110,10 @@ void ProgressTracker::reset() m_totalBytesReceived = 0; m_progressValue = 0; m_lastNotifiedProgressValue = 0; - m_lastNotifiedProgressTime = 0; + m_lastNotifiedProgressTime = std::chrono::steady_clock::time_point(); m_finalProgressChangedSent = false; m_numProgressTrackedFrames = 0; - m_originatingProgressFrame = 0; + m_originatingProgressFrame = nullptr; m_heartbeatsWithNoProgress = 0; m_totalBytesReceivedBeforePreviousHeartbeat = 0; @@ -137,9 +135,9 @@ void ProgressTracker::progressStarted(Frame& frame) m_originatingProgressFrame->loader().loadProgressingStatusChanged(); bool isMainFrame = !m_originatingProgressFrame->tree().parent(); - double elapsedTimeSinceMainLoadComplete = monotonicallyIncreasingTime() - m_mainLoadCompletionTimeStamp; + auto elapsedTimeSinceMainLoadComplete = std::chrono::steady_clock::now() - m_mainLoadCompletionTime; - static const double subframePartOfMainLoadThreshold = 1; + static const auto subframePartOfMainLoadThreshold = 1s; m_isMainLoad = isMainFrame || elapsedTimeSinceMainLoadComplete < subframePartOfMainLoadThreshold; m_client.progressStarted(*m_originatingProgressFrame); @@ -170,7 +168,7 @@ void ProgressTracker::finalProgressComplete() { LOG(Progress, "Final progress complete (%p)", this); - RefPtr<Frame> frame = m_originatingProgressFrame.release(); + auto frame = WTFMove(m_originatingProgressFrame); // Before resetting progress value be sure to send client a least one notification // with final progress value. @@ -182,7 +180,7 @@ void ProgressTracker::finalProgressComplete() reset(); if (m_isMainLoad) - m_mainLoadCompletionTimeStamp = monotonicallyIncreasingTime(); + m_mainLoadCompletionTime = std::chrono::steady_clock::now(); frame->loader().client().setMainFrameDocumentReady(true); m_client.progressFinished(*frame); @@ -236,7 +234,7 @@ void ProgressTracker::incrementProgress(unsigned long identifier, unsigned bytes } int numPendingOrLoadingRequests = frame->loader().numPendingOrLoadingRequests(true); - estimatedBytesForPendingRequests = progressItemDefaultEstimatedLength * numPendingOrLoadingRequests; + estimatedBytesForPendingRequests = static_cast<long long>(progressItemDefaultEstimatedLength) * numPendingOrLoadingRequests; remainingBytes = ((m_totalPageAndResourceBytesToLoad + estimatedBytesForPendingRequests) - m_totalBytesReceived); if (remainingBytes > 0) // Prevent divide by 0. percentOfRemainingBytes = (double)bytesReceived / (double)remainingBytes; @@ -246,7 +244,7 @@ void ProgressTracker::incrementProgress(unsigned long identifier, unsigned bytes // For documents that use WebCore's layout system, treat first layout as the half-way point. // FIXME: The hasHTMLView function is a sort of roundabout way of asking "do you use WebCore's layout system". bool useClampedMaxProgress = frame->loader().client().hasHTMLView() - && !frame->loader().stateMachine()->firstLayoutDone(); + && !frame->loader().stateMachine().firstLayoutDone(); double maxProgressValue = useClampedMaxProgress ? 0.5 : finalProgressValue; increment = (maxProgressValue - m_progressValue) * percentOfRemainingBytes; m_progressValue += increment; @@ -255,14 +253,11 @@ void ProgressTracker::incrementProgress(unsigned long identifier, unsigned bytes m_totalBytesReceived += bytesReceived; - double now = monotonicallyIncreasingTime(); - double notifiedProgressTimeDelta = now - m_lastNotifiedProgressTime; + auto now = std::chrono::steady_clock::now(); + auto notifiedProgressTimeDelta = now - m_lastNotifiedProgressTime; LOG(Progress, "Progress incremented (%p) - value %f, tracked frames %d", this, m_progressValue, m_numProgressTrackedFrames); - double notificationProgressDelta = m_progressValue - m_lastNotifiedProgressValue; - if ((notificationProgressDelta >= m_progressNotificationInterval || - notifiedProgressTimeDelta >= m_progressNotificationTimeInterval) && - m_numProgressTrackedFrames > 0) { + if ((notifiedProgressTimeDelta >= progressNotificationTimeInterval || m_progressValue == 1) && m_numProgressTrackedFrames > 0) { if (!m_finalProgressChangedSent) { if (m_progressValue == 1) m_finalProgressChangedSent = true; @@ -310,7 +305,7 @@ bool ProgressTracker::isMainLoadProgressing() const return m_progressValue && m_progressValue < finalProgressValue && m_heartbeatsWithNoProgress < loadStalledHeartbeatCount; } -void ProgressTracker::progressHeartbeatTimerFired(Timer<ProgressTracker>&) +void ProgressTracker::progressHeartbeatTimerFired() { if (m_totalBytesReceived < m_totalBytesReceivedBeforePreviousHeartbeat + minumumBytesPerHeartbeatForProgress) ++m_heartbeatsWithNoProgress; diff --git a/Source/WebCore/loader/ProgressTracker.h b/Source/WebCore/loader/ProgressTracker.h index 272de2778..71739aef8 100644 --- a/Source/WebCore/loader/ProgressTracker.h +++ b/Source/WebCore/loader/ProgressTracker.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,14 +23,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ProgressTracker_h -#define ProgressTracker_h +#pragma once #include "Timer.h" +#include <chrono> #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -48,7 +47,7 @@ public: static unsigned long createUniqueIdentifier(); - double estimatedProgress() const; + WEBCORE_EXPORT double estimatedProgress() const; void progressStarted(Frame&); void progressCompleted(Frame&); @@ -66,7 +65,7 @@ private: void reset(); void finalProgressComplete(); - void progressHeartbeatTimerFired(Timer<ProgressTracker>&); + void progressHeartbeatTimerFired(); static unsigned long s_uniqueIdentifier; @@ -74,9 +73,7 @@ private: long long m_totalPageAndResourceBytesToLoad; long long m_totalBytesReceived; double m_lastNotifiedProgressValue; - double m_lastNotifiedProgressTime; - double m_progressNotificationInterval; - double m_progressNotificationTimeInterval; + std::chrono::steady_clock::time_point m_lastNotifiedProgressTime; bool m_finalProgressChangedSent; double m_progressValue; RefPtr<Frame> m_originatingProgressFrame; @@ -84,13 +81,11 @@ private: int m_numProgressTrackedFrames; HashMap<unsigned long, std::unique_ptr<ProgressItem>> m_progressItems; - Timer<ProgressTracker> m_progressHeartbeatTimer; + Timer m_progressHeartbeatTimer; unsigned m_heartbeatsWithNoProgress; long long m_totalBytesReceivedBeforePreviousHeartbeat; - double m_mainLoadCompletionTimeStamp; + std::chrono::steady_clock::time_point m_mainLoadCompletionTime; bool m_isMainLoad; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/ProgressTrackerClient.h b/Source/WebCore/loader/ProgressTrackerClient.h index d3ec32e9f..4e31fe282 100644 --- a/Source/WebCore/loader/ProgressTrackerClient.h +++ b/Source/WebCore/loader/ProgressTrackerClient.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ProgressTrackerClient_h -#define ProgressTrackerClient_h +#pragma once namespace WebCore { @@ -46,5 +45,3 @@ public: }; } // namespace WebCore - -#endif // ProgressTrackerClient_h diff --git a/Source/WebCore/loader/ResourceBuffer.cpp b/Source/WebCore/loader/ResourceBuffer.cpp deleted file mode 100644 index 38afaa41a..000000000 --- a/Source/WebCore/loader/ResourceBuffer.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2012 Apple 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 APPLE COMPUTER, 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 COMPUTER, 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 "ResourceBuffer.h" - -#include "PurgeableBuffer.h" - -namespace WebCore { - -ResourceBuffer::ResourceBuffer() - : m_sharedBuffer(SharedBuffer::create()) -{ -} - -ResourceBuffer::ResourceBuffer(const char* data, unsigned size) - : m_sharedBuffer(SharedBuffer::create(data, size)) -{ -} - -ResourceBuffer::ResourceBuffer(PassRefPtr<SharedBuffer> sharedBuffer) - : m_sharedBuffer(sharedBuffer) -{ - ASSERT(m_sharedBuffer); -} - -ResourceBuffer::~ResourceBuffer() -{ -} - -const char* ResourceBuffer::data() const -{ - return m_sharedBuffer->data(); -} - -unsigned ResourceBuffer::size() const -{ - return m_sharedBuffer->size(); -} - -bool ResourceBuffer::isEmpty() const -{ - return m_sharedBuffer->isEmpty(); -} - -void ResourceBuffer::append(const char* data, unsigned size) -{ - m_sharedBuffer->append(data, size); -} - -void ResourceBuffer::append(SharedBuffer* buffer) -{ - m_sharedBuffer->append(buffer); -} - -#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) -void ResourceBuffer::append(CFDataRef data) -{ - ASSERT(m_sharedBuffer); - m_sharedBuffer->append(data); -} -#endif - -void ResourceBuffer::clear() -{ - m_sharedBuffer->clear(); -} - -unsigned ResourceBuffer::getSomeData(const char*& data, unsigned position) const -{ - return m_sharedBuffer->getSomeData(data, position); -} - -SharedBuffer* ResourceBuffer::sharedBuffer() const -{ - // Currently all ResourceBuffers are backed by SharedBuffers. - // In the future we might have to create the SharedBuffer on demand here. - // We should also phase out as much use of this accessor as possible and have clients - // either use the ResourceBuffer directly or use getSomeData() when sensical. - return m_sharedBuffer.get(); -} - -PassRefPtr<ResourceBuffer> ResourceBuffer::copy() const -{ - return ResourceBuffer::adoptSharedBuffer(m_sharedBuffer->copy()); -} - -bool ResourceBuffer::hasPurgeableBuffer() const -{ - return m_sharedBuffer->hasPurgeableBuffer(); -} - -#if PLATFORM(IOS) -void ResourceBuffer::setShouldUsePurgeableMemory(bool shouldUsePurgeableMemory) -{ - ASSERT(m_sharedBuffer); - sharedBuffer()->shouldUsePurgeableMemory(shouldUsePurgeableMemory); -} -#endif - -void ResourceBuffer::createPurgeableBuffer() const -{ - ASSERT(m_sharedBuffer); - sharedBuffer()->createPurgeableBuffer(); -} - -PassOwnPtr<PurgeableBuffer> ResourceBuffer::releasePurgeableBuffer() -{ - return m_sharedBuffer->releasePurgeableBuffer(); -} - -#if USE(CF) -RetainPtr<CFDataRef> ResourceBuffer::createCFData() -{ - return m_sharedBuffer->createCFData(); -} -#endif - -#if ENABLE(DISK_IMAGE_CACHE) -bool ResourceBuffer::isUsingDiskImageCache() const -{ - ASSERT(m_sharedBuffer); - return m_sharedBuffer && m_sharedBuffer->isAllowedToBeMemoryMapped(); -} -#endif - -} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceBuffer.h b/Source/WebCore/loader/ResourceBuffer.h deleted file mode 100644 index cd6cb6cfb..000000000 --- a/Source/WebCore/loader/ResourceBuffer.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2012 Apple 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 APPLE COMPUTER, 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 COMPUTER, 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. - */ - -#ifndef ResourceBuffer_h -#define ResourceBuffer_h - -#include "SharedBuffer.h" - -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> - -#if PLATFORM(MAC) -OBJC_CLASS NSData; -#endif - -namespace WebCore { - -class PurgeableBuffer; -class SharedBuffer; - -class ResourceBuffer : public RefCounted<ResourceBuffer> { -public: - - static PassRefPtr<ResourceBuffer> create() { return adoptRef(new ResourceBuffer); } - static PassRefPtr<ResourceBuffer> create(const char* data, unsigned size) { return adoptRef(new ResourceBuffer(data, size)); } - static PassRefPtr<ResourceBuffer> adoptSharedBuffer(PassRefPtr<SharedBuffer> shared) { return shared ? adoptRef(new ResourceBuffer(shared)) : 0; } - - virtual ~ResourceBuffer(); - - virtual const char* data() const; - virtual unsigned size() const; - virtual bool isEmpty() const; - - void append(const char*, unsigned); - void append(SharedBuffer*); -#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) - void append(CFDataRef); -#endif - void clear(); - - unsigned getSomeData(const char*& data, unsigned position = 0) const; - - SharedBuffer* sharedBuffer() const; -#if PLATFORM(MAC) - void tryReplaceSharedBufferContents(SharedBuffer*); -#endif - PassRefPtr<ResourceBuffer> copy() const; - - bool hasPurgeableBuffer() const; - void createPurgeableBuffer() const; - -#if PLATFORM(IOS) - // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to SharedBuffer.{cpp, h} and SharedBufferCF.cpp. - void setShouldUsePurgeableMemory(bool); -#endif - - // Ensure this buffer has no other clients before calling this. - PassOwnPtr<PurgeableBuffer> releasePurgeableBuffer(); - -#if PLATFORM(MAC) - SharedBuffer::NSDataRetainPtrWithoutImplicitConversionOperator createNSData(); -#endif -#if USE(CF) - RetainPtr<CFDataRef> createCFData(); -#endif -#if ENABLE(DISK_IMAGE_CACHE) - bool isUsingDiskImageCache() const; -#endif - -protected: - ResourceBuffer(); - -private: - ResourceBuffer(const char*, unsigned); - ResourceBuffer(PassRefPtr<SharedBuffer>); - - RefPtr<SharedBuffer> m_sharedBuffer; -}; - -} // namespace WebCore - -#endif // ResourceBuffer_h diff --git a/Source/WebCore/loader/ResourceLoadInfo.cpp b/Source/WebCore/loader/ResourceLoadInfo.cpp new file mode 100644 index 000000000..d8e4d1504 --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadInfo.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2015 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "ResourceLoadInfo.h" + +#include "ContentExtensionActions.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +ResourceType toResourceType(CachedResource::Type type) +{ + switch (type) { + case CachedResource::MainResource: + return ResourceType::Document; + case CachedResource::SVGDocumentResource: + return ResourceType::SVGDocument; + case CachedResource::ImageResource: + return ResourceType::Image; + case CachedResource::CSSStyleSheet: +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif + return ResourceType::StyleSheet; + + case CachedResource::Script: + return ResourceType::Script; + + case CachedResource::FontResource: +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif + return ResourceType::Font; + + case CachedResource::MediaResource: + case CachedResource::RawResource: + return ResourceType::Raw; + +#if ENABLE(VIDEO_TRACK) + case CachedResource::TextTrackResource: + return ResourceType::Media; +#endif +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + case CachedResource::LinkSubresource: + ASSERT_NOT_REACHED(); +#endif + }; +} + +uint16_t readResourceType(const String& name) +{ + if (name == "document") + return static_cast<uint16_t>(ResourceType::Document); + if (name == "image") + return static_cast<uint16_t>(ResourceType::Image); + if (name == "style-sheet") + return static_cast<uint16_t>(ResourceType::StyleSheet); + if (name == "script") + return static_cast<uint16_t>(ResourceType::Script); + if (name == "font") + return static_cast<uint16_t>(ResourceType::Font); + if (name == "raw") + return static_cast<uint16_t>(ResourceType::Raw); + if (name == "svg-document") + return static_cast<uint16_t>(ResourceType::SVGDocument); + if (name == "media") + return static_cast<uint16_t>(ResourceType::Media); + if (name == "popup") + return static_cast<uint16_t>(ResourceType::Popup); + return static_cast<uint16_t>(ResourceType::Invalid); +} + +uint16_t readLoadType(const String& name) +{ + if (name == "first-party") + return static_cast<uint16_t>(LoadType::FirstParty); + if (name == "third-party") + return static_cast<uint16_t>(LoadType::ThirdParty); + return static_cast<uint16_t>(LoadType::Invalid); +} + +bool ResourceLoadInfo::isThirdParty() const +{ + Ref<SecurityOrigin> mainDocumentSecurityOrigin = SecurityOrigin::create(mainDocumentURL); + Ref<SecurityOrigin> resourceSecurityOrigin = SecurityOrigin::create(resourceURL); + + return !mainDocumentSecurityOrigin->canAccess(resourceSecurityOrigin.get()); +} + +ResourceFlags ResourceLoadInfo::getResourceFlags() const +{ + ResourceFlags flags = 0; + ASSERT(type != ResourceType::Invalid); + flags |= static_cast<ResourceFlags>(type); + flags |= isThirdParty() ? static_cast<ResourceFlags>(LoadType::ThirdParty) : static_cast<ResourceFlags>(LoadType::FirstParty); + return flags; +} + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoadInfo.h b/Source/WebCore/loader/ResourceLoadInfo.h new file mode 100644 index 000000000..eaf4a6c43 --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadInfo.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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. + */ + +#pragma once + +#include "CachedResource.h" +#include "URL.h" + +namespace WebCore { + +enum class ResourceType : uint16_t { + Invalid = 0x0000, + Document = 0x0001, + Image = 0x0002, + StyleSheet = 0x0004, + Script = 0x0008, + Font = 0x0010, + Raw = 0x0020, + SVGDocument = 0x0040, + Media = 0x0080, + PlugInStream = 0x0100, + Popup = 0x0200, +}; +const uint16_t ResourceTypeMask = 0x03FF; + +enum class LoadType : uint16_t { + Invalid = 0x0000, + FirstParty = 0x0400, + ThirdParty = 0x0800, +}; +const uint16_t LoadTypeMask = 0x0C00; + +typedef uint16_t ResourceFlags; + +// The first 32 bits of a uint64_t action are used for the action location. +// The next 16 bits are used for the flags (ResourceType and LoadType). +// The next bit is used to mark actions that are from a rule with an if-domain condition. +// The next bit is used to mark actions that in the default stylesheet. +// The values -1 and -2 are used for removed and empty values in HashTables. +const uint64_t ActionFlagMask = 0x0000FFFF00000000; +const uint64_t IfDomainFlag = 0x0001000000000000; + +ResourceType toResourceType(CachedResource::Type); +uint16_t readResourceType(const String&); +uint16_t readLoadType(const String&); + +struct ResourceLoadInfo { + URL resourceURL; + URL mainDocumentURL; + ResourceType type; + + bool isThirdParty() const; + ResourceFlags getResourceFlags() const; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoadNotifier.cpp b/Source/WebCore/loader/ResourceLoadNotifier.cpp index 9d6b70290..63443d36e 100644 --- a/Source/WebCore/loader/ResourceLoadNotifier.cpp +++ b/Source/WebCore/loader/ResourceLoadNotifier.cpp @@ -12,7 +12,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. * @@ -39,6 +39,7 @@ #include "Page.h" #include "ProgressTracker.h" #include "ResourceLoader.h" +#include "RuntimeEnabledFeatures.h" #if USE(QUICK_LOOK) #include "QuickLook.h" @@ -61,16 +62,6 @@ void ResourceLoadNotifier::didReceiveAuthenticationChallenge(unsigned long ident m_frame.loader().client().dispatchDidReceiveAuthenticationChallenge(loader, identifier, currentWebChallenge); } -void ResourceLoadNotifier::didCancelAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge) -{ - didCancelAuthenticationChallenge(loader->identifier(), loader->documentLoader(), currentWebChallenge); -} - -void ResourceLoadNotifier::didCancelAuthenticationChallenge(unsigned long identifier, DocumentLoader* loader, const AuthenticationChallenge& currentWebChallenge) -{ - m_frame.loader().client().dispatchDidCancelAuthenticationChallenge(loader, identifier, currentWebChallenge); -} - void ResourceLoadNotifier::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse) { m_frame.loader().applyUserAgent(clientRequest); @@ -108,6 +99,8 @@ void ResourceLoadNotifier::didFailToLoad(ResourceLoader* loader, const ResourceE if (Page* page = m_frame.page()) page->progress().completeProgress(loader->identifier()); + // Notifying the FrameLoaderClient may cause the frame to be destroyed. + Ref<Frame> protect(m_frame); if (!error.isNull()) m_frame.loader().client().dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error); @@ -130,6 +123,8 @@ void ResourceLoadNotifier::dispatchWillSendRequest(DocumentLoader* loader, unsig String oldRequestURL = request.url().string(); m_frame.loader().documentLoader()->didTellClientAboutLoad(request.url()); + // Notifying the FrameLoaderClient may cause the frame to be destroyed. + Ref<Frame> protect(m_frame); m_frame.loader().client().dispatchWillSendRequest(loader, identifier, request, redirectResponse); // If the URL changed, then we want to put that new URL in the "did tell client" set too. @@ -138,24 +133,27 @@ void ResourceLoadNotifier::dispatchWillSendRequest(DocumentLoader* loader, unsig InspectorInstrumentation::willSendRequest(&m_frame, identifier, loader, request, redirectResponse); - // Report WebTiming for all frames. - if (loader && !request.isNull() && request.url() == loader->requestURL()) + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) request.setReportLoadTiming(true); - -#if ENABLE(RESOURCE_TIMING) - request.setReportLoadTiming(true); -#endif + else if (loader && !request.isNull() && request.url() == loader->url()) { + // Report WebTiming for all frames. + request.setReportLoadTiming(true); + } } void ResourceLoadNotifier::dispatchDidReceiveResponse(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r, ResourceLoader* resourceLoader) { - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(&m_frame, identifier, r); + // Notifying the FrameLoaderClient may cause the frame to be destroyed. + Ref<Frame> protect(m_frame); m_frame.loader().client().dispatchDidReceiveResponse(loader, identifier, r); - InspectorInstrumentation::didReceiveResourceResponse(cookie, identifier, loader, r, resourceLoader); + + InspectorInstrumentation::didReceiveResourceResponse(m_frame, identifier, loader, r, resourceLoader); } void ResourceLoadNotifier::dispatchDidReceiveData(DocumentLoader* loader, unsigned long identifier, const char* data, int dataLength, int encodedDataLength) { + // Notifying the FrameLoaderClient may cause the frame to be destroyed. + Ref<Frame> protect(m_frame); m_frame.loader().client().dispatchDidReceiveContentLength(loader, identifier, dataLength); InspectorInstrumentation::didReceiveData(&m_frame, identifier, data, dataLength, encodedDataLength); @@ -163,6 +161,8 @@ void ResourceLoadNotifier::dispatchDidReceiveData(DocumentLoader* loader, unsign void ResourceLoadNotifier::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier, double finishTime) { + // Notifying the FrameLoaderClient may cause the frame to be destroyed. + Ref<Frame> protect(m_frame); m_frame.loader().client().dispatchDidFinishLoading(loader, identifier); InspectorInstrumentation::didFinishLoading(&m_frame, loader, identifier, finishTime); @@ -170,6 +170,8 @@ void ResourceLoadNotifier::dispatchDidFinishLoading(DocumentLoader* loader, unsi void ResourceLoadNotifier::dispatchDidFailLoading(DocumentLoader* loader, unsigned long identifier, const ResourceError& error) { + // Notifying the FrameLoaderClient may cause the frame to be destroyed. + Ref<Frame> protect(m_frame); m_frame.loader().client().dispatchDidFailLoading(loader, identifier, error); InspectorInstrumentation::didFailLoading(&m_frame, loader, identifier, error); diff --git a/Source/WebCore/loader/ResourceLoadNotifier.h b/Source/WebCore/loader/ResourceLoadNotifier.h index d2ce05436..f316ec2e8 100644 --- a/Source/WebCore/loader/ResourceLoadNotifier.h +++ b/Source/WebCore/loader/ResourceLoadNotifier.h @@ -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. * @@ -27,8 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceLoadNotifier_h -#define ResourceLoadNotifier_h +#pragma once #include <wtf/Noncopyable.h> @@ -50,8 +49,6 @@ public: void didReceiveAuthenticationChallenge(ResourceLoader*, const AuthenticationChallenge&); void didReceiveAuthenticationChallenge(unsigned long identifier, DocumentLoader*, const AuthenticationChallenge&); - void didCancelAuthenticationChallenge(ResourceLoader*, const AuthenticationChallenge&); - void didCancelAuthenticationChallenge(unsigned long identifier, DocumentLoader*, const AuthenticationChallenge&); void willSendRequest(ResourceLoader*, ResourceRequest&, const ResourceResponse& redirectResponse); void didReceiveResponse(ResourceLoader*, const ResourceResponse&); @@ -61,7 +58,7 @@ public: void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); - void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&, ResourceLoader* = 0); + void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&, ResourceLoader* = nullptr); void dispatchDidReceiveData(DocumentLoader*, unsigned long identifier, const char* data, int dataLength, int encodedDataLength); void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier, double finishTime); void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&); @@ -73,5 +70,3 @@ private: }; } // namespace WebCore - -#endif // ResourceLoadNotifier_h diff --git a/Source/WebCore/loader/ResourceLoadObserver.cpp b/Source/WebCore/loader/ResourceLoadObserver.cpp new file mode 100644 index 000000000..8d3da3bd6 --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadObserver.cpp @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2016-2017 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "ResourceLoadObserver.h" + +#include "Document.h" +#include "Frame.h" +#include "Logging.h" +#include "MainFrame.h" +#include "NetworkStorageSession.h" +#include "Page.h" +#include "PlatformStrategies.h" +#include "PublicSuffix.h" +#include "ResourceLoadStatistics.h" +#include "ResourceLoadStatisticsStore.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "SharedBuffer.h" +#include "URL.h" +#include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +// One day in seconds. +static auto timestampResolution = 86400; + +ResourceLoadObserver& ResourceLoadObserver::sharedObserver() +{ + static NeverDestroyed<ResourceLoadObserver> resourceLoadObserver; + return resourceLoadObserver; +} + +RefPtr<ResourceLoadStatisticsStore> ResourceLoadObserver::statisticsStore() +{ + ASSERT(m_store); + return m_store; +} + +void ResourceLoadObserver::setStatisticsStore(Ref<ResourceLoadStatisticsStore>&& store) +{ + m_store = WTFMove(store); +} + +static inline bool is3xxRedirect(const ResourceResponse& response) +{ + return response.httpStatusCode() >= 300 && response.httpStatusCode() <= 399; +} + +bool ResourceLoadObserver::shouldLog(Page* page) +{ + // FIXME: Err on the safe side until we have sorted out what to do in worker contexts + if (!page) + return false; + return Settings::resourceLoadStatisticsEnabled() + && !page->usesEphemeralSession() + && m_store; +} + +void ResourceLoadObserver::logFrameNavigation(const Frame& frame, const Frame& topFrame, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse) +{ + ASSERT(frame.document()); + ASSERT(topFrame.document()); + ASSERT(topFrame.page()); + + if (!shouldLog(topFrame.page())) + return; + + bool isRedirect = is3xxRedirect(redirectResponse); + bool isMainFrame = frame.isMainFrame(); + const URL& sourceURL = frame.document()->url(); + const URL& targetURL = newRequest.url(); + const URL& mainFrameURL = topFrame.document()->url(); + + if (!targetURL.isValid() || !mainFrameURL.isValid()) + return; + + auto targetHost = targetURL.host(); + auto mainFrameHost = mainFrameURL.host(); + + if (targetHost.isEmpty() || mainFrameHost.isEmpty() || targetHost == mainFrameHost || targetHost == sourceURL.host()) + return; + + auto targetPrimaryDomain = primaryDomain(targetURL); + auto mainFramePrimaryDomain = primaryDomain(mainFrameURL); + auto sourcePrimaryDomain = primaryDomain(sourceURL); + + if (targetPrimaryDomain == mainFramePrimaryDomain || targetPrimaryDomain == sourcePrimaryDomain) + return; + + auto targetOrigin = SecurityOrigin::create(targetURL); + auto targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain); + + // Always fire if we have previously removed data records for this domain + bool shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0; + + if (isMainFrame) + targetStatistics.topFrameHasBeenNavigatedToBefore = true; + else { + targetStatistics.subframeHasBeenLoadedBefore = true; + + auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL); + auto subframeUnderTopFrameOriginsResult = targetStatistics.subframeUnderTopFrameOrigins.add(mainFramePrimaryDomain); + if (subframeUnderTopFrameOriginsResult.isNewEntry) + shouldFireDataModificationHandler = true; + } + + if (isRedirect) { + auto& redirectingOriginResourceStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain); + + if (m_store->isPrevalentResource(targetPrimaryDomain)) + redirectingOriginResourceStatistics.redirectedToOtherPrevalentResourceOrigins.add(targetPrimaryDomain); + + if (isMainFrame) { + ++targetStatistics.topFrameHasBeenRedirectedTo; + ++redirectingOriginResourceStatistics.topFrameHasBeenRedirectedFrom; + } else { + ++targetStatistics.subframeHasBeenRedirectedTo; + ++redirectingOriginResourceStatistics.subframeHasBeenRedirectedFrom; + redirectingOriginResourceStatistics.subframeUniqueRedirectsTo.add(targetPrimaryDomain); + + ++targetStatistics.subframeSubResourceCount; + } + } else { + if (sourcePrimaryDomain.isNull() || sourcePrimaryDomain.isEmpty() || sourcePrimaryDomain == "nullOrigin") { + if (isMainFrame) + ++targetStatistics.topFrameInitialLoadCount; + else + ++targetStatistics.subframeSubResourceCount; + } else { + auto& sourceOriginResourceStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain); + + if (isMainFrame) { + ++sourceOriginResourceStatistics.topFrameHasBeenNavigatedFrom; + ++targetStatistics.topFrameHasBeenNavigatedTo; + } else { + ++sourceOriginResourceStatistics.subframeHasBeenNavigatedFrom; + ++targetStatistics.subframeHasBeenNavigatedTo; + } + } + } + + m_store->setResourceStatisticsForPrimaryDomain(targetPrimaryDomain, WTFMove(targetStatistics)); + if (shouldFireDataModificationHandler) + m_store->fireDataModificationHandler(); +} + +void ResourceLoadObserver::logSubresourceLoading(const Frame* frame, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse) +{ + ASSERT(frame->page()); + + if (!shouldLog(frame->page())) + return; + + bool isRedirect = is3xxRedirect(redirectResponse); + const URL& sourceURL = redirectResponse.url(); + const URL& targetURL = newRequest.url(); + const URL& mainFrameURL = frame ? frame->mainFrame().document()->url() : URL(); + + auto targetHost = targetURL.host(); + auto mainFrameHost = mainFrameURL.host(); + + if (targetHost.isEmpty() + || mainFrameHost.isEmpty() + || targetHost == mainFrameHost + || (isRedirect && targetHost == sourceURL.host())) + return; + + auto targetPrimaryDomain = primaryDomain(targetURL); + auto mainFramePrimaryDomain = primaryDomain(mainFrameURL); + auto sourcePrimaryDomain = primaryDomain(sourceURL); + + if (targetPrimaryDomain == mainFramePrimaryDomain || (isRedirect && targetPrimaryDomain == sourcePrimaryDomain)) + return; + + auto& targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain); + + // Always fire if we have previously removed data records for this domain + bool shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0; + + auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL); + auto subresourceUnderTopFrameOriginsResult = targetStatistics.subresourceUnderTopFrameOrigins.add(mainFramePrimaryDomain); + if (subresourceUnderTopFrameOriginsResult.isNewEntry) + shouldFireDataModificationHandler = true; + + if (isRedirect) { + auto& redirectingOriginStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain); + + // We just inserted to the store, so we need to reget 'targetStatistics' + auto& updatedTargetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain); + + if (m_store->isPrevalentResource(targetPrimaryDomain)) + redirectingOriginStatistics.redirectedToOtherPrevalentResourceOrigins.add(targetPrimaryDomain); + + ++redirectingOriginStatistics.subresourceHasBeenRedirectedFrom; + ++updatedTargetStatistics.subresourceHasBeenRedirectedTo; + + auto subresourceUniqueRedirectsToResult = redirectingOriginStatistics.subresourceUniqueRedirectsTo.add(targetPrimaryDomain); + if (subresourceUniqueRedirectsToResult.isNewEntry) + shouldFireDataModificationHandler = true; + + ++updatedTargetStatistics.subresourceHasBeenSubresourceCount; + + auto totalVisited = std::max(m_originsVisitedMap.size(), 1U); + + updatedTargetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(updatedTargetStatistics.subresourceHasBeenSubresourceCount) / totalVisited; + } else { + ++targetStatistics.subresourceHasBeenSubresourceCount; + + auto totalVisited = std::max(m_originsVisitedMap.size(), 1U); + + targetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(targetStatistics.subresourceHasBeenSubresourceCount) / totalVisited; + } + + if (shouldFireDataModificationHandler) + m_store->fireDataModificationHandler(); +} + +void ResourceLoadObserver::logWebSocketLoading(const Frame* frame, const URL& targetURL) +{ + // FIXME: Web sockets can run in detached frames. Decide how to count such connections. + // See LayoutTests/http/tests/websocket/construct-in-detached-frame.html + if (!frame) + return; + + if (!shouldLog(frame->page())) + return; + + const URL& mainFrameURL = frame->mainFrame().document()->url(); + + auto targetHost = targetURL.host(); + auto mainFrameHost = mainFrameURL.host(); + + if (targetHost.isEmpty() + || mainFrameHost.isEmpty() + || targetHost == mainFrameHost) + return; + + auto targetPrimaryDomain = primaryDomain(targetURL); + auto mainFramePrimaryDomain = primaryDomain(mainFrameURL); + + if (targetPrimaryDomain == mainFramePrimaryDomain) + return; + + auto& targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain); + + // Always fire if we have previously removed data records for this domain + bool shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0; + + auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL); + auto subresourceUnderTopFrameOriginsResult = targetStatistics.subresourceUnderTopFrameOrigins.add(mainFramePrimaryDomain); + if (subresourceUnderTopFrameOriginsResult.isNewEntry) + shouldFireDataModificationHandler = true; + + ++targetStatistics.subresourceHasBeenSubresourceCount; + + auto totalVisited = std::max(m_originsVisitedMap.size(), 1U); + + targetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(targetStatistics.subresourceHasBeenSubresourceCount) / totalVisited; + + if (shouldFireDataModificationHandler) + m_store->fireDataModificationHandler(); +} + +static double reduceTimeResolutionToOneDay(double seconds) +{ + return std::floor(seconds / timestampResolution) * timestampResolution; +} + +void ResourceLoadObserver::logUserInteractionWithReducedTimeResolution(const Document& document) +{ + ASSERT(document.page()); + + if (!shouldLog(document.page())) + return; + + auto& url = document.url(); + if (url.isBlankURL() || url.isEmpty()) + return; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + double newTimestamp = reduceTimeResolutionToOneDay(WTF::currentTime()); + if (newTimestamp == statistics.mostRecentUserInteraction) + return; + + statistics.hadUserInteraction = true; + statistics.mostRecentUserInteraction = newTimestamp; + m_store->fireDataModificationHandler(); +} + +void ResourceLoadObserver::logUserInteraction(const URL& url) +{ + if (url.isBlankURL() || url.isEmpty()) + return; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + statistics.hadUserInteraction = true; + statistics.mostRecentUserInteraction = WTF::currentTime(); +} + +void ResourceLoadObserver::clearUserInteraction(const URL& url) +{ + if (url.isBlankURL() || url.isEmpty()) + return; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + + statistics.hadUserInteraction = false; + statistics.mostRecentUserInteraction = 0; +} + +bool ResourceLoadObserver::hasHadUserInteraction(const URL& url) +{ + if (url.isBlankURL() || url.isEmpty()) + return false; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + + return m_store->hasHadRecentUserInteraction(statistics); +} + +void ResourceLoadObserver::setPrevalentResource(const URL& url) +{ + if (url.isBlankURL() || url.isEmpty()) + return; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + + statistics.isPrevalentResource = true; +} + +bool ResourceLoadObserver::isPrevalentResource(const URL& url) +{ + if (url.isBlankURL() || url.isEmpty()) + return false; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + + return statistics.isPrevalentResource; +} + +void ResourceLoadObserver::clearPrevalentResource(const URL& url) +{ + if (url.isBlankURL() || url.isEmpty()) + return; + + auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url)); + + statistics.isPrevalentResource = false; +} + +void ResourceLoadObserver::setTimeToLiveUserInteraction(double seconds) +{ + m_store->setTimeToLiveUserInteraction(seconds); +} + +void ResourceLoadObserver::fireDataModificationHandler() +{ + m_store->fireDataModificationHandler(); +} + +String ResourceLoadObserver::primaryDomain(const URL& url) +{ + String primaryDomain; + String host = url.host(); + if (host.isNull() || host.isEmpty()) + primaryDomain = "nullOrigin"; +#if ENABLE(PUBLIC_SUFFIX_LIST) + else { + primaryDomain = topPrivatelyControlledDomain(host); + // We will have an empty string here if there is no TLD. + // Use the host in such case. + if (primaryDomain.isEmpty()) + primaryDomain = host; + } +#else + else + primaryDomain = host; +#endif + + return primaryDomain; +} + +String ResourceLoadObserver::statisticsForOrigin(const String& origin) +{ + return m_store ? m_store->statisticsForOrigin(origin) : emptyString(); +} + +} diff --git a/Source/WebCore/loader/ResourceLoadObserver.h b/Source/WebCore/loader/ResourceLoadObserver.h new file mode 100644 index 000000000..c8cce7b03 --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadObserver.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016-2017 Apple 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 APPLE 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. + */ + +#pragma once + +#include "ResourceLoadStatisticsStore.h" +#include <wtf/HashMap.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Document; +class Frame; +class Page; +class ResourceRequest; +class ResourceResponse; +class URL; + +struct ResourceLoadStatistics; + +class ResourceLoadObserver { + friend class NeverDestroyed<ResourceLoadObserver>; +public: + WEBCORE_EXPORT static ResourceLoadObserver& sharedObserver(); + + void logFrameNavigation(const Frame& frame, const Frame& topFrame, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse); + void logSubresourceLoading(const Frame*, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse); + void logWebSocketLoading(const Frame*, const URL&); + void logUserInteractionWithReducedTimeResolution(const Document&); + + WEBCORE_EXPORT void logUserInteraction(const URL&); + WEBCORE_EXPORT bool hasHadUserInteraction(const URL&); + WEBCORE_EXPORT void clearUserInteraction(const URL&); + + WEBCORE_EXPORT void setPrevalentResource(const URL&); + WEBCORE_EXPORT bool isPrevalentResource(const URL&); + WEBCORE_EXPORT void clearPrevalentResource(const URL&); + + WEBCORE_EXPORT void setTimeToLiveUserInteraction(double seconds); + WEBCORE_EXPORT void setReducedTimestampResolution(double seconds); + + WEBCORE_EXPORT void fireDataModificationHandler(); + + WEBCORE_EXPORT RefPtr<ResourceLoadStatisticsStore> statisticsStore(); + WEBCORE_EXPORT void setStatisticsStore(Ref<ResourceLoadStatisticsStore>&&); + + WEBCORE_EXPORT String statisticsForOrigin(const String&); + +private: + bool shouldLog(Page*); + static String primaryDomain(const URL&); + + RefPtr<ResourceLoadStatisticsStore> m_store; + HashMap<String, size_t> m_originsVisitedMap; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoadScheduler.cpp b/Source/WebCore/loader/ResourceLoadScheduler.cpp deleted file mode 100644 index e8fee1d3f..000000000 --- a/Source/WebCore/loader/ResourceLoadScheduler.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* - Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2001 Dirk Mueller (mueller@kde.org) - Copyright (C) 2002 Waldo Bastian (bastian@kde.org) - Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - Copyright (C) 2010 Google Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "ResourceLoadScheduler.h" - -#include "Document.h" -#include "DocumentLoader.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "InspectorInstrumentation.h" -#include "URL.h" -#include "LoaderStrategy.h" -#include "Logging.h" -#include "NetscapePlugInStreamLoader.h" -#include "PlatformStrategies.h" -#include "ResourceLoader.h" -#include "ResourceRequest.h" -#include "SubresourceLoader.h" -#include <wtf/MainThread.h> -#include <wtf/TemporaryChange.h> -#include <wtf/text/CString.h> - -#if PLATFORM(IOS) -#include <RuntimeApplicationChecksIOS.h> -#endif - -namespace WebCore { - -// Match the parallel connection count used by the networking layer. -static unsigned maxRequestsInFlightPerHost; -#if !PLATFORM(IOS) -static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; -#else -// Limiting this seems to regress performance in some local cases so let's just make it large. -static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000; -#endif - -ResourceLoadScheduler::HostInformation* ResourceLoadScheduler::hostForURL(const URL& url, CreateHostPolicy createHostPolicy) -{ - if (!url.protocolIsInHTTPFamily()) - return m_nonHTTPProtocolHost; - - m_hosts.checkConsistency(); - String hostName = url.host(); - HostInformation* host = m_hosts.get(hostName); - if (!host && createHostPolicy == CreateIfNotFound) { - host = new HostInformation(hostName, maxRequestsInFlightPerHost); - m_hosts.add(hostName, host); - } - return host; -} - -ResourceLoadScheduler* resourceLoadScheduler() -{ - ASSERT(isMainThread()); - static ResourceLoadScheduler* globalScheduler = 0; - - if (!globalScheduler) { - static bool isCallingOutToStrategy = false; - - // If we're re-entering resourceLoadScheduler() while calling out to the LoaderStrategy, - // then the LoaderStrategy is trying to use the default resourceLoadScheduler. - // So we'll create it here and start using it. - if (isCallingOutToStrategy) { - globalScheduler = new ResourceLoadScheduler; - return globalScheduler; - } - - TemporaryChange<bool> recursionGuard(isCallingOutToStrategy, true); - globalScheduler = platformStrategies()->loaderStrategy()->resourceLoadScheduler(); - } - - return globalScheduler; -} - -ResourceLoadScheduler::ResourceLoadScheduler() - : m_nonHTTPProtocolHost(new HostInformation(String(), maxRequestsInFlightForNonHTTPProtocols)) - , m_requestTimer(this, &ResourceLoadScheduler::requestTimerFired) - , m_suspendPendingRequestsCount(0) - , m_isSerialLoadingEnabled(false) -{ - maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost(); -} - -ResourceLoadScheduler::~ResourceLoadScheduler() -{ -} - -PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, CachedResource* resource, const ResourceRequest& request, ResourceLoadPriority priority, const ResourceLoaderOptions& options) -{ - RefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, resource, request, options); - if (loader) - scheduleLoad(loader.get(), priority); -#if PLATFORM(IOS) - // Since we defer loader initialization until scheduling on iOS, the frame - // load delegate that would be called in SubresourceLoader::create() on - // other ports might be called in scheduleLoad() instead. Our contract to - // callers of this method is that a null loader is returned if the load was - // cancelled by a frame load delegate. - if (!loader || loader->reachedTerminalState()) - return nullptr; -#endif - return loader.release(); -} - -PassRefPtr<NetscapePlugInStreamLoader> ResourceLoadScheduler::schedulePluginStreamLoad(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request) -{ - PassRefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request); - if (loader) - scheduleLoad(loader.get(), ResourceLoadPriorityLow); - return loader; -} - -void ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority) -{ - ASSERT(resourceLoader); - ASSERT(priority != ResourceLoadPriorityUnresolved); - - LOG(ResourceLoading, "ResourceLoadScheduler::load resource %p '%s'", resourceLoader, resourceLoader->url().string().latin1().data()); - -#if PLATFORM(IOS) - // If there's a web archive resource for this URL, we don't need to schedule the load since it will never touch the network. - if (!isSuspendingPendingRequests() && resourceLoader->documentLoader()->archiveResourceForURL(resourceLoader->iOSOriginalRequest().url())) { - resourceLoader->startLoading(); - return; - } -#else - if (resourceLoader->documentLoader()->archiveResourceForURL(resourceLoader->request().url())) { - resourceLoader->start(); - return; - } -#endif - -#if PLATFORM(IOS) - HostInformation* host = hostForURL(resourceLoader->iOSOriginalRequest().url(), CreateIfNotFound); -#else - HostInformation* host = hostForURL(resourceLoader->url(), CreateIfNotFound); -#endif - - bool hadRequests = host->hasRequests(); - host->schedule(resourceLoader, priority); - -#if PLATFORM(MAC) || USE(CFNETWORK) - if (!isSuspendingPendingRequests()) { - // Serve all requests at once to keep the pipeline full at the network layer. - // FIXME: Does this code do anything useful, given that we also set maxRequestsInFlightPerHost to effectively unlimited on these platforms? - servePendingRequests(host, ResourceLoadPriorityVeryLow); - return; - } -#endif - -#if PLATFORM(IOS) - if ((priority > ResourceLoadPriorityLow || !resourceLoader->iOSOriginalRequest().url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) && !isSuspendingPendingRequests()) { - // Try to request important resources immediately. - servePendingRequests(host, priority); - return; - } -#else - if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) { - // Try to request important resources immediately. - servePendingRequests(host, priority); - return; - } -#endif - - notifyDidScheduleResourceRequest(resourceLoader); - - // Handle asynchronously so early low priority requests don't - // get scheduled before later high priority ones. - scheduleServePendingRequests(); -} - -void ResourceLoadScheduler::notifyDidScheduleResourceRequest(ResourceLoader* loader) -{ - InspectorInstrumentation::didScheduleResourceRequest(loader->frameLoader() ? loader->frameLoader()->frame().document() : 0, loader->url()); -} - -void ResourceLoadScheduler::remove(ResourceLoader* resourceLoader) -{ - ASSERT(resourceLoader); - - HostInformation* host = hostForURL(resourceLoader->url()); - if (host) - host->remove(resourceLoader); -#if PLATFORM(IOS) - // ResourceLoader::url() doesn't start returning the correct value until the load starts. If we get canceled before that, we need to look for originalRequest url instead. - // FIXME: ResourceLoader::url() should be made to return a sensible value at all times. - if (!resourceLoader->iOSOriginalRequest().isNull()) { - HostInformation* originalHost = hostForURL(resourceLoader->iOSOriginalRequest().url()); - if (originalHost && originalHost != host) - originalHost->remove(resourceLoader); - } -#endif - scheduleServePendingRequests(); -} - -void ResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader* resourceLoader, const URL& redirectURL) -{ - HostInformation* oldHost = hostForURL(resourceLoader->url()); - ASSERT(oldHost); - if (!oldHost) - return; - - HostInformation* newHost = hostForURL(redirectURL, CreateIfNotFound); - - if (oldHost->name() == newHost->name()) - return; - - newHost->addLoadInProgress(resourceLoader); - oldHost->remove(resourceLoader); -} - -void ResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority) -{ - LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests. m_suspendPendingRequestsCount=%d", m_suspendPendingRequestsCount); - if (isSuspendingPendingRequests()) - return; - - m_requestTimer.stop(); - - servePendingRequests(m_nonHTTPProtocolHost, minimumPriority); - - Vector<HostInformation*> hostsToServe; - m_hosts.checkConsistency(); - HostMap::iterator end = m_hosts.end(); - for (HostMap::iterator iter = m_hosts.begin(); iter != end; ++iter) - hostsToServe.append(iter->value); - - int size = hostsToServe.size(); - for (int i = 0; i < size; ++i) { - HostInformation* host = hostsToServe[i]; - if (host->hasRequests()) - servePendingRequests(host, minimumPriority); - else - delete m_hosts.take(host->name()); - } -} - -void ResourceLoadScheduler::servePendingRequests(HostInformation* host, ResourceLoadPriority minimumPriority) -{ - LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests HostInformation.m_name='%s'", host->name().latin1().data()); - - for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) { - HostInformation::RequestQueue& requestsPending = host->requestsPending(ResourceLoadPriority(priority)); - - while (!requestsPending.isEmpty()) { - RefPtr<ResourceLoader> resourceLoader = requestsPending.first(); - - // For named hosts - which are only http(s) hosts - we should always enforce the connection limit. - // For non-named hosts - everything but http(s) - we should only enforce the limit if the document isn't done parsing - // and we don't know all stylesheets yet. - Document* document = resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame().document() : 0; - bool shouldLimitRequests = !host->name().isNull() || (document && (document->parsing() || !document->haveStylesheetsLoaded())); - if (shouldLimitRequests && host->limitRequests(ResourceLoadPriority(priority))) - return; - - requestsPending.removeFirst(); - host->addLoadInProgress(resourceLoader.get()); -#if PLATFORM(IOS) - if (!applicationIsWebProcess()) { - resourceLoader->startLoading(); - return; - } -#endif - resourceLoader->start(); - } - } -} - -void ResourceLoadScheduler::suspendPendingRequests() -{ - ++m_suspendPendingRequestsCount; -} - -void ResourceLoadScheduler::resumePendingRequests() -{ - ASSERT(m_suspendPendingRequestsCount); - --m_suspendPendingRequestsCount; - if (m_suspendPendingRequestsCount) - return; - if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost->hasRequests()) - scheduleServePendingRequests(); -} - -void ResourceLoadScheduler::scheduleServePendingRequests() -{ - LOG(ResourceLoading, "ResourceLoadScheduler::scheduleServePendingRequests, m_requestTimer.isActive()=%u", m_requestTimer.isActive()); - if (!m_requestTimer.isActive()) - m_requestTimer.startOneShot(0); -} - -void ResourceLoadScheduler::requestTimerFired(Timer<ResourceLoadScheduler>&) -{ - LOG(ResourceLoading, "ResourceLoadScheduler::requestTimerFired\n"); - servePendingRequests(); -} - -ResourceLoadScheduler::HostInformation::HostInformation(const String& name, unsigned maxRequestsInFlight) - : m_name(name) - , m_maxRequestsInFlight(maxRequestsInFlight) -{ -} - -ResourceLoadScheduler::HostInformation::~HostInformation() -{ - ASSERT(m_requestsLoading.isEmpty()); - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) - ASSERT(m_requestsPending[p].isEmpty()); -} - -void ResourceLoadScheduler::HostInformation::schedule(ResourceLoader* resourceLoader, ResourceLoadPriority priority) -{ - m_requestsPending[priority].append(resourceLoader); -} - -void ResourceLoadScheduler::HostInformation::addLoadInProgress(ResourceLoader* resourceLoader) -{ - LOG(ResourceLoading, "HostInformation '%s' loading '%s'. Current count %d", m_name.latin1().data(), resourceLoader->url().string().latin1().data(), m_requestsLoading.size()); - m_requestsLoading.add(resourceLoader); -} - -void ResourceLoadScheduler::HostInformation::remove(ResourceLoader* resourceLoader) -{ - if (m_requestsLoading.remove(resourceLoader)) - return; - - for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) { - RequestQueue::iterator end = m_requestsPending[priority].end(); - for (RequestQueue::iterator it = m_requestsPending[priority].begin(); it != end; ++it) { - if (*it == resourceLoader) { - m_requestsPending[priority].remove(it); - return; - } - } - } -} - -bool ResourceLoadScheduler::HostInformation::hasRequests() const -{ - if (!m_requestsLoading.isEmpty()) - return true; - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) { - if (!m_requestsPending[p].isEmpty()) - return true; - } - return false; -} - -bool ResourceLoadScheduler::HostInformation::limitRequests(ResourceLoadPriority priority) const -{ - if (priority == ResourceLoadPriorityVeryLow && !m_requestsLoading.isEmpty()) - return true; - return m_requestsLoading.size() >= (resourceLoadScheduler()->isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight); -} - -} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoadScheduler.h b/Source/WebCore/loader/ResourceLoadScheduler.h deleted file mode 100644 index 200825d41..000000000 --- a/Source/WebCore/loader/ResourceLoadScheduler.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2001 Dirk Mueller <mueller@kde.org> - Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. - Copyright (C) 2010 Google Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - */ - -#ifndef ResourceLoadScheduler_h -#define ResourceLoadScheduler_h - -#include "FrameLoaderTypes.h" -#include "ResourceLoaderOptions.h" -#include "ResourceLoadPriority.h" -#include "Timer.h" -#include <wtf/Deque.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/Noncopyable.h> -#include <wtf/text/StringHash.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class CachedResource; -class Frame; -class URL; -class NetscapePlugInStreamLoader; -class NetscapePlugInStreamLoaderClient; -class ResourceLoader; -class ResourceRequest; -class SubresourceLoader; - -class ResourceLoadScheduler { - WTF_MAKE_NONCOPYABLE(ResourceLoadScheduler); WTF_MAKE_FAST_ALLOCATED; -public: - friend ResourceLoadScheduler* resourceLoadScheduler(); - - virtual PassRefPtr<SubresourceLoader> scheduleSubresourceLoad(Frame*, CachedResource*, const ResourceRequest&, ResourceLoadPriority, const ResourceLoaderOptions&); - virtual PassRefPtr<NetscapePlugInStreamLoader> schedulePluginStreamLoad(Frame*, NetscapePlugInStreamLoaderClient*, const ResourceRequest&); - virtual void remove(ResourceLoader*); - virtual void crossOriginRedirectReceived(ResourceLoader*, const URL& redirectURL); - - virtual void servePendingRequests(ResourceLoadPriority minimumPriority = ResourceLoadPriorityVeryLow); - virtual void suspendPendingRequests(); - virtual void resumePendingRequests(); - - bool isSerialLoadingEnabled() const { return m_isSerialLoadingEnabled; } - virtual void setSerialLoadingEnabled(bool b) { m_isSerialLoadingEnabled = b; } - - class Suspender { - public: - explicit Suspender(ResourceLoadScheduler& scheduler) : m_scheduler(scheduler) { m_scheduler.suspendPendingRequests(); } - ~Suspender() { m_scheduler.resumePendingRequests(); } - private: - ResourceLoadScheduler& m_scheduler; - }; - -protected: - ResourceLoadScheduler(); - virtual ~ResourceLoadScheduler(); - - void notifyDidScheduleResourceRequest(ResourceLoader*); - -private: - void scheduleLoad(ResourceLoader*, ResourceLoadPriority); - void scheduleServePendingRequests(); - void requestTimerFired(Timer<ResourceLoadScheduler>&); - - bool isSuspendingPendingRequests() const { return !!m_suspendPendingRequestsCount; } - - class HostInformation { - WTF_MAKE_NONCOPYABLE(HostInformation); WTF_MAKE_FAST_ALLOCATED; - public: - HostInformation(const String&, unsigned); - ~HostInformation(); - - const String& name() const { return m_name; } - void schedule(ResourceLoader*, ResourceLoadPriority = ResourceLoadPriorityVeryLow); - void addLoadInProgress(ResourceLoader*); - void remove(ResourceLoader*); - bool hasRequests() const; - bool limitRequests(ResourceLoadPriority) const; - - typedef Deque<RefPtr<ResourceLoader>> RequestQueue; - RequestQueue& requestsPending(ResourceLoadPriority priority) { return m_requestsPending[priority]; } - - private: - RequestQueue m_requestsPending[ResourceLoadPriorityHighest + 1]; - typedef HashSet<RefPtr<ResourceLoader>> RequestMap; - RequestMap m_requestsLoading; - const String m_name; - const int m_maxRequestsInFlight; - }; - - enum CreateHostPolicy { - CreateIfNotFound, - FindOnly - }; - - HostInformation* hostForURL(const URL&, CreateHostPolicy = FindOnly); - void servePendingRequests(HostInformation*, ResourceLoadPriority); - - typedef HashMap<String, HostInformation*, StringHash> HostMap; - HostMap m_hosts; - HostInformation* m_nonHTTPProtocolHost; - - Timer<ResourceLoadScheduler> m_requestTimer; - - unsigned m_suspendPendingRequestsCount; - bool m_isSerialLoadingEnabled; -}; - -ResourceLoadScheduler* resourceLoadScheduler(); - -} - -#endif diff --git a/Source/WebCore/loader/ResourceLoadStatistics.cpp b/Source/WebCore/loader/ResourceLoadStatistics.cpp new file mode 100644 index 000000000..5aa181469 --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadStatistics.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2016-2017 Apple 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 APPLE 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 "ResourceLoadStatistics.h" + +#include "KeyedCoding.h" +#include <wtf/text/StringBuilder.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +typedef WTF::HashMap<String, unsigned, StringHash, HashTraits<String>, HashTraits<unsigned>>::KeyValuePairType ResourceLoadStatisticsValue; + +static void encodeHashCountedSet(KeyedEncoder& encoder, const String& label, const HashCountedSet<String>& hashCountedSet) +{ + if (hashCountedSet.isEmpty()) + return; + + encoder.encodeObjects(label, hashCountedSet.begin(), hashCountedSet.end(), [](KeyedEncoder& encoderInner, const ResourceLoadStatisticsValue& origin) { + encoderInner.encodeString("origin", origin.key); + encoderInner.encodeUInt32("count", origin.value); + }); +} + +void ResourceLoadStatistics::encode(KeyedEncoder& encoder) const +{ + encoder.encodeString("PrevalentResourceOrigin", highLevelDomain); + + // User interaction + encoder.encodeBool("hadUserInteraction", hadUserInteraction); + encoder.encodeDouble("mostRecentUserInteraction", mostRecentUserInteraction); + encoder.encodeBool("grandfathered", grandfathered); + + // Top frame stats + encoder.encodeBool("topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore); + encoder.encodeUInt32("topFrameHasBeenRedirectedTo", topFrameHasBeenRedirectedTo); + encoder.encodeUInt32("topFrameHasBeenRedirectedFrom", topFrameHasBeenRedirectedFrom); + encoder.encodeUInt32("topFrameInitialLoadCount", topFrameInitialLoadCount); + encoder.encodeUInt32("topFrameHasBeenNavigatedTo", topFrameHasBeenNavigatedTo); + encoder.encodeUInt32("topFrameHasBeenNavigatedFrom", topFrameHasBeenNavigatedFrom); + + // Subframe stats + encoder.encodeBool("subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore); + encoder.encodeUInt32("subframeHasBeenRedirectedTo", subframeHasBeenRedirectedTo); + encoder.encodeUInt32("subframeHasBeenRedirectedFrom", subframeHasBeenRedirectedFrom); + encoder.encodeUInt32("subframeSubResourceCount", subframeSubResourceCount); + encodeHashCountedSet(encoder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins); + encodeHashCountedSet(encoder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo); + encoder.encodeUInt32("subframeHasBeenNavigatedTo", subframeHasBeenNavigatedTo); + encoder.encodeUInt32("subframeHasBeenNavigatedFrom", subframeHasBeenNavigatedFrom); + + // Subresource stats + encoder.encodeUInt32("subresourceHasBeenRedirectedFrom", subresourceHasBeenRedirectedFrom); + encoder.encodeUInt32("subresourceHasBeenRedirectedTo", subresourceHasBeenRedirectedTo); + encoder.encodeUInt32("subresourceHasBeenSubresourceCount", subresourceHasBeenSubresourceCount); + encoder.encodeDouble("subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited", subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited); + encodeHashCountedSet(encoder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins); + encodeHashCountedSet(encoder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo); + + // Prevalent Resource + encodeHashCountedSet(encoder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins); + encoder.encodeBool("isPrevalentResource", isPrevalentResource); + encoder.encodeUInt32("dataRecordsRemoved", dataRecordsRemoved); +} + +static void decodeHashCountedSet(KeyedDecoder& decoder, const String& label, HashCountedSet<String>& hashCountedSet) +{ + Vector<String> ignore; + decoder.decodeObjects(label, ignore, [&hashCountedSet](KeyedDecoder& decoderInner, String& origin) { + if (!decoderInner.decodeString("origin", origin)) + return false; + + unsigned count; + if (!decoderInner.decodeUInt32("count", count)) + return false; + + hashCountedSet.add(origin, count); + return true; + }); +} + +bool ResourceLoadStatistics::decode(KeyedDecoder& decoder, unsigned version) +{ + if (!decoder.decodeString("PrevalentResourceOrigin", highLevelDomain)) + return false; + + // User interaction + if (!decoder.decodeBool("hadUserInteraction", hadUserInteraction)) + return false; + + // Top frame stats + if (!decoder.decodeBool("topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore)) + return false; + + if (!decoder.decodeUInt32("topFrameHasBeenRedirectedTo", topFrameHasBeenRedirectedTo)) + return false; + + if (!decoder.decodeUInt32("topFrameHasBeenRedirectedFrom", topFrameHasBeenRedirectedFrom)) + return false; + + if (!decoder.decodeUInt32("topFrameInitialLoadCount", topFrameInitialLoadCount)) + return false; + + if (!decoder.decodeUInt32("topFrameHasBeenNavigatedTo", topFrameHasBeenNavigatedTo)) + return false; + + if (!decoder.decodeUInt32("topFrameHasBeenNavigatedFrom", topFrameHasBeenNavigatedFrom)) + return false; + + // Subframe stats + if (!decoder.decodeBool("subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore)) + return false; + + if (!decoder.decodeUInt32("subframeHasBeenRedirectedTo", subframeHasBeenRedirectedTo)) + return false; + + if (!decoder.decodeUInt32("subframeHasBeenRedirectedFrom", subframeHasBeenRedirectedFrom)) + return false; + + if (!decoder.decodeUInt32("subframeSubResourceCount", subframeSubResourceCount)) + return false; + + decodeHashCountedSet(decoder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins); + decodeHashCountedSet(decoder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo); + + if (!decoder.decodeUInt32("subframeHasBeenNavigatedTo", subframeHasBeenNavigatedTo)) + return false; + + if (!decoder.decodeUInt32("subframeHasBeenNavigatedFrom", subframeHasBeenNavigatedFrom)) + return false; + + // Subresource stats + if (!decoder.decodeUInt32("subresourceHasBeenRedirectedFrom", subresourceHasBeenRedirectedFrom)) + return false; + + if (!decoder.decodeUInt32("subresourceHasBeenRedirectedTo", subresourceHasBeenRedirectedTo)) + return false; + + if (!decoder.decodeUInt32("subresourceHasBeenSubresourceCount", subresourceHasBeenSubresourceCount)) + return false; + + if (!decoder.decodeDouble("subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited", subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited)) + return false; + + decodeHashCountedSet(decoder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins); + decodeHashCountedSet(decoder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo); + + // Prevalent Resource + decodeHashCountedSet(decoder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins); + + if (!decoder.decodeBool("isPrevalentResource", isPrevalentResource)) + return false; + + if (version < 2) + return true; + + if (!decoder.decodeUInt32("dataRecordsRemoved", dataRecordsRemoved)) + return false; + + if (version < 3) + return true; + + if (!decoder.decodeDouble("mostRecentUserInteraction", mostRecentUserInteraction)) + return false; + + if (!decoder.decodeBool("grandfathered", grandfathered)) + return false; + + return true; +} + +static void appendBoolean(StringBuilder& builder, const String& label, bool flag) +{ + builder.appendLiteral(" "); + builder.append(label); + builder.appendLiteral(": "); + builder.append(flag ? "Yes" : "No"); +} + +static void appendHashCountedSet(StringBuilder& builder, const String& label, const HashCountedSet<String>& hashCountedSet) +{ + if (hashCountedSet.isEmpty()) + return; + + builder.appendLiteral(" "); + builder.append(label); + builder.appendLiteral(":\n"); + + for (auto& entry : hashCountedSet) { + builder.appendLiteral(" "); + builder.append(entry.key); + builder.appendLiteral(": "); + builder.appendNumber(entry.value); + builder.append('\n'); + } +} + +String ResourceLoadStatistics::toString() const +{ + StringBuilder builder; + + // User interaction + appendBoolean(builder, "hadUserInteraction", hadUserInteraction); + builder.append('\n'); + builder.appendLiteral(" mostRecentUserInteraction: "); + builder.appendNumber(mostRecentUserInteraction); + builder.append('\n'); + appendBoolean(builder, " grandfathered", grandfathered); + builder.append('\n'); + + // Top frame stats + appendBoolean(builder, "topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore); + builder.append('\n'); + builder.appendLiteral(" topFrameHasBeenRedirectedTo: "); + builder.appendNumber(topFrameHasBeenRedirectedTo); + builder.append('\n'); + builder.appendLiteral(" topFrameHasBeenRedirectedFrom: "); + builder.appendNumber(topFrameHasBeenRedirectedFrom); + builder.append('\n'); + builder.appendLiteral(" topFrameInitialLoadCount: "); + builder.appendNumber(topFrameInitialLoadCount); + builder.append('\n'); + builder.appendLiteral(" topFrameHasBeenNavigatedTo: "); + builder.appendNumber(topFrameHasBeenNavigatedTo); + builder.append('\n'); + builder.appendLiteral(" topFrameHasBeenNavigatedFrom: "); + builder.appendNumber(topFrameHasBeenNavigatedFrom); + builder.append('\n'); + + // Subframe stats + appendBoolean(builder, "subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore); + builder.append('\n'); + builder.appendLiteral(" subframeHasBeenRedirectedTo: "); + builder.appendNumber(subframeHasBeenRedirectedTo); + builder.append('\n'); + builder.appendLiteral(" subframeHasBeenRedirectedFrom: "); + builder.appendNumber(subframeHasBeenRedirectedFrom); + builder.append('\n'); + builder.appendLiteral(" subframeSubResourceCount: "); + builder.appendNumber(subframeSubResourceCount); + builder.append('\n'); + appendHashCountedSet(builder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins); + appendHashCountedSet(builder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo); + builder.appendLiteral(" subframeHasBeenNavigatedTo: "); + builder.appendNumber(subframeHasBeenNavigatedTo); + builder.append('\n'); + builder.appendLiteral(" subframeHasBeenNavigatedFrom: "); + builder.appendNumber(subframeHasBeenNavigatedFrom); + builder.append('\n'); + + // Subresource stats + builder.appendLiteral(" subresourceHasBeenRedirectedFrom: "); + builder.appendNumber(subresourceHasBeenRedirectedFrom); + builder.append('\n'); + builder.appendLiteral(" subresourceHasBeenRedirectedTo: "); + builder.appendNumber(subresourceHasBeenRedirectedTo); + builder.append('\n'); + builder.appendLiteral(" subresourceHasBeenSubresourceCount: "); + builder.appendNumber(subresourceHasBeenSubresourceCount); + builder.append('\n'); + builder.appendLiteral(" subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited: "); + builder.appendNumber(subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited); + builder.append('\n'); + appendHashCountedSet(builder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins); + appendHashCountedSet(builder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo); + + // Prevalent Resource + appendHashCountedSet(builder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins); + appendBoolean(builder, "isPrevalentResource", isPrevalentResource); + builder.appendLiteral(" dataRecordsRemoved: "); + builder.appendNumber(dataRecordsRemoved); + builder.append('\n'); + + builder.append('\n'); + + return builder.toString(); +} + +template <typename T> +static void mergeHashCountedSet(HashCountedSet<T>& to, const HashCountedSet<T>& from) +{ + for (auto& entry : from) + to.add(entry.key, entry.value); +} + +void ResourceLoadStatistics::merge(const ResourceLoadStatistics& other) +{ + ASSERT(other.highLevelDomain == highLevelDomain); + + if (!other.hadUserInteraction) { + // If user interaction has been reset do so here too. + // Else, do nothing. + if (!other.mostRecentUserInteraction) { + hadUserInteraction = false; + mostRecentUserInteraction = 0; + } + } else { + hadUserInteraction = true; + if (mostRecentUserInteraction < other.mostRecentUserInteraction) + mostRecentUserInteraction = other.mostRecentUserInteraction; + } + grandfathered |= other.grandfathered; + + // Top frame stats + topFrameHasBeenRedirectedTo += other.topFrameHasBeenRedirectedTo; + topFrameHasBeenRedirectedFrom += other.topFrameHasBeenRedirectedFrom; + topFrameInitialLoadCount += other.topFrameInitialLoadCount; + topFrameHasBeenNavigatedTo += other.topFrameHasBeenNavigatedTo; + topFrameHasBeenNavigatedFrom += other.topFrameHasBeenNavigatedFrom; + topFrameHasBeenNavigatedToBefore |= other.topFrameHasBeenNavigatedToBefore; + + // Subframe stats + mergeHashCountedSet(subframeUnderTopFrameOrigins, other.subframeUnderTopFrameOrigins); + subframeHasBeenRedirectedTo += other.subframeHasBeenRedirectedTo; + subframeHasBeenRedirectedFrom += other.subframeHasBeenRedirectedFrom; + mergeHashCountedSet(subframeUniqueRedirectsTo, other.subframeUniqueRedirectsTo); + subframeSubResourceCount += other.subframeSubResourceCount; + subframeHasBeenNavigatedTo += other.subframeHasBeenNavigatedTo; + subframeHasBeenNavigatedFrom += other.subframeHasBeenNavigatedFrom; + subframeHasBeenLoadedBefore |= other.subframeHasBeenLoadedBefore; + + // Subresource stats + mergeHashCountedSet(subresourceUnderTopFrameOrigins, other.subresourceUnderTopFrameOrigins); + subresourceHasBeenSubresourceCount += other.subresourceHasBeenSubresourceCount; + subresourceHasBeenRedirectedFrom += other.subresourceHasBeenRedirectedFrom; + subresourceHasBeenRedirectedTo += other.subresourceHasBeenRedirectedTo; + mergeHashCountedSet(subresourceUniqueRedirectsTo, other.subresourceUniqueRedirectsTo); + + // Prevalent resource stats + mergeHashCountedSet(redirectedToOtherPrevalentResourceOrigins, other.redirectedToOtherPrevalentResourceOrigins); + isPrevalentResource |= other.isPrevalentResource; + dataRecordsRemoved += other.dataRecordsRemoved; +} + +} diff --git a/Source/WebCore/loader/ResourceLoadStatistics.h b/Source/WebCore/loader/ResourceLoadStatistics.h new file mode 100644 index 000000000..1ec25e2ec --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadStatistics.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016-2017 Apple 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 APPLE 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. + */ + +#pragma once + +#include <wtf/HashCountedSet.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class KeyedDecoder; +class KeyedEncoder; + +struct ResourceLoadStatistics { + ResourceLoadStatistics(const String& primaryDomain) + : highLevelDomain(primaryDomain) + { + } + + ResourceLoadStatistics() = default; + + void encode(KeyedEncoder&) const; + bool decode(KeyedDecoder&, unsigned version); + + String toString() const; + + void merge(const ResourceLoadStatistics&); + + String highLevelDomain; + + // User interaction + bool hadUserInteraction { false }; + // Timestamp. Default value is negative, 0 means it was reset. + double mostRecentUserInteraction { -1 }; + bool grandfathered { false }; + + // Top frame stats + unsigned topFrameHasBeenRedirectedTo { 0 }; + unsigned topFrameHasBeenRedirectedFrom { 0 }; + unsigned topFrameInitialLoadCount { 0 }; + unsigned topFrameHasBeenNavigatedTo { 0 }; + unsigned topFrameHasBeenNavigatedFrom { 0 }; + bool topFrameHasBeenNavigatedToBefore { false }; + + // Subframe stats + HashCountedSet<String> subframeUnderTopFrameOrigins; + unsigned subframeHasBeenRedirectedTo { 0 }; + unsigned subframeHasBeenRedirectedFrom { 0 }; + HashCountedSet<String> subframeUniqueRedirectsTo; + unsigned subframeSubResourceCount { 0 }; + unsigned subframeHasBeenNavigatedTo { 0 }; + unsigned subframeHasBeenNavigatedFrom { 0 }; + bool subframeHasBeenLoadedBefore { false }; + + // Subresource stats + HashCountedSet<String> subresourceUnderTopFrameOrigins; + unsigned subresourceHasBeenSubresourceCount { 0 }; + double subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited { 0.0 }; + unsigned subresourceHasBeenRedirectedFrom { 0 }; + unsigned subresourceHasBeenRedirectedTo { 0 }; + HashCountedSet<String> subresourceUniqueRedirectsTo; + + // Prevalent resource stats + HashCountedSet<String> redirectedToOtherPrevalentResourceOrigins; + bool isPrevalentResource { false }; + unsigned dataRecordsRemoved { 0 }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoadStatisticsStore.cpp b/Source/WebCore/loader/ResourceLoadStatisticsStore.cpp new file mode 100644 index 000000000..16f45676c --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadStatisticsStore.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016-2017 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "ResourceLoadStatisticsStore.h" + +#include "KeyedCoding.h" +#include "Logging.h" +#include "NetworkStorageSession.h" +#include "PlatformStrategies.h" +#include "ResourceLoadStatistics.h" +#include "SharedBuffer.h" +#include "URL.h" +#include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +static const auto statisticsModelVersion = 3; +// 30 days in seconds +static auto timeToLiveUserInteraction = 2592000; + +Ref<ResourceLoadStatisticsStore> ResourceLoadStatisticsStore::create() +{ + return adoptRef(*new ResourceLoadStatisticsStore()); +} + +bool ResourceLoadStatisticsStore::isPrevalentResource(const String& primaryDomain) const +{ + auto mapEntry = m_resourceStatisticsMap.find(primaryDomain); + if (mapEntry == m_resourceStatisticsMap.end()) + return false; + + return mapEntry->value.isPrevalentResource; +} + +ResourceLoadStatistics& ResourceLoadStatisticsStore::ensureResourceStatisticsForPrimaryDomain(const String& primaryDomain) +{ + auto addResult = m_resourceStatisticsMap.ensure(primaryDomain, [&primaryDomain] { + return ResourceLoadStatistics(primaryDomain); + }); + + return addResult.iterator->value; +} + +void ResourceLoadStatisticsStore::setResourceStatisticsForPrimaryDomain(const String& primaryDomain, ResourceLoadStatistics&& statistics) +{ + m_resourceStatisticsMap.set(primaryDomain, WTFMove(statistics)); +} + +typedef HashMap<String, ResourceLoadStatistics>::KeyValuePairType StatisticsValue; + +std::unique_ptr<KeyedEncoder> ResourceLoadStatisticsStore::createEncoderFromData() +{ + auto encoder = KeyedEncoder::encoder(); + + encoder->encodeUInt32("version", statisticsModelVersion); + encoder->encodeObjects("browsingStatistics", m_resourceStatisticsMap.begin(), m_resourceStatisticsMap.end(), [this](KeyedEncoder& encoderInner, const StatisticsValue& origin) { + origin.value.encode(encoderInner); + }); + + return encoder; +} + +void ResourceLoadStatisticsStore::readDataFromDecoder(KeyedDecoder& decoder) +{ + if (m_resourceStatisticsMap.size()) + return; + + unsigned version; + if (!decoder.decodeUInt32("version", version)) + version = 1; + Vector<ResourceLoadStatistics> loadedStatistics; + bool succeeded = decoder.decodeObjects("browsingStatistics", loadedStatistics, [this, version](KeyedDecoder& decoderInner, ResourceLoadStatistics& statistics) { + return statistics.decode(decoderInner, version); + }); + + if (!succeeded) + return; + + for (auto& statistics : loadedStatistics) + m_resourceStatisticsMap.set(statistics.highLevelDomain, statistics); +} + +String ResourceLoadStatisticsStore::statisticsForOrigin(const String& origin) +{ + auto iter = m_resourceStatisticsMap.find(origin); + if (iter == m_resourceStatisticsMap.end()) + return emptyString(); + + return "Statistics for " + origin + ":\n" + iter->value.toString(); +} + +Vector<ResourceLoadStatistics> ResourceLoadStatisticsStore::takeStatistics() +{ + Vector<ResourceLoadStatistics> statistics; + statistics.reserveInitialCapacity(m_resourceStatisticsMap.size()); + for (auto& statistic : m_resourceStatisticsMap.values()) + statistics.uncheckedAppend(WTFMove(statistic)); + + m_resourceStatisticsMap.clear(); + + return statistics; +} + +void ResourceLoadStatisticsStore::mergeStatistics(const Vector<ResourceLoadStatistics>& statistics) +{ + for (auto& statistic : statistics) { + auto result = m_resourceStatisticsMap.ensure(statistic.highLevelDomain, [&statistic] { + return ResourceLoadStatistics(statistic.highLevelDomain); + }); + + result.iterator->value.merge(statistic); + } +} + +void ResourceLoadStatisticsStore::setNotificationCallback(std::function<void()> handler) +{ + m_dataAddedHandler = WTFMove(handler); +} + +void ResourceLoadStatisticsStore::fireDataModificationHandler() +{ + if (m_dataAddedHandler) + m_dataAddedHandler(); +} + +void ResourceLoadStatisticsStore::setTimeToLiveUserInteraction(double seconds) +{ + if (seconds >= 0) + timeToLiveUserInteraction = seconds; +} + +void ResourceLoadStatisticsStore::processStatistics(std::function<void(ResourceLoadStatistics&)>&& processFunction) +{ + for (auto& resourceStatistic : m_resourceStatisticsMap.values()) + processFunction(resourceStatistic); +} + +bool ResourceLoadStatisticsStore::hasHadRecentUserInteraction(ResourceLoadStatistics& resourceStatistic) +{ + if (!resourceStatistic.hadUserInteraction) + return false; + + if (currentTime() > resourceStatistic.mostRecentUserInteraction + timeToLiveUserInteraction) { + // Drop privacy sensitive data because we no longer need it. + // Set timestamp to 0.0 so that statistics merge will know + // it has been reset as opposed to its default -1. + resourceStatistic.mostRecentUserInteraction = 0; + resourceStatistic.hadUserInteraction = false; + return false; + } + + return true; +} + +Vector<String> ResourceLoadStatisticsStore::prevalentResourceDomainsWithoutUserInteraction() +{ + Vector<String> prevalentResources; + for (auto& resourceStatistic : m_resourceStatisticsMap.values()) { + if (resourceStatistic.isPrevalentResource && !hasHadRecentUserInteraction(resourceStatistic)) + prevalentResources.append(resourceStatistic.highLevelDomain); + } + return prevalentResources; +} + +void ResourceLoadStatisticsStore::updateStatisticsForRemovedDataRecords(const Vector<String>& prevalentResourceDomains) +{ + for (auto& prevalentResourceDomain : prevalentResourceDomains) { + ResourceLoadStatistics& statisic = ensureResourceStatisticsForPrimaryDomain(prevalentResourceDomain); + ++statisic.dataRecordsRemoved; + } +} +} diff --git a/Source/WebCore/loader/ResourceLoadStatisticsStore.h b/Source/WebCore/loader/ResourceLoadStatisticsStore.h new file mode 100644 index 000000000..fcebdf567 --- /dev/null +++ b/Source/WebCore/loader/ResourceLoadStatisticsStore.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016-2017 Apple 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 APPLE 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. + */ + +#pragma once + +#include "ResourceLoadStatistics.h" + +namespace WebCore { + +class KeyedDecoder; +class KeyedEncoder; +class URL; + +struct ResourceLoadStatistics; + +class ResourceLoadStatisticsStore : public RefCounted<ResourceLoadStatisticsStore> { +public: + WEBCORE_EXPORT static Ref<ResourceLoadStatisticsStore> create(); + + WEBCORE_EXPORT std::unique_ptr<KeyedEncoder> createEncoderFromData(); + WEBCORE_EXPORT void readDataFromDecoder(KeyedDecoder&); + + WEBCORE_EXPORT String statisticsForOrigin(const String&); + + bool isEmpty() const { return m_resourceStatisticsMap.isEmpty(); } + size_t size() const { return m_resourceStatisticsMap.size(); } + void clear() { m_resourceStatisticsMap.clear(); } + + ResourceLoadStatistics& ensureResourceStatisticsForPrimaryDomain(const String&); + void setResourceStatisticsForPrimaryDomain(const String&, ResourceLoadStatistics&&); + + bool isPrevalentResource(const String&) const; + + WEBCORE_EXPORT void mergeStatistics(const Vector<ResourceLoadStatistics>&); + WEBCORE_EXPORT Vector<ResourceLoadStatistics> takeStatistics(); + + WEBCORE_EXPORT void setNotificationCallback(std::function<void()> handler); + + void fireDataModificationHandler(); + void setTimeToLiveUserInteraction(double seconds); + + WEBCORE_EXPORT void processStatistics(std::function<void(ResourceLoadStatistics&)>&&); + + WEBCORE_EXPORT bool hasHadRecentUserInteraction(ResourceLoadStatistics&); + WEBCORE_EXPORT Vector<String> prevalentResourceDomainsWithoutUserInteraction(); + WEBCORE_EXPORT void updateStatisticsForRemovedDataRecords(const Vector<String>& prevalentResourceDomains); +private: + ResourceLoadStatisticsStore() = default; + + HashMap<String, ResourceLoadStatistics> m_resourceStatisticsMap; + std::function<void()> m_dataAddedHandler; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoader.cpp b/Source/WebCore/loader/ResourceLoader.cpp index ebef76f47..0fbb0c38e 100644 --- a/Source/WebCore/loader/ResourceLoader.cpp +++ b/Source/WebCore/loader/ResourceLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006-2007, 2010-2011, 2016 Apple Inc. All rights reserved. * (C) 2007 Graham Dennis (graham.dennis@gmail.com) * * 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,36 +31,41 @@ #include "ResourceLoader.h" #include "ApplicationCacheHost.h" -#include "AsyncFileStream.h" #include "AuthenticationChallenge.h" +#include "DataURLDecoder.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "InspectorInstrumentation.h" #include "LoaderStrategy.h" +#include "MainFrame.h" #include "Page.h" #include "PlatformStrategies.h" #include "ProgressTracker.h" -#include "ResourceBuffer.h" #include "ResourceError.h" #include "ResourceHandle.h" -#include "ResourceLoadScheduler.h" #include "SecurityOrigin.h" -#include "Settings.h" #include "SharedBuffer.h" #include <wtf/Ref.h> +#if ENABLE(CONTENT_EXTENSIONS) +#include "UserContentController.h" +#endif + +#if USE(QUICK_LOOK) +#include "PreviewConverter.h" +#include "QuickLook.h" +#endif + namespace WebCore { -ResourceLoader::ResourceLoader(Frame* frame, ResourceLoaderOptions options) - : m_frame(frame) - , m_documentLoader(frame->loader().activeDocumentLoader()) - , m_identifier(0) - , m_reachedTerminalState(false) - , m_notifiedLoadComplete(false) - , m_cancellationStatus(NotCancelled) - , m_defersLoading(frame->page()->defersLoading()) +ResourceLoader::ResourceLoader(Frame& frame, ResourceLoaderOptions options) + : m_frame(&frame) + , m_documentLoader(frame.loader().activeDocumentLoader()) + , m_defersLoading(options.defersLoadingPolicy == DefersLoadingPolicy::AllowDefersLoading && frame.page()->defersLoading()) , m_options(options) { } @@ -70,6 +75,17 @@ ResourceLoader::~ResourceLoader() ASSERT(m_reachedTerminalState); } +void ResourceLoader::finishNetworkLoad() +{ + platformStrategies()->loaderStrategy()->remove(this); + + if (m_handle) { + ASSERT(m_handle->client() == this); + m_handle->clearClient(); + m_handle = nullptr; + } +} + void ResourceLoader::releaseResources() { ASSERT(!m_reachedTerminalState); @@ -78,27 +94,20 @@ void ResourceLoader::releaseResources() // deallocated and release the last reference to this object. // We need to retain to avoid accessing the object after it // has been deallocated and also to avoid reentering this method. - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); - m_frame = 0; - m_documentLoader = 0; + m_frame = nullptr; + m_documentLoader = nullptr; // We need to set reachedTerminalState to true before we release // the resources to prevent a double dealloc of WebView <rdar://problem/4372628> m_reachedTerminalState = true; - platformStrategies()->loaderStrategy()->resourceLoadScheduler()->remove(this); - m_identifier = 0; + finishNetworkLoad(); - if (m_handle) { - // Clear out the ResourceHandle's client so that it doesn't try to call - // us back after we release it, unless it has been replaced by someone else. - if (m_handle->client() == this) - m_handle->setClient(0); - m_handle = 0; - } + m_identifier = 0; - m_resourceData = 0; + m_resourceData = nullptr; m_deferredRequest = ResourceRequest(); } @@ -111,6 +120,8 @@ bool ResourceLoader::init(const ResourceRequest& r) ResourceRequest clientRequest(r); + m_loadTiming.markStartTimeAndFetchStart(); + #if PLATFORM(IOS) // If the documentLoader was detached while this ResourceLoader was waiting its turn // in ResourceLoadScheduler queue, don't continue. @@ -120,8 +131,9 @@ bool ResourceLoader::init(const ResourceRequest& r) } #endif - m_defersLoading = m_frame->page()->defersLoading(); - if (m_options.securityCheck == DoSecurityCheck && !m_frame->document()->securityOrigin()->canDisplay(clientRequest.url())) { + m_defersLoading = m_options.defersLoadingPolicy == DefersLoadingPolicy::AllowDefersLoading && m_frame->page()->defersLoading(); + + if (m_options.securityCheck == DoSecurityCheck && !m_frame->document()->securityOrigin().canDisplay(clientRequest.url())) { FrameLoader::reportLocalLoadFailed(m_frame.get(), clientRequest.url().string()); releaseResources(); return false; @@ -137,7 +149,7 @@ bool ResourceLoader::init(const ResourceRequest& r) clientRequest.setFirstPartyForCookies(document->firstPartyForCookies()); } - willSendRequest(clientRequest, ResourceResponse()); + willSendRequestInternal(clientRequest, ResourceResponse()); #if PLATFORM(IOS) // If this ResourceLoader was stopped as a result of willSendRequest, bail out. @@ -154,18 +166,37 @@ bool ResourceLoader::init(const ResourceRequest& r) return true; } +void ResourceLoader::deliverResponseAndData(const ResourceResponse& response, RefPtr<SharedBuffer>&& buffer) +{ + Ref<ResourceLoader> protectedThis(*this); + + didReceiveResponse(response); + if (reachedTerminalState()) + return; + + if (buffer) { + unsigned size = buffer->size(); + didReceiveBuffer(buffer.releaseNonNull(), size, DataPayloadWholeResource); + if (reachedTerminalState()) + return; + } + + didFinishLoading(0); +} + void ResourceLoader::start() { ASSERT(!m_handle); ASSERT(!m_request.isNull()); ASSERT(m_deferredRequest.isNull()); + ASSERT(frameLoader()); #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) - if (m_documentLoader->scheduleArchiveLoad(this, m_request)) + if (m_documentLoader->scheduleArchiveLoad(*this, m_request)) return; #endif - if (m_documentLoader->applicationCacheHost()->maybeLoadResource(this, m_request, m_request.url())) + if (m_documentLoader->applicationCacheHost().maybeLoadResource(*this, m_request, m_request.url())) return; if (m_defersLoading) { @@ -173,12 +204,22 @@ void ResourceLoader::start() return; } - if (!m_reachedTerminalState) - m_handle = ResourceHandle::create(m_frame->loader().networkingContext(), m_request, this, m_defersLoading, m_options.sniffContent == SniffContent); + if (m_reachedTerminalState) + return; + + if (m_request.url().protocolIsData()) { + loadDataURL(); + return; + } + + m_handle = ResourceHandle::create(frameLoader()->networkingContext(), m_request, this, m_defersLoading, m_options.sniffContent == SniffContent); } void ResourceLoader::setDefersLoading(bool defers) { + if (m_options.defersLoadingPolicy == DefersLoadingPolicy::DisallowDefersLoading) + return; + m_defersLoading = defers; if (m_handle) m_handle->setDefersLoading(defers); @@ -187,43 +228,88 @@ void ResourceLoader::setDefersLoading(bool defers) m_deferredRequest = ResourceRequest(); start(); } + + platformStrategies()->loaderStrategy()->setDefersLoading(this, defers); } FrameLoader* ResourceLoader::frameLoader() const { if (!m_frame) - return 0; + return nullptr; return &m_frame->loader(); } +void ResourceLoader::loadDataURL() +{ + auto url = m_request.url(); + ASSERT(url.protocolIsData()); + + RefPtr<ResourceLoader> protectedThis(this); + DataURLDecoder::ScheduleContext scheduleContext; +#if HAVE(RUNLOOP_TIMER) + if (auto* scheduledPairs = m_frame->page()->scheduledRunLoopPairs()) + scheduleContext.scheduledPairs = *scheduledPairs; +#endif + DataURLDecoder::decode(url, scheduleContext, [protectedThis, url](auto decodeResult) { + if (protectedThis->reachedTerminalState()) + return; + if (!decodeResult) { + protectedThis->didFail(ResourceError(errorDomainWebKitInternal, 0, url, "Data URL decoding failed")); + return; + } + if (protectedThis->wasCancelled()) + return; + auto& result = decodeResult.value(); + auto dataSize = result.data ? result.data->size() : 0; + + ResourceResponse dataResponse { url, result.mimeType, dataSize, result.charset }; + dataResponse.setHTTPStatusCode(200); + dataResponse.setHTTPStatusText(ASCIILiteral("OK")); + dataResponse.setHTTPHeaderField(HTTPHeaderName::ContentType, result.contentType); + protectedThis->didReceiveResponse(dataResponse); + + if (!protectedThis->reachedTerminalState() && dataSize) + protectedThis->didReceiveBuffer(result.data.releaseNonNull(), dataSize, DataPayloadWholeResource); + + if (!protectedThis->reachedTerminalState()) + protectedThis->didFinishLoading(currentTime()); + }); +} + void ResourceLoader::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) -{ - m_options.dataBufferingPolicy = dataBufferingPolicy; +{ + m_options.dataBufferingPolicy = dataBufferingPolicy; // Reset any already buffered data if (dataBufferingPolicy == DoNotBufferData) - m_resourceData = 0; + m_resourceData = nullptr; +} + +void ResourceLoader::willSwitchToSubstituteResource() +{ + ASSERT(!m_documentLoader->isSubstituteLoadPending(this)); + platformStrategies()->loaderStrategy()->remove(this); + if (m_handle) + m_handle->cancel(); } - void ResourceLoader::addDataOrBuffer(const char* data, unsigned length, SharedBuffer* buffer, DataPayloadType dataPayloadType) { if (m_options.dataBufferingPolicy == DoNotBufferData) return; - if (dataPayloadType == DataPayloadWholeResource) { - m_resourceData = buffer ? ResourceBuffer::adoptSharedBuffer(buffer) : ResourceBuffer::create(data, length); - return; - } - - if (!m_resourceData) - m_resourceData = buffer ? ResourceBuffer::adoptSharedBuffer(buffer) : ResourceBuffer::create(data, length); - else { + if (!m_resourceData || dataPayloadType == DataPayloadWholeResource) { if (buffer) - m_resourceData->append(buffer); + m_resourceData = buffer; else - m_resourceData->append(data, length); + m_resourceData = SharedBuffer::create(data, length); + return; } + + if (buffer) + m_resourceData->append(*buffer); + else + m_resourceData->append(data, length); } void ResourceLoader::clearResourceData() @@ -237,26 +323,15 @@ bool ResourceLoader::isSubresourceLoader() return false; } -void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) +void ResourceLoader::willSendRequestInternal(ResourceRequest& request, const ResourceResponse& redirectResponse) { // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); ASSERT(!m_reachedTerminalState); - -#if PLATFORM(IOS) - // Ensure an identifier is always set. This ensures that this assetion is not hit: - // <rdar://problem/11059794> ASSERTION FAILED: !HashTranslator::equal(KeyTraits::emptyValue(), key) in WebFrameLoaderClient::canAuthenticateAgainstProtectionSpace loading the attached web archive - // This is not needed in WebKit2, as it doesn't use m_identifier in WebFrameLoaderClient::canAuthenticateAgainstProtectionSpace - if (!m_identifier) { - m_identifier = m_frame->page()->progress().createUniqueIdentifier(); - frameLoader()->notifier().assignIdentifierToInitialRequest(m_identifier, documentLoader(), request); - - // If this ResourceLoader was stopped as a result of assignIdentifierToInitialRequest, bail out - if (m_reachedTerminalState) - return; - } +#if ENABLE(CONTENT_EXTENSIONS) + ASSERT(m_resourceType != ResourceType::Invalid); #endif // We need a resource identifier for all requests, even if FrameLoader is never going to see it (such as with CORS preflight requests). @@ -266,43 +341,115 @@ void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceRes createdResourceIdentifier = true; } +#if ENABLE(CONTENT_EXTENSIONS) + if (frameLoader()) { + Page* page = frameLoader()->frame().page(); + if (page && m_documentLoader) { + auto blockedStatus = page->userContentProvider().processContentExtensionRulesForLoad(request.url(), m_resourceType, *m_documentLoader); + applyBlockedStatusToRequest(blockedStatus, request); + if (blockedStatus.blockedLoad) { + request = { }; + didFail(blockedByContentBlockerError()); + return; + } + } + } +#endif + + if (request.isNull()) { + didFail(cannotShowURLError()); + return; + } + if (m_options.sendLoadCallbacks == SendCallbacks) { if (createdResourceIdentifier) frameLoader()->notifier().assignIdentifierToInitialRequest(m_identifier, documentLoader(), request); +#if PLATFORM(IOS) + // If this ResourceLoader was stopped as a result of assignIdentifierToInitialRequest, bail out + if (m_reachedTerminalState) + return; +#endif + frameLoader()->notifier().willSendRequest(this, request, redirectResponse); } -#if ENABLE(INSPECTOR) else InspectorInstrumentation::willSendRequest(m_frame.get(), m_identifier, m_frame->loader().documentLoader(), request, redirectResponse); + +#if USE(QUICK_LOOK) + if (auto previewConverter = m_documentLoader->previewConverter()) + request = previewConverter->safeRequest(request); #endif - if (!redirectResponse.isNull()) - platformStrategies()->loaderStrategy()->resourceLoadScheduler()->crossOriginRedirectReceived(this, request.url()); + bool isRedirect = !redirectResponse.isNull(); + if (isRedirect) + platformStrategies()->loaderStrategy()->crossOriginRedirectReceived(this, request.url()); m_request = request; - if (!redirectResponse.isNull() && !m_documentLoader->isCommitted()) - frameLoader()->client().dispatchDidReceiveServerRedirectForProvisionalLoad(); + if (isRedirect) { + auto& redirectURL = request.url(); + if (!m_documentLoader->isCommitted()) + frameLoader()->client().dispatchDidReceiveServerRedirectForProvisionalLoad(); + + if (redirectURL.protocolIsData()) { + // Handle data URL decoding locally. + finishNetworkLoad(); + loadDataURL(); + } + } +} + +void ResourceLoader::willSendRequest(ResourceRequest&& request, const ResourceResponse& redirectResponse, std::function<void(ResourceRequest&&)>&& callback) +{ + willSendRequestInternal(request, redirectResponse); + callback(WTFMove(request)); } void ResourceLoader::didSendData(unsigned long long, unsigned long long) { } +static void logResourceResponseSource(Frame* frame, ResourceResponse::Source source) +{ + if (!frame || !frame->page()) + return; + + String sourceKey; + switch (source) { + case ResourceResponse::Source::Network: + sourceKey = DiagnosticLoggingKeys::networkKey(); + break; + case ResourceResponse::Source::DiskCache: + sourceKey = DiagnosticLoggingKeys::diskCacheKey(); + break; + case ResourceResponse::Source::DiskCacheAfterValidation: + sourceKey = DiagnosticLoggingKeys::diskCacheAfterValidationKey(); + break; + case ResourceResponse::Source::MemoryCache: + case ResourceResponse::Source::MemoryCacheAfterValidation: + case ResourceResponse::Source::Unknown: + return; + } + + frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceResponseSourceKey(), sourceKey, ShouldSample::Yes); +} + void ResourceLoader::didReceiveResponse(const ResourceResponse& r) { ASSERT(!m_reachedTerminalState); // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); + + logResourceResponseSource(m_frame.get(), r.source()); m_response = r; if (FormData* data = m_request.httpBody()) data->removeGeneratedFilesIfNeeded(); - + if (m_options.sendLoadCallbacks == SendCallbacks) frameLoader()->notifier().didReceiveResponse(this, m_response); } @@ -315,26 +462,25 @@ void ResourceLoader::didReceiveData(const char* data, unsigned length, long long // ASSERT(con == connection); // ASSERT(!m_reachedTerminalState); - didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType); + didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType); } -void ResourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType) +void ResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { - didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType); + didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType); } -void ResourceLoader::didReceiveDataOrBuffer(const char* data, unsigned length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType) +void ResourceLoader::didReceiveDataOrBuffer(const char* data, unsigned length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { // This method should only get data+length *OR* a SharedBuffer. - ASSERT(!prpBuffer || (!data && !length)); + ASSERT(!buffer || (!data && !length)); // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. - Ref<ResourceLoader> protect(*this); - RefPtr<SharedBuffer> buffer = prpBuffer; + Ref<ResourceLoader> protectedThis(*this); addDataOrBuffer(data, length, buffer.get(), dataPayloadType); - + // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. // However, with today's computers and networking speeds, this won't happen in practice. // Could be an issue with a giant local file. @@ -342,15 +488,6 @@ void ResourceLoader::didReceiveDataOrBuffer(const char* data, unsigned length, P frameLoader()->notifier().didReceiveData(this, buffer ? buffer->data() : data, buffer ? buffer->size() : length, static_cast<int>(encodedDataLength)); } -void ResourceLoader::willStopBufferingData(const char* data, unsigned length) -{ - if (m_options.dataBufferingPolicy == DoNotBufferData) - return; - - ASSERT(!m_resourceData); - m_resourceData = ResourceBuffer::create(data, length); -} - void ResourceLoader::didFinishLoading(double finishTime) { didFinishLoadingOnePart(finishTime); @@ -385,7 +522,7 @@ void ResourceLoader::didFail(const ResourceError& error) // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); cleanupForError(error); releaseResources(); @@ -403,12 +540,6 @@ void ResourceLoader::cleanupForError(const ResourceError& error) frameLoader()->notifier().didFailToLoad(this, error); } -void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority) -{ - if (handle()) - handle()->didChangePriority(loadPriority); -} - void ResourceLoader::cancel() { cancel(ResourceError()); @@ -424,7 +555,7 @@ void ResourceLoader::cancel(const ResourceError& error) // willCancel() and didFailToLoad() both call out to clients that might do // something causing the last reference to this object to go away. - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); // If we re-enter cancel() from inside willCancel(), we want to pick up from where we left // off without re-running willCancel() @@ -445,7 +576,7 @@ void ResourceLoader::cancel(const ResourceError& error) m_documentLoader->cancelPendingSubstituteLoad(this); if (m_handle) { m_handle->cancel(); - m_handle = 0; + m_handle = nullptr; } cleanupForError(nonNullError); } @@ -474,16 +605,22 @@ ResourceError ResourceLoader::blockedError() return frameLoader()->client().blockedError(m_request); } +ResourceError ResourceLoader::blockedByContentBlockerError() +{ + return frameLoader()->client().blockedByContentBlockerError(m_request); +} + ResourceError ResourceLoader::cannotShowURLError() { return frameLoader()->client().cannotShowURLError(m_request); } -void ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse) +ResourceRequest ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&& redirectResponse) { - if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForRedirect(this, request, redirectResponse)) - return; - willSendRequest(request, redirectResponse); + if (documentLoader()->applicationCacheHost().maybeLoadFallbackForRedirect(this, request, redirectResponse)) + return WTFMove(request); + willSendRequestInternal(request, redirectResponse); + return WTFMove(request); } void ResourceLoader::didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) @@ -491,25 +628,21 @@ void ResourceLoader::didSendData(ResourceHandle*, unsigned long long bytesSent, didSendData(bytesSent, totalBytesToBeSent); } -void ResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +void ResourceLoader::didReceiveResponse(ResourceHandle*, ResourceResponse&& response) { - if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForResponse(this, response)) + if (documentLoader()->applicationCacheHost().maybeLoadFallbackForResponse(this, response)) return; didReceiveResponse(response); } void ResourceLoader::didReceiveData(ResourceHandle*, const char* data, unsigned length, int encodedDataLength) { - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(m_frame.get(), identifier(), encodedDataLength); didReceiveData(data, length, encodedDataLength, DataPayloadBytes); - InspectorInstrumentation::didReceiveResourceData(cookie); } -void ResourceLoader::didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int encodedDataLength) +void ResourceLoader::didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&& buffer, int encodedDataLength) { - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(m_frame.get(), identifier(), encodedDataLength); - didReceiveBuffer(buffer, encodedDataLength, DataPayloadBytes); - InspectorInstrumentation::didReceiveResourceData(cookie); + didReceiveBuffer(WTFMove(buffer), encodedDataLength, DataPayloadBytes); } void ResourceLoader::didFinishLoading(ResourceHandle*, double finishTime) @@ -519,7 +652,7 @@ void ResourceLoader::didFinishLoading(ResourceHandle*, double finishTime) void ResourceLoader::didFail(ResourceHandle*, const ResourceError& error) { - if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForError(this, error)) + if (documentLoader()->applicationCacheHost().maybeLoadFallbackForError(this, error)) return; didFail(error); } @@ -538,56 +671,53 @@ bool ResourceLoader::shouldUseCredentialStorage() { if (m_options.allowCredentials == DoNotAllowStoredCredentials) return false; - - Ref<ResourceLoader> protect(*this); + + Ref<ResourceLoader> protectedThis(*this); return frameLoader()->client().shouldUseCredentialStorage(documentLoader(), identifier()); } +bool ResourceLoader::isAllowedToAskUserForCredentials() const +{ + if (m_options.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials) + return false; + return m_options.credentials == FetchOptions::Credentials::Include || (m_options.credentials == FetchOptions::Credentials::SameOrigin && m_frame->document()->securityOrigin().canRequest(originalRequest().url())); +} + void ResourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) { - ASSERT(handle()->hasAuthenticationChallenge()); + ASSERT(m_handle->hasAuthenticationChallenge()); // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); if (m_options.allowCredentials == AllowStoredCredentials) { - if (m_options.clientCredentialPolicy == AskClientForAllCredentials || (m_options.clientCredentialPolicy == DoNotAskClientForCrossOriginCredentials && m_frame->document()->securityOrigin()->canRequest(originalRequest().url()))) { + if (isAllowedToAskUserForCredentials()) { frameLoader()->notifier().didReceiveAuthenticationChallenge(this, challenge); return; } } - // Only these platforms provide a way to continue without credentials. - // If we can't continue with credentials, we need to cancel the load altogether. -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(CURL) || PLATFORM(GTK) || PLATFORM(EFL) challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); - ASSERT(!handle() || !handle()->hasAuthenticationChallenge()); -#else - didFail(blockedError()); -#endif -} - -void ResourceLoader::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge) -{ - // Protect this in this delegate method since the additional processing can do - // anything including possibly derefing this; one example of this is Radar 3266216. - Ref<ResourceLoader> protect(*this); - frameLoader()->notifier().didCancelAuthenticationChallenge(this, challenge); + ASSERT(!m_handle || !m_handle->hasAuthenticationChallenge()); } #if USE(PROTECTION_SPACE_AUTH_CALLBACK) + bool ResourceLoader::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace) { - Ref<ResourceLoader> protect(*this); + Ref<ResourceLoader> protectedThis(*this); return frameLoader()->client().canAuthenticateAgainstProtectionSpace(documentLoader(), identifier(), protectionSpace); } + #endif #if PLATFORM(IOS) + RetainPtr<CFDictionaryRef> ResourceLoader::connectionProperties(ResourceHandle*) { return frameLoader()->connectionProperties(this); } + #endif void ResourceLoader::receivedCancellation(const AuthenticationChallenge&) @@ -595,4 +725,36 @@ void ResourceLoader::receivedCancellation(const AuthenticationChallenge&) cancel(); } +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + +void ResourceLoader::schedule(SchedulePair& pair) +{ + if (m_handle) + m_handle->schedule(pair); +} + +void ResourceLoader::unschedule(SchedulePair& pair) +{ + if (m_handle) + m_handle->unschedule(pair); +} + +#endif + +#if USE(QUICK_LOOK) +bool ResourceLoader::isQuickLookResource() const +{ + return !!m_quickLookHandle; +} +#endif + +bool ResourceLoader::isAlwaysOnLoggingAllowed() const +{ + return frameLoader() && frameLoader()->isAlwaysOnLoggingAllowed(); +} + +void ResourceLoader::didRetrieveDerivedDataFromCache(const String&, SharedBuffer&) +{ +} + } diff --git a/Source/WebCore/loader/ResourceLoader.h b/Source/WebCore/loader/ResourceLoader.h index 6b075ed3d..5bb1eda0a 100644 --- a/Source/WebCore/loader/ResourceLoader.h +++ b/Source/WebCore/loader/ResourceLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2005-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -26,17 +26,23 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceLoader_h -#define ResourceLoader_h +#pragma once +#include "LoadTiming.h" #include "ResourceHandleClient.h" #include "ResourceLoaderOptions.h" #include "ResourceLoaderTypes.h" #include "ResourceRequest.h" #include "ResourceResponse.h" - #include <wtf/Forward.h> -#include <wtf/RefCounted.h> + +#if ENABLE(CONTENT_EXTENSIONS) +#include "ResourceLoadInfo.h" +#endif + +namespace WTF { +class SchedulePair; +} namespace WebCore { @@ -44,18 +50,19 @@ class AuthenticationChallenge; class DocumentLoader; class Frame; class FrameLoader; +class QuickLookHandle; class URL; -class ResourceBuffer; -class ResourceHandle; class ResourceLoader : public RefCounted<ResourceLoader>, protected ResourceHandleClient { public: - virtual ~ResourceLoader(); + virtual ~ResourceLoader() = 0; - void cancel(); + WEBCORE_EXPORT void cancel(); virtual bool init(const ResourceRequest&); + void deliverResponseAndData(const ResourceResponse&, RefPtr<SharedBuffer>&&); + #if PLATFORM(IOS) virtual bool startLoading() { @@ -64,16 +71,17 @@ public: } virtual const ResourceRequest& iOSOriginalRequest() const { return request(); } - virtual RetainPtr<CFDictionaryRef> connectionProperties(ResourceHandle*) override; #endif - FrameLoader* frameLoader() const; + WEBCORE_EXPORT FrameLoader* frameLoader() const; DocumentLoader* documentLoader() const { return m_documentLoader.get(); } const ResourceRequest& originalRequest() const { return m_originalRequest; } - - virtual void cancel(const ResourceError&); - ResourceError cancelledError(); + + WEBCORE_EXPORT void start(); + WEBCORE_EXPORT void cancel(const ResourceError&); + WEBCORE_EXPORT ResourceError cancelledError(); ResourceError blockedError(); + ResourceError blockedByContentBlockerError(); ResourceError cannotShowURLError(); virtual void setDefersLoading(bool); @@ -82,118 +90,136 @@ public: unsigned long identifier() const { return m_identifier; } virtual void releaseResources(); - const ResourceResponse& response() const; + const ResourceResponse& response() const { return m_response; } - ResourceBuffer* resourceData() const { return m_resourceData.get(); } + SharedBuffer* resourceData() const { return m_resourceData.get(); } void clearResourceData(); virtual bool isSubresourceLoader(); - - virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); + + virtual void willSendRequest(ResourceRequest&&, const ResourceResponse& redirectResponse, std::function<void(ResourceRequest&&)>&& callback); virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, unsigned, long long encodedDataLength, DataPayloadType); - virtual void didReceiveBuffer(PassRefPtr<SharedBuffer>, long long encodedDataLength, DataPayloadType); - void willStopBufferingData(const char*, unsigned); + virtual void didReceiveBuffer(Ref<SharedBuffer>&&, long long encodedDataLength, DataPayloadType); virtual void didFinishLoading(double finishTime); virtual void didFail(const ResourceError&); #if USE(NETWORK_CFDATA_ARRAY_CALLBACK) virtual void didReceiveDataArray(CFArrayRef dataArray); #endif - void didChangePriority(ResourceLoadPriority); + virtual void didRetrieveDerivedDataFromCache(const String& type, SharedBuffer&); virtual bool shouldUseCredentialStorage(); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); - void didCancelAuthenticationChallenge(const AuthenticationChallenge&); #if USE(PROTECTION_SPACE_AUTH_CALLBACK) virtual bool canAuthenticateAgainstProtectionSpace(const ProtectionSpace&); #endif virtual void receivedCancellation(const AuthenticationChallenge&); - // ResourceHandleClient - virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& redirectResponse) override; - virtual void didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) override; - virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int encodedDataLength) override; - virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedDataLength) override; - virtual void didFinishLoading(ResourceHandle*, double finishTime) override; - virtual void didFail(ResourceHandle*, const ResourceError&) override; - virtual void wasBlocked(ResourceHandle*) override; - virtual void cannotShowURL(ResourceHandle*) override; -#if PLATFORM(MAC) && !USE(CFNETWORK) - virtual void willStopBufferingData(ResourceHandle*, const char* data, unsigned length) override { willStopBufferingData(data, length); } -#endif - virtual bool shouldUseCredentialStorage(ResourceHandle*) override { return shouldUseCredentialStorage(); } - virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) override { didReceiveAuthenticationChallenge(challenge); } - virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) override { didCancelAuthenticationChallenge(challenge); } -#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) - virtual void didReceiveDataArray(ResourceHandle*, CFArrayRef dataArray) override; -#endif -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) - virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace& protectionSpace) override { return canAuthenticateAgainstProtectionSpace(protectionSpace); } -#endif - virtual void receivedCancellation(ResourceHandle*, const AuthenticationChallenge& challenge) override { receivedCancellation(challenge); } -#if PLATFORM(MAC) -#if USE(CFNETWORK) - virtual CFCachedURLResponseRef willCacheResponse(ResourceHandle*, CFCachedURLResponseRef) override; -#else - virtual NSCachedURLResponse* willCacheResponse(ResourceHandle*, NSCachedURLResponse*) override; -#endif -#endif // PLATFORM(MAC) -#if PLATFORM(WIN) && USE(CFNETWORK) - // FIXME: Windows should use willCacheResponse - <https://bugs.webkit.org/show_bug.cgi?id=57257>. - virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef) override; +#if USE(QUICK_LOOK) + bool isQuickLookResource() const; #endif - const URL& url() const { return m_request.url(); } + const URL& url() const { return m_request.url(); } ResourceHandle* handle() const { return m_handle.get(); } bool shouldSendResourceLoadCallbacks() const { return m_options.sendLoadCallbacks == SendCallbacks; } void setSendCallbackPolicy(SendCallbackPolicy sendLoadCallbacks) { m_options.sendLoadCallbacks = sendLoadCallbacks; } bool shouldSniffContent() const { return m_options.sniffContent == SniffContent; } - ClientCredentialPolicy clientCredentialPolicy() const { return m_options.clientCredentialPolicy; } + WEBCORE_EXPORT bool isAllowedToAskUserForCredentials() const; + bool shouldIncludeCertificateInfo() const { return m_options.certificateInfoPolicy == IncludeCertificateInfo; } bool reachedTerminalState() const { return m_reachedTerminalState; } + const ResourceRequest& request() const { return m_request; } void setDataBufferingPolicy(DataBufferingPolicy); -protected: - ResourceLoader(Frame*, ResourceLoaderOptions); + void willSwitchToSubstituteResource(); + + const LoadTiming& loadTiming() { return m_loadTiming; } + +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + void schedule(WTF::SchedulePair&); + void unschedule(WTF::SchedulePair&); +#endif + + const Frame* frame() const { return m_frame.get(); } + WEBCORE_EXPORT bool isAlwaysOnLoggingAllowed() const; + + const ResourceLoaderOptions& options() const { return m_options; } - friend class ResourceLoadScheduler; // for access to start() - // start() actually sends the load to the network (unless the load is being - // deferred) and should only be called by ResourceLoadScheduler or setDefersLoading(). - void start(); +protected: + ResourceLoader(Frame&, ResourceLoaderOptions); void didFinishLoadingOnePart(double finishTime); void cleanupForError(const ResourceError&); bool wasCancelled() const { return m_cancellationStatus >= Cancelled; } - void didReceiveDataOrBuffer(const char*, unsigned, PassRefPtr<SharedBuffer>, long long encodedDataLength, DataPayloadType); + void didReceiveDataOrBuffer(const char*, unsigned, RefPtr<SharedBuffer>&&, long long encodedDataLength, DataPayloadType); + +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + NSCachedURLResponse* willCacheResponse(ResourceHandle*, NSCachedURLResponse*) override; +#endif +#if PLATFORM(COCOA) && USE(CFURLCONNECTION) + CFCachedURLResponseRef willCacheResponse(ResourceHandle*, CFCachedURLResponseRef) override; +#endif - const ResourceLoaderOptions& options() { return m_options; } + virtual void willSendRequestInternal(ResourceRequest&, const ResourceResponse& redirectResponse); RefPtr<ResourceHandle> m_handle; RefPtr<Frame> m_frame; RefPtr<DocumentLoader> m_documentLoader; ResourceResponse m_response; - + LoadTiming m_loadTiming; +#if USE(QUICK_LOOK) + std::unique_ptr<QuickLookHandle> m_quickLookHandle; +#endif + private: virtual void willCancel(const ResourceError&) = 0; virtual void didCancel(const ResourceError&) = 0; void addDataOrBuffer(const char*, unsigned, SharedBuffer*, DataPayloadType); + void loadDataURL(); + void finishNetworkLoad(); + + // ResourceHandleClient + ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&& redirectResponse) override; + void didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override; + void didReceiveData(ResourceHandle*, const char*, unsigned, int encodedDataLength) override; + void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedDataLength) override; + void didFinishLoading(ResourceHandle*, double finishTime) override; + void didFail(ResourceHandle*, const ResourceError&) override; + void wasBlocked(ResourceHandle*) override; + void cannotShowURL(ResourceHandle*) override; +#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) + void didReceiveDataArray(ResourceHandle*, CFArrayRef dataArray) override; +#endif + bool shouldUseCredentialStorage(ResourceHandle*) override { return shouldUseCredentialStorage(); } + void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) override { didReceiveAuthenticationChallenge(challenge); } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace& protectionSpace) override { return canAuthenticateAgainstProtectionSpace(protectionSpace); } +#endif + void receivedCancellation(ResourceHandle*, const AuthenticationChallenge& challenge) override { receivedCancellation(challenge); } +#if PLATFORM(IOS) + RetainPtr<CFDictionaryRef> connectionProperties(ResourceHandle*) override; +#endif +#if PLATFORM(WIN) && USE(CFURLCONNECTION) + // FIXME: Windows should use willCacheResponse - <https://bugs.webkit.org/show_bug.cgi?id=57257>. + bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef) override; +#endif ResourceRequest m_request; ResourceRequest m_originalRequest; // Before redirects. - RefPtr<ResourceBuffer> m_resourceData; + RefPtr<SharedBuffer> m_resourceData; - unsigned long m_identifier; + unsigned long m_identifier { 0 }; - bool m_reachedTerminalState; - bool m_notifiedLoadComplete; + bool m_reachedTerminalState { false }; + bool m_notifiedLoadComplete { false }; enum CancellationStatus { NotCancelled, @@ -201,18 +227,16 @@ private: Cancelled, FinishedCancel }; - CancellationStatus m_cancellationStatus; + CancellationStatus m_cancellationStatus { NotCancelled }; bool m_defersLoading; ResourceRequest m_deferredRequest; ResourceLoaderOptions m_options; -}; - -inline const ResourceResponse& ResourceLoader::response() const -{ - return m_response; -} - -} +#if ENABLE(CONTENT_EXTENSIONS) +protected: + ResourceType m_resourceType { ResourceType::Invalid }; #endif +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoaderOptions.h b/Source/WebCore/loader/ResourceLoaderOptions.h index b87d98722..b93bcb68f 100644 --- a/Source/WebCore/loader/ResourceLoaderOptions.h +++ b/Source/WebCore/loader/ResourceLoaderOptions.h @@ -28,13 +28,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceLoaderOptions_h -#define ResourceLoaderOptions_h +#pragma once +#include "FetchOptions.h" #include "ResourceHandleTypes.h" +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> namespace WebCore { - + enum SendCallbackPolicy { SendCallbacks, DoNotSendCallbacks @@ -55,43 +57,78 @@ enum SecurityCheckPolicy { DoSecurityCheck }; -enum RequestOriginPolicy { - UseDefaultOriginRestrictionsForType, - RestrictToSameOrigin, - PotentiallyCrossOriginEnabled // Indicates "potentially CORS-enabled fetch" in HTML standard. +enum CertificateInfoPolicy { + IncludeCertificateInfo, + DoNotIncludeCertificateInfo }; -struct ResourceLoaderOptions { - ResourceLoaderOptions() - : sendLoadCallbacks(DoNotSendCallbacks) - , sniffContent(DoNotSniffContent) - , dataBufferingPolicy(BufferData) - , allowCredentials(DoNotAllowStoredCredentials) - , clientCredentialPolicy(DoNotAskClientForAnyCredentials) - , securityCheck(DoSecurityCheck) - , requestOriginPolicy(UseDefaultOriginRestrictionsForType) - { - } +enum class ContentSecurityPolicyImposition : uint8_t { + SkipPolicyCheck, + DoPolicyCheck +}; + +enum class DefersLoadingPolicy : uint8_t { + AllowDefersLoading, + DisallowDefersLoading +}; + +enum class CachingPolicy : uint8_t { + AllowCaching, + DisallowCaching +}; + +enum class ClientCredentialPolicy { + CannotAskClientForCredentials, + MayAskClientForCredentials +}; + +enum class SameOriginDataURLFlag { + Set, + Unset +}; + +enum class InitiatorContext { + Document, + Worker, +}; - ResourceLoaderOptions(SendCallbackPolicy sendLoadCallbacks, ContentSniffingPolicy sniffContent, DataBufferingPolicy dataBufferingPolicy, StoredCredentials allowCredentials, ClientCredentialPolicy credentialPolicy, SecurityCheckPolicy securityCheck, RequestOriginPolicy requestOriginPolicy) +struct ResourceLoaderOptions : public FetchOptions { + ResourceLoaderOptions() { } + + ResourceLoaderOptions(const FetchOptions& options) : FetchOptions(options) { } + + ResourceLoaderOptions(SendCallbackPolicy sendLoadCallbacks, ContentSniffingPolicy sniffContent, DataBufferingPolicy dataBufferingPolicy, StoredCredentials allowCredentials, ClientCredentialPolicy credentialPolicy, FetchOptions::Credentials credentials, SecurityCheckPolicy securityCheck, FetchOptions::Mode mode, CertificateInfoPolicy certificateInfoPolicy, ContentSecurityPolicyImposition contentSecurityPolicyImposition, DefersLoadingPolicy defersLoadingPolicy, CachingPolicy cachingPolicy) : sendLoadCallbacks(sendLoadCallbacks) , sniffContent(sniffContent) , dataBufferingPolicy(dataBufferingPolicy) , allowCredentials(allowCredentials) - , clientCredentialPolicy(credentialPolicy) , securityCheck(securityCheck) - , requestOriginPolicy(requestOriginPolicy) + , certificateInfoPolicy(certificateInfoPolicy) + , contentSecurityPolicyImposition(contentSecurityPolicyImposition) + , defersLoadingPolicy(defersLoadingPolicy) + , cachingPolicy(cachingPolicy) + , clientCredentialPolicy(credentialPolicy) { + this->credentials = credentials; + this->mode = mode; } - SendCallbackPolicy sendLoadCallbacks : 1; - ContentSniffingPolicy sniffContent : 1; - DataBufferingPolicy dataBufferingPolicy : 1; - StoredCredentials allowCredentials : 1; // Whether HTTP credentials and cookies are sent with the request. - ClientCredentialPolicy clientCredentialPolicy : 2; // When we should ask the client for credentials (if we allow credentials at all). - SecurityCheckPolicy securityCheck : 1; - RequestOriginPolicy requestOriginPolicy : 2; -}; -} // namespace WebCore + SendCallbackPolicy sendLoadCallbacks { DoNotSendCallbacks }; + ContentSniffingPolicy sniffContent { DoNotSniffContent }; + DataBufferingPolicy dataBufferingPolicy { BufferData }; + StoredCredentials allowCredentials { DoNotAllowStoredCredentials }; + SecurityCheckPolicy securityCheck { DoSecurityCheck }; + CertificateInfoPolicy certificateInfoPolicy { DoNotIncludeCertificateInfo }; + ContentSecurityPolicyImposition contentSecurityPolicyImposition { ContentSecurityPolicyImposition::DoPolicyCheck }; + DefersLoadingPolicy defersLoadingPolicy { DefersLoadingPolicy::AllowDefersLoading }; + CachingPolicy cachingPolicy { CachingPolicy::AllowCaching }; + SameOriginDataURLFlag sameOriginDataURLFlag { SameOriginDataURLFlag::Unset }; + InitiatorContext initiatorContext { InitiatorContext::Document }; + + ClientCredentialPolicy clientCredentialPolicy { ClientCredentialPolicy::CannotAskClientForCredentials }; + unsigned maxRedirectCount { 20 }; + + Vector<String> derivedCachedDataTypesToRetrieve; +}; -#endif // ResourceLoaderOptions_h +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceLoaderTypes.h b/Source/WebCore/loader/ResourceLoaderTypes.h index e85c3c26e..2f14f203f 100644 --- a/Source/WebCore/loader/ResourceLoaderTypes.h +++ b/Source/WebCore/loader/ResourceLoaderTypes.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceLoaderTypes_h -#define ResourceLoaderTypes_h +#pragma once namespace WebCore { @@ -39,5 +38,3 @@ enum DataPayloadType { }; } // namespace WebCore - -#endif // ResourceLoaderTypes_h diff --git a/Source/WebCore/loader/ResourceTiming.cpp b/Source/WebCore/loader/ResourceTiming.cpp new file mode 100644 index 000000000..ccdef73f2 --- /dev/null +++ b/Source/WebCore/loader/ResourceTiming.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "ResourceTiming.h" + +#include "CachedResource.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +static bool passesTimingAllowCheck(const ResourceResponse& response, const SecurityOrigin& initiatorSecurityOrigin) +{ + Ref<SecurityOrigin> resourceOrigin = SecurityOrigin::create(response.url()); + if (resourceOrigin->isSameSchemeHostPort(initiatorSecurityOrigin)) + return true; + + const String& timingAllowOriginString = response.httpHeaderField(HTTPHeaderName::TimingAllowOrigin); + if (timingAllowOriginString.isEmpty() || equalLettersIgnoringASCIICase(timingAllowOriginString, "null")) + return false; + + if (timingAllowOriginString == "*") + return true; + + const String& securityOrigin = initiatorSecurityOrigin.toString(); + Vector<String> timingAllowOrigins; + timingAllowOriginString.split(',', timingAllowOrigins); + for (auto& origin : timingAllowOrigins) { + if (origin.stripWhiteSpace() == securityOrigin) + return true; + } + + return false; +} + +ResourceTiming ResourceTiming::fromCache(const URL& url, const String& initiator, const LoadTiming& loadTiming) +{ + return ResourceTiming(url, initiator, loadTiming); +} + +ResourceTiming ResourceTiming::fromLoad(CachedResource& resource, const String& initiator, const LoadTiming& loadTiming, const SecurityOrigin& securityOrigin) +{ + return ResourceTiming(resource, initiator, loadTiming, securityOrigin); +} + +ResourceTiming ResourceTiming::fromSynchronousLoad(const URL& url, const String& initiator, const LoadTiming& loadTiming, const NetworkLoadTiming& networkLoadTiming, const ResourceResponse& response, const SecurityOrigin& securityOrigin) +{ + return ResourceTiming(url, initiator, loadTiming, networkLoadTiming, response, securityOrigin); +} + +ResourceTiming::ResourceTiming(const URL& url, const String& initiator, const LoadTiming& loadTiming) + : m_url(url) + , m_initiator(initiator) + , m_loadTiming(loadTiming) + , m_allowTimingDetails(true) +{ +} + +ResourceTiming::ResourceTiming(CachedResource& resource, const String& initiator, const LoadTiming& loadTiming, const SecurityOrigin& securityOrigin) + : m_url(resource.resourceRequest().url()) + , m_initiator(initiator) + , m_loadTiming(loadTiming) + , m_networkLoadTiming(resource.response().networkLoadTiming()) + , m_allowTimingDetails(passesTimingAllowCheck(resource.response(), securityOrigin)) +{ +} + +ResourceTiming::ResourceTiming(const URL& url, const String& initiator, const LoadTiming& loadTiming, const NetworkLoadTiming& networkLoadTiming, const ResourceResponse& response, const SecurityOrigin& securityOrigin) + : m_url(url) + , m_initiator(initiator) + , m_loadTiming(loadTiming) + , m_networkLoadTiming(networkLoadTiming) + , m_allowTimingDetails(passesTimingAllowCheck(response, securityOrigin)) +{ +} + +ResourceTiming ResourceTiming::isolatedCopy() const +{ + return ResourceTiming(m_url.isolatedCopy(), m_initiator.isolatedCopy(), m_loadTiming.isolatedCopy(), m_networkLoadTiming.isolatedCopy(), m_allowTimingDetails); +} + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceTiming.h b/Source/WebCore/loader/ResourceTiming.h new file mode 100644 index 000000000..222122ab1 --- /dev/null +++ b/Source/WebCore/loader/ResourceTiming.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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. + */ + +#pragma once + +#include "LoadTiming.h" +#include "NetworkLoadTiming.h" +#include "URL.h" + +namespace WebCore { + +class CachedResource; +class ResourceResponse; +class SecurityOrigin; + +class ResourceTiming { + WTF_MAKE_FAST_ALLOCATED; +public: + static ResourceTiming fromCache(const URL&, const String& initiator, const LoadTiming&); + static ResourceTiming fromLoad(CachedResource&, const String& initiator, const LoadTiming&, const SecurityOrigin&); + static ResourceTiming fromSynchronousLoad(const URL&, const String& initiator, const LoadTiming&, const NetworkLoadTiming&, const ResourceResponse&, const SecurityOrigin&); + + URL url() const { return m_url; } + String initiator() const { return m_initiator; } + LoadTiming loadTiming() const { return m_loadTiming; } + NetworkLoadTiming networkLoadTiming() const { return m_networkLoadTiming; } + bool allowTimingDetails() const { return m_allowTimingDetails; } + + ResourceTiming isolatedCopy() const; + + void overrideInitiatorName(const String& name) { m_initiator = name; } + +private: + ResourceTiming(CachedResource&, const String& initiator, const LoadTiming&, const SecurityOrigin&); + ResourceTiming(const URL&, const String& initiator, const LoadTiming&, const NetworkLoadTiming&, const ResourceResponse&, const SecurityOrigin&); + ResourceTiming(const URL&, const String& initiator, const LoadTiming&); + ResourceTiming(const URL& url, const String& initiator, const LoadTiming& loadTiming, const NetworkLoadTiming& networkLoadTiming, bool allowTimingDetails) + : m_url(url) + , m_initiator(initiator) + , m_loadTiming(loadTiming) + , m_networkLoadTiming(networkLoadTiming) + , m_allowTimingDetails(allowTimingDetails) + { + } + + URL m_url; + String m_initiator; + LoadTiming m_loadTiming; + NetworkLoadTiming m_networkLoadTiming; + bool m_allowTimingDetails { false }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/ResourceTimingInformation.cpp b/Source/WebCore/loader/ResourceTimingInformation.cpp new file mode 100644 index 000000000..5ef15536c --- /dev/null +++ b/Source/WebCore/loader/ResourceTimingInformation.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 Akamai Technologies 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "ResourceTimingInformation.h" + +#if ENABLE(WEB_TIMING) + +#include "CachedResource.h" +#include "DOMWindow.h" +#include "Document.h" +#include "Frame.h" +#include "HTMLFrameOwnerElement.h" +#include "LoadTiming.h" +#include "Performance.h" +#include "ResourceTiming.h" +#include "RuntimeEnabledFeatures.h" + +namespace WebCore { + +bool ResourceTimingInformation::shouldAddResourceTiming(CachedResource& resource) +{ + // FIXME: We can be less restrictive here. + // <https://github.com/w3c/resource-timing/issues/100> + if (!resource.resourceRequest().url().protocolIsInHTTPFamily()) + return false; + if (resource.response().httpStatusCode() >= 400) + return false; + if (resource.errorOccurred()) + return false; + if (resource.wasCanceled()) + return false; + + return true; +} + +void ResourceTimingInformation::addResourceTiming(CachedResource& resource, Document& document, ResourceTiming&& resourceTiming) +{ + ASSERT(RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()); + if (!ResourceTimingInformation::shouldAddResourceTiming(resource)) + return; + + auto iterator = m_initiatorMap.find(&resource); + if (iterator == m_initiatorMap.end()) + return; + + InitiatorInfo& info = iterator->value; + if (info.added == Added) + return; + + Document* initiatorDocument = &document; + if (resource.type() == CachedResource::MainResource) + initiatorDocument = document.parentDocument(); + if (!initiatorDocument) + return; + if (!initiatorDocument->domWindow()) + return; + if (!initiatorDocument->domWindow()->performance()) + return; + + resourceTiming.overrideInitiatorName(info.name); + + initiatorDocument->domWindow()->performance()->addResourceTiming(WTFMove(resourceTiming)); + + info.added = Added; +} + +void ResourceTimingInformation::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const AtomicString& initiatorName, Frame* frame) +{ + ASSERT(RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()); + ASSERT(resource.get()); + + if (resource->type() == CachedResource::MainResource) { + // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. + ASSERT(frame); + if (frame->ownerElement()) { + InitiatorInfo info = { frame->ownerElement()->localName(), NotYetAdded }; + m_initiatorMap.add(resource.get(), info); + } + } else { + InitiatorInfo info = { initiatorName, NotYetAdded }; + m_initiatorMap.add(resource.get(), info); + } +} + +} + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/loader/ResourceTimingInformation.h b/Source/WebCore/loader/ResourceTimingInformation.h new file mode 100644 index 000000000..594a91a1f --- /dev/null +++ b/Source/WebCore/loader/ResourceTimingInformation.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Akamai Technologies 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +#pragma once + +#if ENABLE(WEB_TIMING) + +#include "CachedResourceHandle.h" +#include <wtf/HashMap.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class CachedResource; +class Document; +class Frame; +class ResourceTiming; + +class ResourceTimingInformation { +public: + static bool shouldAddResourceTiming(CachedResource&); + + void addResourceTiming(CachedResource&, Document&, ResourceTiming&&); + void storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>&, const AtomicString&, Frame*); + +private: + enum AlreadyAdded { NotYetAdded, Added }; + struct InitiatorInfo { + AtomicString name; + AlreadyAdded added; + }; + HashMap<CachedResource*, InitiatorInfo> m_initiatorMap; +}; + +} + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/loader/SinkDocument.cpp b/Source/WebCore/loader/SinkDocument.cpp index ad2f099e4..a1bf52a3c 100644 --- a/Source/WebCore/loader/SinkDocument.cpp +++ b/Source/WebCore/loader/SinkDocument.cpp @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -32,9 +32,9 @@ namespace WebCore { class SinkDocumentParser final : public RawDataDocumentParser { public: - static PassRefPtr<SinkDocumentParser> create(SinkDocument& document) + static Ref<SinkDocumentParser> create(SinkDocument& document) { - return adoptRef(new SinkDocumentParser(document)); + return adoptRef(*new SinkDocumentParser(document)); } private: @@ -44,7 +44,7 @@ private: } // Ignore all data. - virtual void appendBytes(DocumentWriter&, const char*, size_t) + void appendBytes(DocumentWriter&, const char*, size_t) override { } }; @@ -52,11 +52,11 @@ private: SinkDocument::SinkDocument(Frame* frame, const URL& url) : HTMLDocument(frame, url) { - setCompatibilityMode(QuirksMode); + setCompatibilityMode(DocumentCompatibilityMode::QuirksMode); lockCompatibilityMode(); } -PassRefPtr<DocumentParser> SinkDocument::createParser() +Ref<DocumentParser> SinkDocument::createParser() { return SinkDocumentParser::create(*this); } diff --git a/Source/WebCore/loader/SinkDocument.h b/Source/WebCore/loader/SinkDocument.h index bde3e59ed..f70f28cbc 100644 --- a/Source/WebCore/loader/SinkDocument.h +++ b/Source/WebCore/loader/SinkDocument.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SinkDocument_h -#define SinkDocument_h +#pragma once #include "HTMLDocument.h" @@ -32,18 +31,15 @@ namespace WebCore { class SinkDocument final : public HTMLDocument { public: - static PassRefPtr<SinkDocument> create(Frame* frame, const URL& url) + static Ref<SinkDocument> create(Frame* frame, const URL& url) { - return adoptRef(new SinkDocument(frame, url)); + return adoptRef(*new SinkDocument(frame, url)); } private: SinkDocument(Frame*, const URL&); - - virtual PassRefPtr<DocumentParser> createParser(); -}; + Ref<DocumentParser> createParser() final; +}; }; // namespace WebCore - -#endif // SinkDocument_h diff --git a/Source/WebCore/loader/SubframeLoader.cpp b/Source/WebCore/loader/SubframeLoader.cpp index 2d8ba8164..234625280 100644 --- a/Source/WebCore/loader/SubframeLoader.cpp +++ b/Source/WebCore/loader/SubframeLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008 Alp Toker <alp@atoker.com> @@ -14,7 +14,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. * @@ -33,19 +33,19 @@ #include "config.h" #include "SubframeLoader.h" -#include "Chrome.h" -#include "ChromeClient.h" #include "ContentSecurityPolicy.h" +#include "DiagnosticLoggingClient.h" #include "DiagnosticLoggingKeys.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "HTMLAppletElement.h" -#include "HTMLAudioElement.h" -#include "HTMLFrameElementBase.h" +#include "HTMLFrameElement.h" +#include "HTMLIFrameElement.h" #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "MIMETypeRegistry.h" +#include "MainFrame.h" #include "Page.h" #include "PluginData.h" #include "PluginDocument.h" @@ -56,11 +56,6 @@ #include "SecurityPolicy.h" #include "Settings.h" -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -#include "HTMLMediaElement.h" -#include "RenderVideo.h" -#endif - namespace WebCore { using namespace HTMLNames; @@ -76,7 +71,7 @@ void SubframeLoader::clear() m_containsPlugins = false; } -bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) +bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList) { // Support for <frame src="javascript:string"> URL scriptURL; @@ -87,55 +82,50 @@ bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const Str } else url = completeURL(urlString); + if (shouldConvertInvalidURLsToBlank() && !url.isValid()) + url = blankURL(); + Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList); if (!frame) return false; - if (!scriptURL.isEmpty()) + if (!scriptURL.isEmpty() && ownerElement.isURLAllowed(scriptURL)) frame->script().executeIfJavaScriptURL(scriptURL); return true; } -bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages) +bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType) { URL completedURL; if (!url.isEmpty()) completedURL = completeURL(url); bool useFallback; - return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback); + return shouldUsePlugin(completedURL, mimeType, false, useFallback); } -bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType) +bool SubframeLoader::pluginIsLoadable(const URL& url, const String& mimeType) { + auto* document = m_frame.document(); + if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { if (!m_frame.settings().isJavaEnabled()) return false; - if (document() && document()->securityOrigin()->isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles()) + if (document && document->securityOrigin().isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles()) return false; } - if (document()) { - if (document()->isSandboxed(SandboxPlugins)) + if (document) { + if (document->isSandboxed(SandboxPlugins)) return false; - if (!document()->securityOrigin()->canDisplay(url)) { + if (!document->securityOrigin().canDisplay(url)) { FrameLoader::reportLocalLoadFailed(&m_frame, url.string()); return false; } - String declaredMimeType = document()->isPluginDocument() && document()->ownerElement() ? - document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) : - pluginElement.fastGetAttribute(HTMLNames::typeAttr); - if (!document()->contentSecurityPolicy()->allowObjectFromSource(url) - || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) { - RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject(); - renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy); - return false; - } - - if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document()->securityOrigin(), url)) + if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document->securityOrigin(), url)) return false; } @@ -147,10 +137,10 @@ bool SubframeLoader::requestPlugin(HTMLPlugInImageElement& ownerElement, const U // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins, // as opposed to third-party code such as Flash. The user agent decides whether or not they are // permitted, rather than WebKit. - if ((!allowPlugins(AboutToInstantiatePlugin) && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType))) + if ((!allowPlugins() && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType))) return false; - if (!pluginIsLoadable(ownerElement, url, mimeType)) + if (!pluginIsLoadable(url, mimeType)) return false; ASSERT(ownerElement.hasTagName(objectTag) || ownerElement.hasTagName(embedTag)); @@ -170,11 +160,13 @@ static String findPluginMIMETypeFromURL(Page* page, const String& url) const PluginData& pluginData = page->pluginData(); - for (size_t i = 0; i < pluginData.mimes().size(); ++i) { - const MimeClassInfo& mimeClassInfo = pluginData.mimes()[i]; - for (size_t j = 0; j < mimeClassInfo.extensions.size(); ++j) { - if (equalIgnoringCase(extension, mimeClassInfo.extensions[j])) - return mimeClassInfo.type; + Vector<MimeClassInfo> mimes; + Vector<size_t> mimePluginIndices; + pluginData.getWebVisibleMimesAndPluginIndices(mimes, mimePluginIndices); + for (auto& mime : mimes) { + for (auto& mimeExtension : mime.extensions) { + if (equalIgnoringASCIICase(extension, mimeExtension)) + return mime.type; } } @@ -183,7 +175,7 @@ static String findPluginMIMETypeFromURL(Page* page, const String& url) static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success) { - if (!page || !page->settings().diagnosticLoggingEnabled()) + if (!page) return; String newMIMEType = mimeType; @@ -194,17 +186,17 @@ static void logPluginRequest(Page* page, const String& mimeType, const String& u return; } - String pluginFile = page->pluginData().pluginFileForMimeType(newMIMEType); + String pluginFile = page->pluginData().pluginFileForWebVisibleMimeType(newMIMEType); String description = !pluginFile ? newMIMEType : pluginFile; - ChromeClient& chromeClient = page->chrome().client(); - chromeClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, DiagnosticLoggingKeys::noopKey()); + DiagnosticLoggingClient& diagnosticLoggingClient = page->diagnosticLoggingClient(); + diagnosticLoggingClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, ShouldSample::No); if (!page->hasSeenAnyPlugin()) - chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), DiagnosticLoggingKeys::noopKey()); - + diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), ShouldSample::No); + if (!page->hasSeenPlugin(description)) - chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, DiagnosticLoggingKeys::noopKey()); + diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, ShouldSample::No); page->sawPlugin(description); } @@ -214,99 +206,66 @@ bool SubframeLoader::requestObject(HTMLPlugInImageElement& ownerElement, const S if (url.isEmpty() && mimeType.isEmpty()) return false; + auto& document = ownerElement.document(); + URL completedURL; if (!url.isEmpty()) completedURL = completeURL(url); - bool hasFallbackContent = isHTMLObjectElement(ownerElement) && toHTMLObjectElement(ownerElement).hasFallbackContent(); + document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(completedURL, ContentSecurityPolicy::InsecureRequestType::Load); + + bool hasFallbackContent = is<HTMLObjectElement>(ownerElement) && downcast<HTMLObjectElement>(ownerElement).hasFallbackContent(); bool useFallback; - if (shouldUsePlugin(completedURL, mimeType, ownerElement.shouldPreferPlugInsForImages(), hasFallbackContent, useFallback)) { + if (shouldUsePlugin(completedURL, mimeType, hasFallbackContent, useFallback)) { bool success = requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback); - logPluginRequest(document()->page(), mimeType, completedURL, success); + logPluginRequest(document.page(), mimeType, completedURL, success); return success; } // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise, // it will create a new frame and set it as the RenderWidget's Widget, causing what was previously // in the widget to be torn down. - return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true); + return loadOrRedirectSubframe(ownerElement, completedURL, frameName, LockHistory::Yes, LockBackForwardList::Yes); } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(HTMLMediaElement& mediaElement, const URL& url, const Vector<String>& paramNames, const Vector<String>& paramValues) -{ - ASSERT(mediaElement.hasTagName(videoTag) || isHTMLAudioElement(mediaElement)); - - URL completedURL; - if (!url.isEmpty()) - completedURL = completeURL(url); - - if (!m_frame.document()->securityOrigin()->canDisplay(completedURL)) { - FrameLoader::reportLocalLoadFailed(&m_frame, completedURL.string()); - return nullptr; - } - - if (!m_frame.document()->contentSecurityPolicy()->allowMediaFromSource(completedURL)) - return nullptr; - - RenderWidget* renderer = toRenderWidget(mediaElement.renderer()); - IntSize size; - - if (renderer) - size = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight())); - else if (mediaElement.isVideo()) - size = RenderVideo::defaultSize(); - - if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(m_frame.document()->securityOrigin(), completedURL)) - return nullptr; - - RefPtr<Widget> widget = m_frame.loader().client().createMediaPlayerProxyPlugin(size, &mediaElement, completedURL, paramNames, paramValues, "application/x-media-element-proxy-plugin"); - - if (widget && renderer) { - renderer->setWidget(widget); - renderer->frameOwnerElement().setNeedsStyleRecalc(SyntheticStyleChange); - } - m_containsPlugins = true; - - return widget ? widget.release() : nullptr; -} -#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) - -PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues) +RefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues) { String baseURLString; String codeBaseURLString; for (size_t i = 0; i < paramNames.size(); ++i) { - if (equalIgnoringCase(paramNames[i], "baseurl")) + if (equalLettersIgnoringASCIICase(paramNames[i], "baseurl")) baseURLString = paramValues[i]; - else if (equalIgnoringCase(paramNames[i], "codebase")) + else if (equalLettersIgnoringASCIICase(paramNames[i], "codebase")) codeBaseURLString = paramValues[i]; } if (!codeBaseURLString.isEmpty()) { URL codeBaseURL = completeURL(codeBaseURLString); - if (!element.document().securityOrigin()->canDisplay(codeBaseURL)) { + if (!element.document().securityOrigin().canDisplay(codeBaseURL)) { FrameLoader::reportLocalLoadFailed(&m_frame, codeBaseURL.string()); return nullptr; } const char javaAppletMimeType[] = "application/x-java-applet"; - if (!element.document().contentSecurityPolicy()->allowObjectFromSource(codeBaseURL) - || !element.document().contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL)) + ASSERT(element.document().contentSecurityPolicy()); + auto& contentSecurityPolicy = *element.document().contentSecurityPolicy(); + // Elements in user agent show tree should load whatever the embedding document policy is. + if (!element.isInUserAgentShadowTree() + && (!contentSecurityPolicy.allowObjectFromSource(codeBaseURL) || !contentSecurityPolicy.allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL))) return nullptr; } if (baseURLString.isEmpty()) - baseURLString = m_frame.document()->baseURL().string(); + baseURLString = element.document().baseURL().string(); URL baseURL = completeURL(baseURLString); RefPtr<Widget> widget; - if (allowPlugins(AboutToInstantiatePlugin)) - widget = m_frame.loader().client().createJavaAppletWidget(size, &element, baseURL, paramNames, paramValues); + if (allowPlugins()) + widget = m_frame.loader().client().createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); - logPluginRequest(document()->page(), element.serviceType(), String(), widget); + logPluginRequest(m_frame.page(), element.serviceType(), String(), widget); if (!widget) { RenderEmbeddedObject* renderer = element.renderEmbeddedObject(); @@ -320,13 +279,18 @@ PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, H return widget; } -Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) +Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& requestURL, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList) { - Frame* frame = ownerElement.contentFrame(); + auto& initiatingDocument = ownerElement.document(); + + URL upgradedRequestURL = requestURL; + initiatingDocument.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(upgradedRequestURL, ContentSecurityPolicy::InsecureRequestType::Load); + + auto* frame = ownerElement.contentFrame(); if (frame) - frame->navigationScheduler().scheduleLocationChange(m_frame.document()->securityOrigin(), url.string(), m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList); + frame->navigationScheduler().scheduleLocationChange(initiatingDocument, initiatingDocument.securityOrigin(), upgradedRequestURL, m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList); else - frame = loadSubframe(ownerElement, url, frameName, m_frame.loader().outgoingReferrer()); + frame = loadSubframe(ownerElement, upgradedRequestURL, frameName, m_frame.loader().outgoingReferrer()); if (!frame) return nullptr; @@ -342,14 +306,16 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U bool allowsScrolling = true; int marginWidth = -1; int marginHeight = -1; - if (ownerElement.hasTagName(frameTag) || ownerElement.hasTagName(iframeTag)) { - HTMLFrameElementBase& frameElementBase = toHTMLFrameElementBase(ownerElement); + if (is<HTMLFrameElementBase>(ownerElement)) { + auto& frameElementBase = downcast<HTMLFrameElementBase>(ownerElement); allowsScrolling = frameElementBase.scrollingMode() != ScrollbarAlwaysOff; marginWidth = frameElementBase.marginWidth(); marginHeight = frameElementBase.marginHeight(); } - if (!ownerElement.document().securityOrigin()->canDisplay(url)) { + auto document = makeRef(ownerElement.document()); + + if (!document->securityOrigin().canDisplay(url)) { FrameLoader::reportLocalLoadFailed(&m_frame, url.string()); return nullptr; } @@ -357,14 +323,20 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U if (!SubframeLoadingDisabler::canLoadFrame(ownerElement)) return nullptr; - String referrerToUse = SecurityPolicy::generateReferrerHeader(ownerElement.document().referrerPolicy(), url, referrer); - RefPtr<Frame> frame = m_frame.loader().client().createFrame(url, name, &ownerElement, referrerToUse, allowsScrolling, marginWidth, marginHeight); + String referrerToUse = SecurityPolicy::generateReferrerHeader(document->referrerPolicy(), url, referrer); + + // Prevent initial empty document load from triggering load events. + document->incrementLoadEventDelayCount(); + + auto frame = m_frame.loader().client().createFrame(url, name, ownerElement, referrerToUse, allowsScrolling, marginWidth, marginHeight); + + document->decrementLoadEventDelayCount(); if (!frame) { m_frame.loader().checkCallImplicitClose(); return nullptr; } - + // All new frames will have m_isComplete set to true at this point due to synchronously loading // an empty document in FrameLoader::init(). But many frames will now be starting an // asynchronous load of url, so we set m_isComplete to false and then check if the load is @@ -373,13 +345,13 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed. frame->loader().started(); - RenderObject* renderer = ownerElement.renderer(); - FrameView* view = frame->view(); - if (renderer && renderer->isWidget() && view) - toRenderWidget(renderer)->setWidget(view); - + auto* renderer = ownerElement.renderer(); + auto* view = frame->view(); + if (is<RenderWidget>(renderer) && view) + downcast<RenderWidget>(*renderer).setWidget(view); + m_frame.loader().checkCallImplicitClose(); - + // Some loads are performed synchronously (e.g., about:blank and loads // cancelled by returning a null ResourceRequest from requestFromDelegate). // In these cases, the synchronous load would have finished @@ -395,36 +367,24 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U return frame.get(); } -bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins) +bool SubframeLoader::allowPlugins() { - return m_frame.loader().client().allowPlugins(m_frame.settings().arePluginsEnabled()); + return m_frame.settings().arePluginsEnabled(); } -bool SubframeLoader::shouldUsePlugin(const URL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback) +bool SubframeLoader::shouldUsePlugin(const URL& url, const String& mimeType, bool hasFallback, bool& useFallback) { if (m_frame.loader().client().shouldAlwaysUsePluginDocument(mimeType)) { useFallback = false; return true; } - // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that - // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. - if (m_frame.page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { - String pluginName = m_frame.page()->pluginData().pluginNameForMimeType(mimeType); - if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) - return true; - } - - ObjectContentType objectType = m_frame.loader().client().objectContentType(url, mimeType, shouldPreferPlugInsForImages); + ObjectContentType objectType = m_frame.loader().client().objectContentType(url, mimeType); // If an object's content can't be handled and it has no fallback, let // it be handled as a plugin to show the broken plugin icon. - useFallback = objectType == ObjectContentNone && hasFallback; - return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; -} + useFallback = objectType == ObjectContentType::None && hasFallback; -Document* SubframeLoader::document() const -{ - return m_frame.document(); + return objectType == ObjectContentType::None || objectType == ObjectContentType::PlugIn; } bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) @@ -432,7 +392,9 @@ bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL if (useFallback) return false; - RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject(); + auto& document = pluginElement.document(); + auto* renderer = pluginElement.renderEmbeddedObject(); + // FIXME: This code should not depend on renderer! if (!renderer) return false; @@ -440,17 +402,19 @@ bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL pluginElement.subframeLoaderWillCreatePlugIn(url); IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight())); - bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually(); + bool loadManually = is<PluginDocument>(document) && !m_containsPlugins && downcast<PluginDocument>(document).shouldLoadPluginManually(); #if PLATFORM(IOS) // On iOS, we only tell the plugin to be in full page mode if the containing plugin document is the top level document. - if (document()->ownerElement()) + if (document.ownerElement()) loadManually = false; #endif - WeakPtr<RenderWidget> weakRenderer = renderer->createWeakPtr(); - // createPlugin *may* cause this renderer to disappear from underneath. - RefPtr<Widget> widget = m_frame.loader().client().createPlugin(contentSize, &pluginElement, url, paramNames, paramValues, mimeType, loadManually); + auto weakRenderer = renderer->createWeakPtr(); + + auto widget = m_frame.loader().client().createPlugin(contentSize, pluginElement, url, paramNames, paramValues, mimeType, loadManually); + + // The call to createPlugin *may* cause this renderer to disappear from underneath. if (!weakRenderer) return false; @@ -460,13 +424,9 @@ bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL return false; } - pluginElement.subframeLoaderDidCreatePlugIn(widget.get()); - renderer->setWidget(widget); + pluginElement.subframeLoaderDidCreatePlugIn(*widget); + renderer->setWidget(WTFMove(widget)); m_containsPlugins = true; - -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - pluginElement.setNeedsStyleRecalc(SyntheticStyleChange); -#endif return true; } @@ -476,4 +436,9 @@ URL SubframeLoader::completeURL(const String& url) const return m_frame.document()->completeURL(url); } +bool SubframeLoader::shouldConvertInvalidURLsToBlank() const +{ + return m_frame.settings().shouldConvertInvalidURLsToBlank(); +} + } // namespace WebCore diff --git a/Source/WebCore/loader/SubframeLoader.h b/Source/WebCore/loader/SubframeLoader.h index 1932ae102..12bf7717e 100644 --- a/Source/WebCore/loader/SubframeLoader.h +++ b/Source/WebCore/loader/SubframeLoader.h @@ -12,7 +12,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. * @@ -28,12 +28,10 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SubframeLoader_h -#define SubframeLoader_h +#pragma once #include "FrameLoaderTypes.h" #include <wtf/Forward.h> -#include <wtf/HashMap.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> #include <wtf/text/WTFString.h> @@ -53,45 +51,39 @@ class Widget; // This is a slight misnomer. It handles the higher level logic of loading both subframes and plugins. class SubframeLoader { - WTF_MAKE_NONCOPYABLE(SubframeLoader); + WTF_MAKE_NONCOPYABLE(SubframeLoader); WTF_MAKE_FAST_ALLOCATED; public: explicit SubframeLoader(Frame&); void clear(); - bool requestFrame(HTMLFrameOwnerElement&, const String& url, const AtomicString& frameName, bool lockHistory = true, bool lockBackForwardList = true); + bool requestFrame(HTMLFrameOwnerElement&, const String& url, const AtomicString& frameName, LockHistory = LockHistory::Yes, LockBackForwardList = LockBackForwardList::Yes); bool requestObject(HTMLPlugInImageElement&, const String& url, const AtomicString& frameName, const String& serviceType, const Vector<String>& paramNames, const Vector<String>& paramValues); -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - PassRefPtr<Widget> loadMediaPlayerProxyPlugin(HTMLMediaElement&, const URL&, const Vector<String>& paramNames, const Vector<String>& paramValues); -#endif + RefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement&, const Vector<String>& paramNames, const Vector<String>& paramValues); - PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement&, const Vector<String>& paramNames, const Vector<String>& paramValues); - - bool allowPlugins(ReasonForCallingAllowPlugins); + WEBCORE_EXPORT bool allowPlugins(); bool containsPlugins() const { return m_containsPlugins; } - bool resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages); + bool resourceWillUsePlugin(const String& url, const String& mimeType); private: bool requestPlugin(HTMLPlugInImageElement&, const URL&, const String& serviceType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback); - Frame* loadOrRedirectSubframe(HTMLFrameOwnerElement&, const URL&, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList); + Frame* loadOrRedirectSubframe(HTMLFrameOwnerElement&, const URL&, const AtomicString& frameName, LockHistory, LockBackForwardList); Frame* loadSubframe(HTMLFrameOwnerElement&, const URL&, const String& name, const String& referrer); bool loadPlugin(HTMLPlugInImageElement&, const URL&, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback); - bool shouldUsePlugin(const URL&, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback); - bool pluginIsLoadable(HTMLPlugInImageElement&, const URL&, const String& mimeType); + bool shouldUsePlugin(const URL&, const String& mimeType, bool hasFallback, bool& useFallback); + bool pluginIsLoadable(const URL&, const String& mimeType); + + URL completeURL(const String&) const; - Document* document() const; + bool shouldConvertInvalidURLsToBlank() const; bool m_containsPlugins; Frame& m_frame; - - URL completeURL(const String&) const; }; } // namespace WebCore - -#endif // SubframeLoader_h diff --git a/Source/WebCore/loader/SubresourceLoader.cpp b/Source/WebCore/loader/SubresourceLoader.cpp index 0f17264e0..dc6f5d77a 100644 --- a/Source/WebCore/loader/SubresourceLoader.cpp +++ b/Source/WebCore/loader/SubresourceLoader.cpp @@ -1,18 +1,18 @@ /* - * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple 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. + * 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * documentation and/or other materials provided with the distribution. + * 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. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -29,51 +29,68 @@ #include "config.h" #include "SubresourceLoader.h" +#include "CachedRawResource.h" #include "CachedResourceLoader.h" +#include "CrossOriginAccessControl.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" #include "Document.h" #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" #include "Logging.h" +#include "MainFrame.h" #include "MemoryCache.h" #include "Page.h" -#include "PageActivityAssertionToken.h" -#include "ResourceBuffer.h" +#include "ResourceLoadObserver.h" +#include "ResourceTiming.h" +#include "RuntimeEnabledFeatures.h" #include <wtf/Ref.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/StdLibExtras.h> #include <wtf/text/CString.h> #if PLATFORM(IOS) -#include <RuntimeApplicationChecksIOS.h> +#include <RuntimeApplicationChecks.h> +#endif + +#if ENABLE(CONTENT_EXTENSIONS) +#include "ResourceLoadInfo.h" +#endif + +#if USE(QUICK_LOOK) +#include "QuickLook.h" #endif namespace WebCore { DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader")); -SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource) +SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader& cachedResourceLoader, const CachedResource& resource) : m_cachedResourceLoader(cachedResourceLoader) , m_resource(resource) { - m_cachedResourceLoader->incrementRequestCount(m_resource); + m_cachedResourceLoader.incrementRequestCount(m_resource); } SubresourceLoader::RequestCountTracker::~RequestCountTracker() { - m_cachedResourceLoader->decrementRequestCount(m_resource); + m_cachedResourceLoader.decrementRequestCount(m_resource); } -SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options) +SubresourceLoader::SubresourceLoader(Frame& frame, CachedResource& resource, const ResourceLoaderOptions& options) : ResourceLoader(frame, options) - , m_resource(resource) + , m_resource(&resource) , m_loadingMultipartContent(false) , m_state(Uninitialized) - , m_requestCountTracker(adoptPtr(new RequestCountTracker(frame->document()->cachedResourceLoader(), resource))) + , m_requestCountTracker(std::in_place, frame.document()->cachedResourceLoader(), resource) { #ifndef NDEBUG subresourceLoaderCounter.increment(); #endif +#if ENABLE(CONTENT_EXTENSIONS) + m_resourceType = toResourceType(resource.type()); +#endif } SubresourceLoader::~SubresourceLoader() @@ -85,11 +102,11 @@ SubresourceLoader::~SubresourceLoader() #endif } -PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) +RefPtr<SubresourceLoader> SubresourceLoader::create(Frame& frame, CachedResource& resource, const ResourceRequest& request, const ResourceLoaderOptions& options) { RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options))); #if PLATFORM(IOS) - if (!applicationIsWebProcess()) { + if (!IOSApplication::isWebProcess()) { // On iOS, do not invoke synchronous resource load delegates while resource load scheduling // is disabled to avoid re-entering style selection from a different thread (see <rdar://problem/9121719>). // FIXME: This should be fixed for all ports in <https://bugs.webkit.org/show_bug.cgi?id=56647>. @@ -99,13 +116,13 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedReso #endif if (!subloader->init(request)) return nullptr; - return subloader.release(); + return subloader; } #if PLATFORM(IOS) bool SubresourceLoader::startLoading() { - ASSERT(!applicationIsWebProcess()); + ASSERT(!IOSApplication::isWebProcess()); if (!init(m_iOSOriginalRequest)) return false; m_iOSOriginalRequest = ResourceRequest(); @@ -135,6 +152,12 @@ bool SubresourceLoader::init(const ResourceRequest& request) ASSERT(!reachedTerminalState()); m_state = Initialized; m_documentLoader->addSubresourceLoader(this); + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=155633. + // SubresourceLoader could use the document origin as a default and set PotentiallyCrossOriginEnabled requests accordingly. + // This would simplify resource loader users as they would only need to set fetch mode to Cors. + m_origin = m_resource->origin(); + return true; } @@ -143,70 +166,157 @@ bool SubresourceLoader::isSubresourceLoader() return true; } -void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) +void SubresourceLoader::willSendRequestInternal(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) { // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it. URL previousURL = request().url(); - Ref<SubresourceLoader> protect(*this); + Ref<SubresourceLoader> protectedThis(*this); + + if (!newRequest.url().isValid()) { + cancel(cannotShowURLError()); + return; + } + + if (newRequest.requester() != ResourceRequestBase::Requester::Main) + ResourceLoadObserver::sharedObserver().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse); ASSERT(!newRequest.isNull()); if (!redirectResponse.isNull()) { + if (options().redirect != FetchOptions::Redirect::Follow) { + if (options().redirect == FetchOptions::Redirect::Error) { + cancel(); + return; + } + + ResourceResponse opaqueRedirectedResponse; + opaqueRedirectedResponse.setURL(redirectResponse.url()); + opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect); + m_resource->responseReceived(opaqueRedirectedResponse); + didFinishLoading(currentTime()); + return; + } else if (m_redirectCount++ >= options().maxRedirectCount) { + cancel(ResourceError(String(), 0, request().url(), ASCIILiteral("Too many redirections"), ResourceError::Type::General)); + return; + } + // CachedResources are keyed off their original request URL. // Requesting the same original URL a second time can redirect to a unique second resource. // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource. // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds. if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) { newRequest.makeUnconditional(); - memoryCache()->revalidationFailed(m_resource); + MemoryCache::singleton().revalidationFailed(*m_resource); + if (m_frame && m_frame->page()) + m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes); } - - if (!m_documentLoader->cachedResourceLoader().canRequest(m_resource->type(), newRequest.url(), options())) { + + if (!m_documentLoader->cachedResourceLoader().updateRequestAfterRedirection(m_resource->type(), newRequest, options())) { cancel(); return; } - if (m_resource->type() == CachedResource::ImageResource && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) { + + String errorDescription; + if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest, errorDescription)) { + String errorMessage = "Cross-origin redirection to " + newRequest.url().string() + " denied by Cross-Origin Resource Sharing policy: " + errorDescription; + if (m_frame && m_frame->document()) + m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorMessage); + cancel(ResourceError(String(), 0, request().url(), errorMessage, ResourceError::Type::AccessControl)); + return; + } + + if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) { cancel(); return; } - m_resource->willSendRequest(newRequest, redirectResponse); + m_loadTiming.addRedirect(redirectResponse.url(), newRequest.url()); + m_resource->redirectReceived(newRequest, redirectResponse); } if (newRequest.isNull() || reachedTerminalState()) return; - ResourceLoader::willSendRequest(newRequest, redirectResponse); - if (newRequest.isNull()) + ResourceLoader::willSendRequestInternal(newRequest, redirectResponse); + + if (reachedTerminalState()) + return; + + if (newRequest.isNull()) { cancel(); + return; + } + + if (m_resource->type() == CachedResource::MainResource && !redirectResponse.isNull()) + m_documentLoader->willContinueMainResourceLoadAfterRedirect(newRequest); } void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { ASSERT(m_state == Initialized); - Ref<SubresourceLoader> protect(*this); + Ref<SubresourceLoader> protectedThis(*this); m_resource->didSendData(bytesSent, totalBytesToBeSent); } +#if USE(QUICK_LOOK) + +bool SubresourceLoader::shouldCreateQuickLookHandleForResponse(const ResourceResponse& response) const +{ + if (m_resource->type() != CachedResource::MainResource) + return false; + + if (m_quickLookHandle) + return false; + + return QuickLookHandle::shouldCreateForMIMEType(response.mimeType()); +} + +#endif + void SubresourceLoader::didReceiveResponse(const ResourceResponse& response) { ASSERT(!response.isNull()); ASSERT(m_state == Initialized); +#if USE(QUICK_LOOK) + if (shouldCreateQuickLookHandleForResponse(response)) { + m_quickLookHandle = QuickLookHandle::create(*this, response); + return; + } +#endif + + // We want redirect responses to be processed through willSendRequestInternal. The only exception is redirection with no Location headers. + ASSERT(response.httpStatusCode() < 300 || response.httpStatusCode() >= 400 || response.httpStatusCode() == 304 || !response.httpHeaderField(HTTPHeaderName::Location)); + // Reference the object in this method since the additional processing can do // anything including removing the last reference to this object; one example of this is 3266216. - Ref<SubresourceLoader> protect(*this); + Ref<SubresourceLoader> protectedThis(*this); + + if (shouldIncludeCertificateInfo()) + response.includeCertificateInfo(); if (m_resource->resourceToRevalidate()) { if (response.httpStatusCode() == 304) { // 304 Not modified / Use local copy // Existing resource is ok, just use it updating the expiration time. m_resource->setResponse(response); - memoryCache()->revalidationSucceeded(m_resource, response); + MemoryCache::singleton().revalidationSucceeded(*m_resource, response); + if (m_frame && m_frame->page()) + m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes); if (!reachedTerminalState()) ResourceLoader::didReceiveResponse(response); return; } // Did not get 304 response, continue as a regular resource load. - memoryCache()->revalidationFailed(m_resource); + MemoryCache::singleton().revalidationFailed(*m_resource); + if (m_frame && m_frame->page()) + m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes); + } + + String errorDescription; + if (!checkResponseCrossOriginAccessControl(response, errorDescription)) { + if (m_frame && m_frame->document()) + m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorDescription); + cancel(ResourceError(String(), 0, request().url(), errorDescription, ResourceError::Type::AccessControl)); + return; } m_resource->responseReceived(response); @@ -223,21 +333,20 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response) m_loadingMultipartContent = true; // We don't count multiParts in a CachedResourceLoader's request count - m_requestCountTracker.clear(); + m_requestCountTracker = std::nullopt; if (!m_resource->isImage()) { cancel(); return; } } - RefPtr<ResourceBuffer> buffer = resourceData(); + auto* buffer = resourceData(); if (m_loadingMultipartContent && buffer && buffer->size()) { // The resource data will change as the next part is loaded, so we need to make a copy. - RefPtr<ResourceBuffer> copiedData = ResourceBuffer::create(buffer->data(), buffer->size()); - m_resource->finishLoading(copiedData.get()); + m_resource->finishLoading(buffer->copy().ptr()); clearResourceData(); - // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. - // After the first multipart section is complete, signal to delegates that this load is "finished" + // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. + // After the first multipart section is complete, signal to delegates that this load is "finished" m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this); didFinishLoadingOnePart(0); } @@ -247,15 +356,29 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response) void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType) { - didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType); +#if USE(QUICK_LOOK) + if (auto quickLookHandle = m_quickLookHandle.get()) { + if (quickLookHandle->didReceiveData(data, length)) + return; + } +#endif + + didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType); } -void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType) +void SubresourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { - didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType); +#if USE(QUICK_LOOK) + if (auto quickLookHandle = m_quickLookHandle.get()) { + if (quickLookHandle->didReceiveBuffer(buffer.get())) + return; + } +#endif + + didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType); } -void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType) +void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) return; @@ -264,14 +387,13 @@ void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, Pas ASSERT(m_state == Initialized); // Reference the object in this method since the additional processing can do // anything including removing the last reference to this object; one example of this is 3266216. - Ref<SubresourceLoader> protect(*this); - RefPtr<SharedBuffer> buffer = prpBuffer; - - ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType); + Ref<SubresourceLoader> protectedThis(*this); + + ResourceLoader::didReceiveDataOrBuffer(data, length, buffer.copyRef(), encodedDataLength, dataPayloadType); if (!m_loadingMultipartContent) { - if (ResourceBuffer* resourceData = this->resourceData()) - m_resource->addDataBuffer(resourceData); + if (auto* resourceData = this->resourceData()) + m_resource->addDataBuffer(*resourceData); else m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length); } @@ -288,31 +410,151 @@ bool SubresourceLoader::checkForHTTPStatusCodeError() return true; } +static void logResourceLoaded(Frame* frame, CachedResource::Type type) +{ + if (!frame || !frame->page()) + return; + + String resourceType; + switch (type) { + case CachedResource::MainResource: + resourceType = DiagnosticLoggingKeys::mainResourceKey(); + break; + case CachedResource::ImageResource: + resourceType = DiagnosticLoggingKeys::imageKey(); + break; +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif + case CachedResource::CSSStyleSheet: + resourceType = DiagnosticLoggingKeys::styleSheetKey(); + break; + case CachedResource::Script: + resourceType = DiagnosticLoggingKeys::scriptKey(); + break; + case CachedResource::FontResource: +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif + resourceType = DiagnosticLoggingKeys::fontKey(); + break; + case CachedResource::MediaResource: + case CachedResource::RawResource: + resourceType = DiagnosticLoggingKeys::rawKey(); + break; + case CachedResource::SVGDocumentResource: + resourceType = DiagnosticLoggingKeys::svgDocumentKey(); + break; +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + case CachedResource::LinkSubresource: +#endif +#if ENABLE(VIDEO_TRACK) + case CachedResource::TextTrackResource: +#endif + resourceType = DiagnosticLoggingKeys::otherKey(); + break; + } + frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceLoadedKey(), resourceType, ShouldSample::Yes); +} + +bool SubresourceLoader::checkResponseCrossOriginAccessControl(const ResourceResponse& response, String& errorDescription) +{ + if (!m_resource->isCrossOrigin() || options().mode != FetchOptions::Mode::Cors) + return true; + + ASSERT(m_origin); + return passesAccessControlCheck(response, options().allowCredentials, *m_origin, errorDescription); +} + +bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest, String& errorMessage) +{ + bool crossOriginFlag = m_resource->isCrossOrigin(); + bool isNextRequestCrossOrigin = m_origin && !m_origin->canRequest(newRequest.url()); + + if (isNextRequestCrossOrigin) + m_resource->setCrossOrigin(); + + ASSERT(options().mode != FetchOptions::Mode::SameOrigin || !m_resource->isCrossOrigin()); + + if (options().mode != FetchOptions::Mode::Cors) + return true; + + // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 8 & 9. + if (m_resource->isCrossOrigin() && !isValidCrossOriginRedirectionURL(newRequest.url())) { + errorMessage = ASCIILiteral("URL is either a non-HTTP URL or contains credentials."); + return false; + } + + ASSERT(m_origin); + if (crossOriginFlag && !passesAccessControlCheck(redirectResponse, options().allowCredentials, *m_origin, errorMessage)) + return false; + + bool redirectingToNewOrigin = false; + if (m_resource->isCrossOrigin()) { + if (!crossOriginFlag && isNextRequestCrossOrigin) + redirectingToNewOrigin = true; + else + redirectingToNewOrigin = !protocolHostAndPortAreEqual(previousRequest.url(), newRequest.url()); + } + + // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 10. + if (crossOriginFlag && redirectingToNewOrigin) + m_origin = SecurityOrigin::createUnique(); + + if (redirectingToNewOrigin) { + cleanRedirectedRequestForAccessControl(newRequest); + updateRequestForAccessControl(newRequest, *m_origin, options().allowCredentials); + } + + return true; +} + void SubresourceLoader::didFinishLoading(double finishTime) { +#if USE(QUICK_LOOK) + if (auto quickLookHandle = m_quickLookHandle.get()) { + if (quickLookHandle->didFinishLoading()) + return; + } +#endif + if (m_state != Initialized) return; ASSERT(!reachedTerminalState()); ASSERT(!m_resource->resourceToRevalidate()); - ASSERT(!m_resource->errorOccurred()); + // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion. + ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError || !m_resource->isLoading()); LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data()); + logResourceLoaded(m_frame.get(), m_resource->type()); - Ref<SubresourceLoader> protect(*this); + Ref<SubresourceLoader> protectedThis(*this); + CachedResourceHandle<CachedResource> protectResource(m_resource); -#if PLATFORM(IOS) - if (resourceData()) - resourceData()->setShouldUsePurgeableMemory(true); + // FIXME: <https://webkit.org/b/168351> [Resource Timing] Gather timing information with reliable responseEnd time + // The finishTime that is passed in is from the NetworkProcess and is more accurate. + // However, all other load times are generated from the web process or offsets. + // Mixing times from different processes can cause the finish time to be earlier than + // the response received time due to inter-process communication lag. This could be solved + // by gathering NetworkLoadTiming information at completion time instead of at + // didReceiveResponse time. + UNUSED_PARAM(finishTime); + MonotonicTime responseEndTime = MonotonicTime::now(); + m_loadTiming.setResponseEnd(responseEndTime); + +#if ENABLE(WEB_TIMING) + reportResourceTiming(); #endif - CachedResourceHandle<CachedResource> protectResource(m_resource); + m_state = Finishing; - m_resource->setLoadFinishTime(finishTime); + m_resource->setLoadFinishTime(responseEndTime.secondsSinceEpoch().seconds()); // FIXME: Users of the loadFinishTime should use the LoadTiming struct instead. m_resource->finishLoading(resourceData()); if (wasCancelled()) return; m_resource->finish(); ASSERT(!reachedTerminalState()); - didFinishLoadingOnePart(finishTime); + didFinishLoadingOnePart(responseEndTime.secondsSinceEpoch().seconds()); notifyDone(); if (reachedTerminalState()) return; @@ -321,19 +563,24 @@ void SubresourceLoader::didFinishLoading(double finishTime) void SubresourceLoader::didFail(const ResourceError& error) { +#if USE(QUICK_LOOK) + if (auto quickLookHandle = m_quickLookHandle.get()) + quickLookHandle->didFail(); +#endif + if (m_state != Initialized) return; ASSERT(!reachedTerminalState()); LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data()); - Ref<SubresourceLoader> protect(*this); + Ref<SubresourceLoader> protectedThis(*this); CachedResourceHandle<CachedResource> protectResource(m_resource); m_state = Finishing; if (m_resource->resourceToRevalidate()) - memoryCache()->revalidationFailed(m_resource); + MemoryCache::singleton().revalidationFailed(*m_resource); m_resource->setResourceError(error); if (!m_resource->isPreloaded()) - memoryCache()->remove(m_resource); + MemoryCache::singleton().remove(*m_resource); m_resource->error(CachedResource::LoadError); cleanupForError(error); notifyDone(); @@ -356,16 +603,17 @@ void SubresourceLoader::willCancel(const ResourceError& error) ASSERT(!reachedTerminalState()); LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data()); - Ref<SubresourceLoader> protect(*this); + Ref<SubresourceLoader> protectedThis(*this); #if PLATFORM(IOS) m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing; #else m_state = Finishing; #endif + auto& memoryCache = MemoryCache::singleton(); if (m_resource->resourceToRevalidate()) - memoryCache()->revalidationFailed(m_resource); + memoryCache.revalidationFailed(*m_resource); m_resource->setResourceError(error); - memoryCache()->remove(m_resource); + memoryCache.remove(*m_resource); } void SubresourceLoader::didCancel(const ResourceError&) @@ -377,16 +625,23 @@ void SubresourceLoader::didCancel(const ResourceError&) notifyDone(); } +void SubresourceLoader::didRetrieveDerivedDataFromCache(const String& type, SharedBuffer& buffer) +{ + if (m_state != Initialized) + return; + m_resource->didRetrieveDerivedDataFromCache(type, buffer); +} + void SubresourceLoader::notifyDone() { if (reachedTerminalState()) return; - m_requestCountTracker.clear(); + m_requestCountTracker = std::nullopt; #if PLATFORM(IOS) - m_documentLoader->cachedResourceLoader().loadDone(m_resource, m_state != CancelledWhileInitializing); + m_documentLoader->cachedResourceLoader().loadDone(m_state != CancelledWhileInitializing); #else - m_documentLoader->cachedResourceLoader().loadDone(m_resource); + m_documentLoader->cachedResourceLoader().loadDone(); #endif if (reachedTerminalState()) return; @@ -402,8 +657,39 @@ void SubresourceLoader::releaseResources() if (m_state != Uninitialized) #endif m_resource->clearLoader(); - m_resource = 0; + m_resource = nullptr; ResourceLoader::releaseResources(); } +#if ENABLE(WEB_TIMING) +void SubresourceLoader::reportResourceTiming() +{ + if (!RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) + return; + + if (!ResourceTimingInformation::shouldAddResourceTiming(*m_resource)) + return; + + Document* document = m_documentLoader->cachedResourceLoader().document(); + if (!document) + return; + + SecurityOrigin& origin = m_origin ? *m_origin : document->securityOrigin(); + ResourceTiming resourceTiming = ResourceTiming::fromLoad(*m_resource, m_resource->initiatorName(), m_loadTiming, origin); + + // Worker resources loaded here are all CachedRawResources loaded through WorkerThreadableLoader. + // Pass the ResourceTiming information on so that WorkerThreadableLoader may add them to the + // Worker's Performance object. + if (options().initiatorContext == InitiatorContext::Worker) { + ASSERT(m_origin); + ASSERT(is<CachedRawResource>(m_resource)); + downcast<CachedRawResource>(*m_resource).finishedTimingForWorkerLoad(WTFMove(resourceTiming)); + return; + } + + ASSERT(options().initiatorContext == InitiatorContext::Document); + m_documentLoader->cachedResourceLoader().resourceTimingInformation().addResourceTiming(*m_resource, *document, WTFMove(resourceTiming)); +} +#endif + } diff --git a/Source/WebCore/loader/SubresourceLoader.h b/Source/WebCore/loader/SubresourceLoader.h index 58a7817bb..929bc563a 100644 --- a/Source/WebCore/loader/SubresourceLoader.h +++ b/Source/WebCore/loader/SubresourceLoader.h @@ -1,18 +1,18 @@ /* - * Copyright (C) 2005, 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2005-2017 Apple 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. + * 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * documentation and/or other materials provided with the distribution. + * 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. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SubresourceLoader_h -#define SubresourceLoader_h +#pragma once #include "FrameLoaderTypes.h" #include "ResourceLoader.h" @@ -39,57 +38,78 @@ namespace WebCore { class CachedResource; class CachedResourceLoader; class Document; -class PageActivityAssertionToken; class ResourceRequest; +class SecurityOrigin; -class SubresourceLoader : public ResourceLoader { +class SubresourceLoader final : public ResourceLoader { public: - static PassRefPtr<SubresourceLoader> create(Frame*, CachedResource*, const ResourceRequest&, const ResourceLoaderOptions&); + WEBCORE_EXPORT static RefPtr<SubresourceLoader> create(Frame&, CachedResource&, const ResourceRequest&, const ResourceLoaderOptions&); virtual ~SubresourceLoader(); void cancelIfNotFinishing(); - virtual bool isSubresourceLoader() override; + bool isSubresourceLoader() override; CachedResource* cachedResource(); + SecurityOrigin* origin() { return m_origin.get(); } #if PLATFORM(IOS) - virtual bool startLoading() override; + bool startLoading() override; // FIXME: What is an "iOS" original request? Why is it necessary? - virtual const ResourceRequest& iOSOriginalRequest() const override { return m_iOSOriginalRequest; } + const ResourceRequest& iOSOriginalRequest() const override { return m_iOSOriginalRequest; } #endif -private: - SubresourceLoader(Frame*, CachedResource*, const ResourceLoaderOptions&); - - virtual bool init(const ResourceRequest&) override; + unsigned redirectCount() const { return m_redirectCount; } - virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse) override; - virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; - virtual void didReceiveResponse(const ResourceResponse&) override; - virtual void didReceiveData(const char*, unsigned, long long encodedDataLength, DataPayloadType) override; - virtual void didReceiveBuffer(PassRefPtr<SharedBuffer>, long long encodedDataLength, DataPayloadType) override; - virtual void didFinishLoading(double finishTime) override; - virtual void didFail(const ResourceError&) override; - virtual void willCancel(const ResourceError&) override; - virtual void didCancel(const ResourceError&) override; +private: + SubresourceLoader(Frame&, CachedResource&, const ResourceLoaderOptions&); + + bool init(const ResourceRequest&) override; + + void willSendRequestInternal(ResourceRequest&, const ResourceResponse& redirectResponse) override; + void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + void didReceiveResponse(const ResourceResponse&) override; + void didReceiveData(const char*, unsigned, long long encodedDataLength, DataPayloadType) override; + void didReceiveBuffer(Ref<SharedBuffer>&&, long long encodedDataLength, DataPayloadType) override; + void didFinishLoading(double finishTime) override; + void didFail(const ResourceError&) override; + void willCancel(const ResourceError&) override; + void didCancel(const ResourceError&) override; + void didRetrieveDerivedDataFromCache(const String& type, SharedBuffer&) override; + +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + NSCachedURLResponse *willCacheResponse(ResourceHandle*, NSCachedURLResponse*) override; +#endif +#if PLATFORM(COCOA) && USE(CFURLCONNECTION) + CFCachedURLResponseRef willCacheResponse(ResourceHandle*, CFCachedURLResponseRef) override; +#endif #if USE(NETWORK_CFDATA_ARRAY_CALLBACK) - virtual bool supportsDataArray() override { return true; } - virtual void didReceiveDataArray(CFArrayRef) override; + bool supportsDataArray() override { return true; } + void didReceiveDataArray(CFArrayRef) override; #endif - virtual void releaseResources() override; + void releaseResources() override; #if USE(SOUP) - virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override; + char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override; #endif bool checkForHTTPStatusCodeError(); + bool checkResponseCrossOriginAccessControl(const ResourceResponse&, String&); + bool checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse&, ResourceRequest& newRequest, String&); - void didReceiveDataOrBuffer(const char*, int, PassRefPtr<SharedBuffer>, long long encodedDataLength, DataPayloadType); + void didReceiveDataOrBuffer(const char*, int, RefPtr<SharedBuffer>&&, long long encodedDataLength, DataPayloadType); void notifyDone(); +#if ENABLE(WEB_TIMING) + void reportResourceTiming(); +#endif + +#if USE(QUICK_LOOK) + bool shouldCreateQuickLookHandleForResponse(const ResourceResponse&) const; +#endif + enum SubresourceLoaderState { Uninitialized, Initialized, @@ -100,12 +120,15 @@ private: }; class RequestCountTracker { +#if !COMPILER(MSVC) + WTF_MAKE_FAST_ALLOCATED; +#endif public: - RequestCountTracker(CachedResourceLoader*, CachedResource*); + RequestCountTracker(CachedResourceLoader&, const CachedResource&); ~RequestCountTracker(); private: - CachedResourceLoader* m_cachedResourceLoader; - CachedResource* m_resource; + CachedResourceLoader& m_cachedResourceLoader; + const CachedResource& m_resource; }; #if PLATFORM(IOS) @@ -114,9 +137,9 @@ private: CachedResource* m_resource; bool m_loadingMultipartContent; SubresourceLoaderState m_state; - OwnPtr<RequestCountTracker> m_requestCountTracker; + std::optional<RequestCountTracker> m_requestCountTracker; + RefPtr<SecurityOrigin> m_origin; + unsigned m_redirectCount { 0 }; }; } - -#endif // SubresourceLoader_h diff --git a/Source/WebCore/loader/SubstituteData.h b/Source/WebCore/loader/SubstituteData.h index 2959de7a8..f10135159 100644 --- a/Source/WebCore/loader/SubstituteData.h +++ b/Source/WebCore/loader/SubstituteData.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,56 +23,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SubstituteData_h -#define SubstituteData_h +#pragma once -#include "URL.h" +#include "ResourceResponse.h" #include "SharedBuffer.h" -#include <wtf/PassRefPtr.h> +#include "URL.h" #include <wtf/RefPtr.h> namespace WebCore { class SubstituteData { public: + enum class SessionHistoryVisibility { + Visible, + Hidden, + }; + SubstituteData() - : m_shouldRevealToSessionHistory(false) { } - SubstituteData(PassRefPtr<SharedBuffer> content, const String& mimeType, - const String& textEncoding, const URL& failingURL, - const URL& responseURL = URL(), bool shouldRevealToSessionHistory = false) - : m_content(content) - , m_mimeType(mimeType) - , m_textEncoding(textEncoding) + SubstituteData(RefPtr<SharedBuffer>&& content, const URL& failingURL, const ResourceResponse& response, SessionHistoryVisibility shouldRevealToSessionHistory) + : m_content(WTFMove(content)) , m_failingURL(failingURL) - , m_responseURL(responseURL) + , m_response(response) , m_shouldRevealToSessionHistory(shouldRevealToSessionHistory) { } - static const bool ShouldRevealToSessionHistory = true; - - bool isValid() const { return m_content != 0; } - bool shouldRevealToSessionHistory() const { return m_shouldRevealToSessionHistory; } + bool isValid() const { return m_content != nullptr; } + bool shouldRevealToSessionHistory() const { return m_shouldRevealToSessionHistory == SessionHistoryVisibility::Visible; } const SharedBuffer* content() const { return m_content.get(); } - const String& mimeType() const { return m_mimeType; } - const String& textEncoding() const { return m_textEncoding; } + const String& mimeType() const { return m_response.mimeType(); } + const String& textEncoding() const { return m_response.textEncodingName(); } const URL& failingURL() const { return m_failingURL; } - const URL& responseURL() const { return m_responseURL; } + const ResourceResponse& response() const { return m_response; } private: RefPtr<SharedBuffer> m_content; - String m_mimeType; - String m_textEncoding; URL m_failingURL; - URL m_responseURL; - bool m_shouldRevealToSessionHistory; + ResourceResponse m_response; + SessionHistoryVisibility m_shouldRevealToSessionHistory { SessionHistoryVisibility::Hidden }; }; -} - -#endif // SubstituteData_h - +} // namespace WebCore diff --git a/Source/WebCore/loader/SubstituteResource.h b/Source/WebCore/loader/SubstituteResource.h index 98c87a235..0e8dd35e2 100644 --- a/Source/WebCore/loader/SubstituteResource.h +++ b/Source/WebCore/loader/SubstituteResource.h @@ -23,17 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SubstituteResource_h -#define SubstituteResource_h +#pragma once -#include <wtf/RefCounted.h> - -#include "URL.h" +#include "ResourceLoader.h" #include "ResourceResponse.h" #include "SharedBuffer.h" -#include <wtf/RefPtr.h> - namespace WebCore { class SubstituteResource : public RefCounted<SubstituteResource> { @@ -42,23 +37,22 @@ public: const URL& url() const { return m_url; } const ResourceResponse& response() const { return m_response; } - SharedBuffer* data() const { return m_data.get(); } + SharedBuffer& data() const { return static_reference_cast<SharedBuffer>(m_data); } + + virtual void deliver(ResourceLoader& loader) { loader.deliverResponseAndData(m_response, m_data->copy()); } protected: - SubstituteResource(const URL& url, const ResourceResponse& response, PassRefPtr<SharedBuffer> data) + SubstituteResource(const URL& url, const ResourceResponse& response, Ref<SharedBuffer>&& data) : m_url(url) , m_response(response) - , m_data(data) + , m_data(WTFMove(data)) { - ASSERT(m_data); } - + private: URL m_url; ResourceResponse m_response; - RefPtr<SharedBuffer> m_data; + Ref<SharedBuffer> m_data; }; - -} -#endif // SubstituteResource_h +} // namespace WebCore diff --git a/Source/WebCore/loader/TextResourceDecoder.cpp b/Source/WebCore/loader/TextResourceDecoder.cpp index 5105ae0bb..fe94b7f81 100644 --- a/Source/WebCore/loader/TextResourceDecoder.cpp +++ b/Source/WebCore/loader/TextResourceDecoder.cpp @@ -23,9 +23,9 @@ #include "config.h" #include "TextResourceDecoder.h" -#include "DOMImplementation.h" #include "HTMLMetaCharsetParser.h" #include "HTMLNames.h" +#include "MIMETypeRegistry.h" #include "TextCodec.h" #include "TextEncoding.h" #include "TextEncodingDetector.h" @@ -302,11 +302,11 @@ breakBreak: TextResourceDecoder::ContentType TextResourceDecoder::determineContentType(const String& mimeType) { - if (equalIgnoringCase(mimeType, "text/css")) + if (equalLettersIgnoringASCIICase(mimeType, "text/css")) return CSS; - if (equalIgnoringCase(mimeType, "text/html")) + if (equalLettersIgnoringASCIICase(mimeType, "text/html")) return HTML; - if (DOMImplementation::isXMLMIMEType(mimeType)) + if (MIMETypeRegistry::isXMLMIMEType(mimeType)) return XML; return PlainText; } @@ -326,7 +326,7 @@ TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncod : m_contentType(determineContentType(mimeType)) , m_encoding(defaultEncoding(m_contentType, specifiedDefaultEncoding)) , m_source(DefaultEncoding) - , m_hintEncoding(0) + , m_hintEncoding(nullptr) , m_checkedForBOM(false) , m_checkedForCSSCharset(false) , m_checkedForHeadCharset(false) @@ -355,10 +355,15 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour else m_encoding = encoding; - m_codec.clear(); + m_codec = nullptr; m_source = source; } +bool TextResourceDecoder::hasEqualEncodingForCharset(const String& charset) const +{ + return defaultEncoding(m_contentType, charset) == m_encoding; +} + // Returns the position of the encoding string. static int findXMLEncoding(const char* str, int len, int& encodingLength) { @@ -475,6 +480,8 @@ bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& int encodingNameLength = pos - dataStart; ++pos; + if (pos == dataEnd) + return false; if (*pos == ';') setEncoding(findTextEncoding(dataStart, encodingNameLength), EncodingFromCSSCharset); @@ -658,9 +665,15 @@ String TextResourceDecoder::flush() String result = m_codec->decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError); m_buffer.clear(); - m_codec.clear(); + m_codec = nullptr; m_checkedForBOM = false; // Skip BOM again when re-decoding. return result; } +String TextResourceDecoder::decodeAndFlush(const char* data, size_t length) +{ + String decoded = decode(data, length); + return decoded + flush(); +} + } diff --git a/Source/WebCore/loader/TextResourceDecoder.h b/Source/WebCore/loader/TextResourceDecoder.h index e51f1f7a8..98ec9257a 100644 --- a/Source/WebCore/loader/TextResourceDecoder.h +++ b/Source/WebCore/loader/TextResourceDecoder.h @@ -20,8 +20,7 @@ */ -#ifndef TextResourceDecoder_h -#define TextResourceDecoder_h +#pragma once #include "TextEncoding.h" #include <wtf/RefCounted.h> @@ -43,17 +42,21 @@ public: EncodingFromParentFrame }; - static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding(), bool usesEncodingDetector = false) + static Ref<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding(), bool usesEncodingDetector = false) { - return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding, usesEncodingDetector)); + return adoptRef(*new TextResourceDecoder(mimeType, defaultEncoding, usesEncodingDetector)); } - ~TextResourceDecoder(); + WEBCORE_EXPORT ~TextResourceDecoder(); void setEncoding(const TextEncoding&, EncodingSource); const TextEncoding& encoding() const { return m_encoding; } - String decode(const char* data, size_t length); - String flush(); + bool hasEqualEncodingForCharset(const String&) const; + + WEBCORE_EXPORT String decode(const char* data, size_t length); + WEBCORE_EXPORT String flush(); + + WEBCORE_EXPORT String decodeAndFlush(const char* data, size_t length); void setHintEncoding(const TextResourceDecoder* hintDecoder) { @@ -67,7 +70,7 @@ public: bool sawError() const { return m_sawError; } private: - TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding, bool usesEncodingDetector); + WEBCORE_EXPORT TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding, bool usesEncodingDetector); enum ContentType { PlainText, HTML, XML, CSS }; // PlainText only checks for BOM. static ContentType determineContentType(const String& mimeType); @@ -82,7 +85,7 @@ private: ContentType m_contentType; TextEncoding m_encoding; - OwnPtr<TextCodec> m_codec; + std::unique_ptr<TextCodec> m_codec; EncodingSource m_source; const char* m_hintEncoding; Vector<char> m_buffer; @@ -96,6 +99,4 @@ private: std::unique_ptr<HTMLMetaCharsetParser> m_charsetParser; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/TextTrackLoader.cpp b/Source/WebCore/loader/TextTrackLoader.cpp index 35d788cfb..866096875 100644 --- a/Source/WebCore/loader/TextTrackLoader.cpp +++ b/Source/WebCore/loader/TextTrackLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -35,10 +36,8 @@ #include "CrossOriginAccessControl.h" #include "Document.h" #include "Logging.h" -#include "ResourceBuffer.h" -#include "ScriptCallStack.h" -#include "SecurityOrigin.h" -#include "TextTrackCue.h" +#include "SharedBuffer.h" +#include "VTTCue.h" #include "WebVTTParser.h" namespace WebCore { @@ -46,7 +45,7 @@ namespace WebCore { TextTrackLoader::TextTrackLoader(TextTrackLoaderClient& client, ScriptExecutionContext* context) : m_client(client) , m_scriptExecutionContext(context) - , m_cueLoadTimer(this, &TextTrackLoader::cueLoadTimerFired) + , m_cueLoadTimer(*this, &TextTrackLoader::cueLoadTimerFired) , m_state(Idle) , m_parseOffset(0) , m_newCuesAvailable(false) @@ -56,13 +55,11 @@ TextTrackLoader::TextTrackLoader(TextTrackLoaderClient& client, ScriptExecutionC TextTrackLoader::~TextTrackLoader() { if (m_resource) - m_resource->removeClient(this); + m_resource->removeClient(*this); } -void TextTrackLoader::cueLoadTimerFired(Timer<TextTrackLoader>* timer) +void TextTrackLoader::cueLoadTimerFired() { - ASSERT_UNUSED(timer, timer == &m_cueLoadTimer); - if (m_newCuesAvailable) { m_newCuesAvailable = false; m_client.newCuesAvailable(this); @@ -75,24 +72,24 @@ void TextTrackLoader::cueLoadTimerFired(Timer<TextTrackLoader>* timer) void TextTrackLoader::cancelLoad() { if (m_resource) { - m_resource->removeClient(this); + m_resource->removeClient(*this); m_resource = nullptr; } } -void TextTrackLoader::processNewCueData(CachedResource* resource) +void TextTrackLoader::processNewCueData(CachedResource& resource) { - ASSERT(m_resource == resource); - - if (m_state == Failed || !resource->resourceBuffer()) + ASSERT_UNUSED(resource, m_resource == &resource); + + if (m_state == Failed || !m_resource->resourceBuffer()) return; - - ResourceBuffer* buffer = resource->resourceBuffer(); + + auto* buffer = m_resource->resourceBuffer(); if (m_parseOffset == buffer->size()) return; if (!m_cueParser) - m_cueParser = WebVTTParser::create(this, m_scriptExecutionContext); + m_cueParser = std::make_unique<WebVTTParser>(static_cast<WebVTTParserClient*>(this), m_scriptExecutionContext); const char* data; unsigned length; @@ -104,77 +101,67 @@ void TextTrackLoader::processNewCueData(CachedResource* resource) } // FIXME: This is a very unusual pattern, no other CachedResourceClient does this. Refactor to use notifyFinished() instead. -void TextTrackLoader::deprecatedDidReceiveCachedResource(CachedResource* resource) +void TextTrackLoader::deprecatedDidReceiveCachedResource(CachedResource& resource) { - ASSERT(m_resource == resource); - - if (!resource->resourceBuffer()) + ASSERT_UNUSED(resource, m_resource == &resource); + + if (!m_resource->resourceBuffer()) return; - - processNewCueData(resource); + + processNewCueData(*m_resource); } void TextTrackLoader::corsPolicyPreventedLoad() { - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin text track load denied by Cross-Origin Resource Sharing policy."))); - Document* document = toDocument(m_scriptExecutionContext); - document->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, consoleMessage); + static NeverDestroyed<String> consoleMessage(ASCIILiteral("Cross-origin text track load denied by Cross-Origin Resource Sharing policy.")); + Document* document = downcast<Document>(m_scriptExecutionContext); + document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage); m_state = Failed; } -void TextTrackLoader::notifyFinished(CachedResource* resource) +void TextTrackLoader::notifyFinished(CachedResource& resource) { - ASSERT(m_resource == resource); - - Document* document = toDocument(m_scriptExecutionContext); - if (!m_crossOriginMode.isNull() - && !document->securityOrigin()->canRequest(resource->response().url()) - && !resource->passesAccessControlCheck(document->securityOrigin())) { + ASSERT_UNUSED(resource, m_resource == &resource); + if (m_resource->resourceError().isAccessControl()) corsPolicyPreventedLoad(); - } if (m_state != Failed) { - processNewCueData(resource); + processNewCueData(*m_resource); if (m_cueParser) m_cueParser->fileFinished(); if (m_state != Failed) - m_state = resource->errorOccurred() ? Failed : Finished; + m_state = m_resource->errorOccurred() ? Failed : Finished; } + if (m_state == Finished && m_cueParser) + m_cueParser->flush(); + if (!m_cueLoadTimer.isActive()) m_cueLoadTimer.startOneShot(0); - + cancelLoad(); } -bool TextTrackLoader::load(const URL& url, const String& crossOriginMode) +bool TextTrackLoader::load(const URL& url, const String& crossOriginMode, bool isInitiatingElementInUserAgentShadowTree) { cancelLoad(); - ASSERT(m_scriptExecutionContext->isDocument()); - Document* document = toDocument(m_scriptExecutionContext); - CachedResourceRequest cueRequest(ResourceRequest(document->completeURL(url))); - - if (!crossOriginMode.isNull()) { - m_crossOriginMode = crossOriginMode; - StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials; - updateRequestForAccessControl(cueRequest.mutableResourceRequest(), document->securityOrigin(), allowCredentials); - } else { - // Cross-origin resources that are not suitably CORS-enabled may not load. - if (!document->securityOrigin()->canRequest(url)) { - corsPolicyPreventedLoad(); - return false; - } - } + ASSERT(is<Document>(m_scriptExecutionContext)); + Document* document = downcast<Document>(m_scriptExecutionContext); - CachedResourceLoader* cachedResourceLoader = document->cachedResourceLoader(); - m_resource = cachedResourceLoader->requestTextTrack(cueRequest); + ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); + options.contentSecurityPolicyImposition = isInitiatingElementInUserAgentShadowTree ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck; + + CachedResourceRequest cueRequest(ResourceRequest(document->completeURL(url)), options); + cueRequest.setAsPotentiallyCrossOrigin(crossOriginMode, *document); + + m_resource = document->cachedResourceLoader().requestTextTrack(WTFMove(cueRequest)); if (!m_resource) return false; - m_resource->addClient(this); - + m_resource->addClient(*this); + return true; } @@ -187,12 +174,10 @@ void TextTrackLoader::newCuesParsed() m_cueLoadTimer.startOneShot(0); } -#if ENABLE(WEBVTT_REGIONS) void TextTrackLoader::newRegionsParsed() { m_client.newRegionsAvailable(this); } -#endif void TextTrackLoader::fileFailedToParse() { @@ -212,24 +197,19 @@ void TextTrackLoader::getNewCues(Vector<RefPtr<TextTrackCue>>& outputCues) if (m_cueParser) { Vector<RefPtr<WebVTTCueData>> newCues; m_cueParser->getNewCues(newCues); - for (size_t i = 0; i < newCues.size(); ++i) { - RefPtr<WebVTTCueData> data = newCues[i]; - RefPtr<TextTrackCue> cue = TextTrackCue::create(*m_scriptExecutionContext, data->startTime(), data->endTime(), data->content()); - cue->setId(data->id()); - cue->setCueSettings(data->settings()); - outputCues.append(cue); - } + + for (auto& cueData : newCues) + outputCues.append(VTTCue::create(*m_scriptExecutionContext, *cueData)); } } -#if ENABLE(WEBVTT_REGIONS) -void TextTrackLoader::getNewRegions(Vector<RefPtr<TextTrackRegion>>& outputRegions) +void TextTrackLoader::getNewRegions(Vector<RefPtr<VTTRegion>>& outputRegions) { ASSERT(m_cueParser); if (m_cueParser) m_cueParser->getNewRegions(outputRegions); } -#endif + } #endif diff --git a/Source/WebCore/loader/TextTrackLoader.h b/Source/WebCore/loader/TextTrackLoader.h index cd62ca2f7..337c8e131 100644 --- a/Source/WebCore/loader/TextTrackLoader.h +++ b/Source/WebCore/loader/TextTrackLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackLoader_h -#define TextTrackLoader_h +#pragma once #if ENABLE(VIDEO_TRACK) @@ -32,7 +31,7 @@ #include "CachedResourceHandle.h" #include "Timer.h" #include "WebVTTParser.h" -#include <wtf/OwnPtr.h> +#include <memory> namespace WebCore { @@ -47,54 +46,42 @@ public: virtual void newCuesAvailable(TextTrackLoader*) = 0; virtual void cueLoadingCompleted(TextTrackLoader*, bool loadingFailed) = 0; -#if ENABLE(WEBVTT_REGIONS) virtual void newRegionsAvailable(TextTrackLoader*) = 0; -#endif }; class TextTrackLoader : public CachedResourceClient, private WebVTTParserClient { WTF_MAKE_NONCOPYABLE(TextTrackLoader); WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<TextTrackLoader> create(TextTrackLoaderClient& client, ScriptExecutionContext* context) - { - return adoptPtr(new TextTrackLoader(client, context)); - } + TextTrackLoader(TextTrackLoaderClient&, ScriptExecutionContext*); virtual ~TextTrackLoader(); - bool load(const URL&, const String& crossOriginMode); + bool load(const URL&, const String& crossOriginMode, bool isInitiatingElementInUserAgentShadowTree); void cancelLoad(); void getNewCues(Vector<RefPtr<TextTrackCue>>& outputCues); -#if ENABLE(WEBVTT_REGIONS) - void getNewRegions(Vector<RefPtr<TextTrackRegion>>& outputRegions); -#endif + void getNewRegions(Vector<RefPtr<VTTRegion>>& outputRegions); private: // CachedResourceClient - virtual void notifyFinished(CachedResource*); - virtual void deprecatedDidReceiveCachedResource(CachedResource*); - + void notifyFinished(CachedResource&) override; + void deprecatedDidReceiveCachedResource(CachedResource&) override; + // WebVTTParserClient - virtual void newCuesParsed(); -#if ENABLE(WEBVTT_REGIONS) - virtual void newRegionsParsed(); -#endif - virtual void fileFailedToParse(); - - TextTrackLoader(TextTrackLoaderClient&, ScriptExecutionContext*); - - void processNewCueData(CachedResource*); - void cueLoadTimerFired(Timer<TextTrackLoader>*); + void newCuesParsed() override; + void newRegionsParsed() override; + void fileFailedToParse() override; + + void processNewCueData(CachedResource&); + void cueLoadTimerFired(); void corsPolicyPreventedLoad(); enum State { Idle, Loading, Finished, Failed }; - + TextTrackLoaderClient& m_client; - OwnPtr<WebVTTParser> m_cueParser; + std::unique_ptr<WebVTTParser> m_cueParser; CachedResourceHandle<CachedTextTrack> m_resource; ScriptExecutionContext* m_scriptExecutionContext; - Timer<TextTrackLoader> m_cueLoadTimer; - String m_crossOriginMode; + Timer m_cueLoadTimer; State m_state; unsigned m_parseOffset; bool m_newCuesAvailable; @@ -102,5 +89,4 @@ private: } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/loader/ThreadableLoader.cpp b/Source/WebCore/loader/ThreadableLoader.cpp index 8a3a98576..a316da496 100644 --- a/Source/WebCore/loader/ThreadableLoader.cpp +++ b/Source/WebCore/loader/ThreadableLoader.cpp @@ -31,10 +31,11 @@ #include "config.h" #include "ThreadableLoader.h" +#include "CachedResourceRequestInitiators.h" #include "Document.h" #include "DocumentThreadableLoader.h" +#include "ResourceError.h" #include "ScriptExecutionContext.h" -#include "SecurityOrigin.h" #include "WorkerGlobalScope.h" #include "WorkerRunLoop.h" #include "WorkerThreadableLoader.h" @@ -42,36 +43,75 @@ namespace WebCore { ThreadableLoaderOptions::ThreadableLoaderOptions() - : preflightPolicy(ConsiderPreflight) - , crossOriginRequestPolicy(DenyCrossOriginRequests) { + mode = FetchOptions::Mode::SameOrigin; } ThreadableLoaderOptions::~ThreadableLoaderOptions() { } -PassRefPtr<ThreadableLoader> ThreadableLoader::create(ScriptExecutionContext* context, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options) +ThreadableLoaderOptions::ThreadableLoaderOptions(const ResourceLoaderOptions& baseOptions, PreflightPolicy preflightPolicy, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, String&& initiator, ResponseFilteringPolicy filteringPolicy) + : ResourceLoaderOptions(baseOptions) + , preflightPolicy(preflightPolicy) + , contentSecurityPolicyEnforcement(contentSecurityPolicyEnforcement) + , initiator(WTFMove(initiator)) + , filteringPolicy(filteringPolicy) { - ASSERT(client); - ASSERT(context); +} + +RefPtr<ThreadableLoader> ThreadableLoader::create(ScriptExecutionContext& context, ThreadableLoaderClient& client, ResourceRequest&& request, const ThreadableLoaderOptions& options, String&& referrer) +{ + if (is<WorkerGlobalScope>(context)) + return WorkerThreadableLoader::create(downcast<WorkerGlobalScope>(context), client, WorkerRunLoop::defaultMode(), WTFMove(request), options, referrer); - if (context->isWorkerGlobalScope()) - return WorkerThreadableLoader::create(static_cast<WorkerGlobalScope*>(context), client, WorkerRunLoop::defaultMode(), request, options); + return DocumentThreadableLoader::create(downcast<Document>(context), client, WTFMove(request), options, WTFMove(referrer)); +} - return DocumentThreadableLoader::create(toDocument(context), client, request, options); +void ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext& context, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options) +{ + if (is<WorkerGlobalScope>(context)) + WorkerThreadableLoader::loadResourceSynchronously(downcast<WorkerGlobalScope>(context), WTFMove(request), client, options); + else + DocumentThreadableLoader::loadResourceSynchronously(downcast<Document>(context), WTFMove(request), client, options); + context.didLoadResourceSynchronously(); } -void ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext* context, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options) +void ThreadableLoader::logError(ScriptExecutionContext& context, const ResourceError& error, const String& initiator) { - ASSERT(context); + // FIXME: extend centralized logging to other clients than fetch, at least XHR and EventSource. + if (initiator != cachedResourceRequestInitiators().fetch) + return; - if (context->isWorkerGlobalScope()) { - WorkerThreadableLoader::loadResourceSynchronously(static_cast<WorkerGlobalScope*>(context), request, client, options); + if (error.isCancellation()) return; + + // FIXME: Some errors are returned with null URLs. This leads to poor console messages. We should do better for these errors. + if (error.failingURL().isNull()) + return; + + // We further reduce logging to some errors. + // FIXME: Log more errors when making so do not make some layout tests flaky. + if (error.domain() != errorDomainWebKitInternal && !error.isAccessControl()) + return; + + const char* messageStart; + if (initiator == cachedResourceRequestInitiators().fetch) + messageStart = "Fetch API cannot load "; + else + messageStart = "Cannot load "; + + const char* messageMiddle = ". "; + String description = error.localizedDescription(); + if (description.isEmpty()) { + // FIXME: We should probably define default description error message for all error types. + if (error.isAccessControl()) + messageMiddle = ASCIILiteral(" due to access control checks."); + else + messageMiddle = "."; } - DocumentThreadableLoader::loadResourceSynchronously(toDocument(context), request, client, options); + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString(messageStart, error.failingURL().string(), messageMiddle, description)); } } // namespace WebCore diff --git a/Source/WebCore/loader/ThreadableLoader.h b/Source/WebCore/loader/ThreadableLoader.h index 0c9a0f253..e924b240a 100644 --- a/Source/WebCore/loader/ThreadableLoader.h +++ b/Source/WebCore/loader/ThreadableLoader.h @@ -28,18 +28,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ThreadableLoader_h -#define ThreadableLoader_h +#pragma once #include "ResourceLoaderOptions.h" #include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> -#include <wtf/Vector.h> - -#if ENABLE(RESOURCE_TIMING) #include <wtf/text/AtomicString.h> -#endif namespace WebCore { @@ -47,14 +41,7 @@ namespace WebCore { class ResourceRequest; class ResourceResponse; class ScriptExecutionContext; - class SecurityOrigin; class ThreadableLoaderClient; - - enum CrossOriginRequestPolicy { - DenyCrossOriginRequests, - UseAccessControl, - AllowCrossOriginRequests - }; enum PreflightPolicy { ConsiderPreflight, @@ -62,30 +49,43 @@ namespace WebCore { PreventPreflight }; - struct ThreadableLoaderOptions : public ResourceLoaderOptions { + enum class ContentSecurityPolicyEnforcement { + DoNotEnforce, + EnforceChildSrcDirective, + EnforceConnectSrcDirective, + EnforceScriptSrcDirective, + }; + + enum class ResponseFilteringPolicy { + Enable, + Disable, + }; + + struct ThreadableLoaderOptions : ResourceLoaderOptions { ThreadableLoaderOptions(); + ThreadableLoaderOptions(const ResourceLoaderOptions&, PreflightPolicy, ContentSecurityPolicyEnforcement, String&& initiator, ResponseFilteringPolicy); ~ThreadableLoaderOptions(); - PreflightPolicy preflightPolicy; // If AccessControl is used, how to determine if a preflight is needed. - CrossOriginRequestPolicy crossOriginRequestPolicy; - RefPtr<SecurityOrigin> securityOrigin; -#if ENABLE(RESOURCE_TIMING) - AtomicString initiator; -#endif + PreflightPolicy preflightPolicy { ConsiderPreflight }; + ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement { ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective }; + String initiator; // This cannot be an AtomicString, as isolatedCopy() wouldn't create an object that's safe for passing to another thread. + ResponseFilteringPolicy filteringPolicy { ResponseFilteringPolicy::Disable }; }; - // Useful for doing loader operations from any thread (not threadsafe, + // Useful for doing loader operations from any thread (not threadsafe, // just able to run on threads other than the main thread). class ThreadableLoader { WTF_MAKE_NONCOPYABLE(ThreadableLoader); public: - static void loadResourceSynchronously(ScriptExecutionContext*, const ResourceRequest&, ThreadableLoaderClient&, const ThreadableLoaderOptions&); - static PassRefPtr<ThreadableLoader> create(ScriptExecutionContext*, ThreadableLoaderClient*, const ResourceRequest&, const ThreadableLoaderOptions&); + static void loadResourceSynchronously(ScriptExecutionContext&, ResourceRequest&&, ThreadableLoaderClient&, const ThreadableLoaderOptions&); + static RefPtr<ThreadableLoader> create(ScriptExecutionContext&, ThreadableLoaderClient&, ResourceRequest&&, const ThreadableLoaderOptions&, String&& referrer = String()); virtual void cancel() = 0; void ref() { refThreadableLoader(); } void deref() { derefThreadableLoader(); } + static void logError(ScriptExecutionContext&, const ResourceError&, const String&); + protected: ThreadableLoader() { } virtual ~ThreadableLoader() { } @@ -94,5 +94,3 @@ namespace WebCore { }; } // namespace WebCore - -#endif // ThreadableLoader_h diff --git a/Source/WebCore/loader/ThreadableLoaderClient.h b/Source/WebCore/loader/ThreadableLoaderClient.h index e8936528d..a51c3883d 100644 --- a/Source/WebCore/loader/ThreadableLoaderClient.h +++ b/Source/WebCore/loader/ThreadableLoaderClient.h @@ -28,14 +28,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ThreadableLoaderClient_h -#define ThreadableLoaderClient_h - +#pragma once namespace WebCore { class ResourceError; class ResourceResponse; + class ResourceTiming; class ThreadableLoaderClient { WTF_MAKE_NONCOPYABLE(ThreadableLoaderClient); WTF_MAKE_FAST_ALLOCATED; @@ -46,10 +45,10 @@ namespace WebCore { virtual void didReceiveData(const char*, int /*dataLength*/) { } virtual void didFinishLoading(unsigned long /*identifier*/, double /*finishTime*/) { } virtual void didFail(const ResourceError&) { } - virtual void didFailAccessControlCheck(const ResourceError& error) { didFail(error); } - virtual void didFailRedirectCheck() { } - virtual bool isDocumentThreadableLoaderClient() { return false; } +#if ENABLE(WEB_TIMING) + virtual void didFinishTiming(const ResourceTiming&) { } +#endif protected: ThreadableLoaderClient() { } @@ -57,5 +56,3 @@ namespace WebCore { }; } // namespace WebCore - -#endif // ThreadableLoaderClient_h diff --git a/Source/WebCore/loader/ThreadableLoaderClientWrapper.h b/Source/WebCore/loader/ThreadableLoaderClientWrapper.h index 715c5aa64..602b2f295 100644 --- a/Source/WebCore/loader/ThreadableLoaderClientWrapper.h +++ b/Source/WebCore/loader/ThreadableLoaderClientWrapper.h @@ -28,27 +28,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ThreadableLoaderClientWrapper_h -#define ThreadableLoaderClientWrapper_h +#pragma once #include "ThreadableLoaderClient.h" #include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> +#include <wtf/Ref.h> #include <wtf/Threading.h> namespace WebCore { class ThreadableLoaderClientWrapper : public ThreadSafeRefCounted<ThreadableLoaderClientWrapper> { public: - static PassRefPtr<ThreadableLoaderClientWrapper> create(ThreadableLoaderClient* client) + static Ref<ThreadableLoaderClientWrapper> create(ThreadableLoaderClient& client, const String& initiator) { - return adoptRef(new ThreadableLoaderClientWrapper(client)); + return adoptRef(*new ThreadableLoaderClientWrapper(client, initiator)); } void clearClient() { m_done = true; - m_client = 0; + m_client = nullptr; } bool done() const @@ -88,37 +87,26 @@ public: m_client->didFail(error); } - void didFailAccessControlCheck(const ResourceError& error) - { - m_done = true; - if (m_client) - m_client->didFailAccessControlCheck(error); - } - - void didFailRedirectCheck() - { - m_done = true; - if (m_client) - m_client->didFailRedirectCheck(); - } - void didReceiveAuthenticationCancellation(unsigned long identifier, const ResourceResponse& response) { if (m_client) m_client->didReceiveResponse(identifier, response); } + const String& initiator() const { return m_initiator; } + protected: - explicit ThreadableLoaderClientWrapper(ThreadableLoaderClient* client) - : m_client(client) - , m_done(false) - { - } + explicit ThreadableLoaderClientWrapper(ThreadableLoaderClient&, const String&); ThreadableLoaderClient* m_client; - bool m_done; + String m_initiator; + bool m_done { false }; }; -} // namespace WebCore +inline ThreadableLoaderClientWrapper::ThreadableLoaderClientWrapper(ThreadableLoaderClient& client, const String& initiator) + : m_client(&client) + , m_initiator(initiator) +{ +} -#endif // ThreadableLoaderClientWrapper_h +} // namespace WebCore diff --git a/Source/WebCore/loader/WorkerThreadableLoader.cpp b/Source/WebCore/loader/WorkerThreadableLoader.cpp index d10ef7ed2..31ea9bb83 100644 --- a/Source/WebCore/loader/WorkerThreadableLoader.cpp +++ b/Source/WebCore/loader/WorkerThreadableLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -31,29 +32,30 @@ #include "config.h" #include "WorkerThreadableLoader.h" +#include "ContentSecurityPolicy.h" #include "Document.h" #include "DocumentThreadableLoader.h" -#include "CrossThreadTask.h" +#include "Performance.h" #include "ResourceError.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "ResourceTiming.h" #include "SecurityOrigin.h" #include "ThreadableLoader.h" #include "WorkerGlobalScope.h" #include "WorkerLoaderProxy.h" #include "WorkerThread.h" #include <wtf/MainThread.h> -#include <wtf/OwnPtr.h> #include <wtf/Vector.h> namespace WebCore { static const char loadResourceSynchronouslyMode[] = "loadResourceSynchronouslyMode"; -WorkerThreadableLoader::WorkerThreadableLoader(WorkerGlobalScope* workerGlobalScope, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, const ThreadableLoaderOptions& options) +WorkerThreadableLoader::WorkerThreadableLoader(WorkerGlobalScope& workerGlobalScope, ThreadableLoaderClient& client, const String& taskMode, ResourceRequest&& request, const ThreadableLoaderOptions& options, const String& referrer) : m_workerGlobalScope(workerGlobalScope) - , m_workerClientWrapper(ThreadableLoaderClientWrapper::create(client)) - , m_bridge(*(new MainThreadBridge(m_workerClientWrapper, m_workerGlobalScope->thread()->workerLoaderProxy(), taskMode, request, options, workerGlobalScope->url().strippedForUseAsReferrer()))) + , m_workerClientWrapper(ThreadableLoaderClientWrapper::create(client, options.initiator)) + , m_bridge(*new MainThreadBridge(m_workerClientWrapper.get(), workerGlobalScope.thread().workerLoaderProxy(), taskMode, WTFMove(request), options, referrer.isEmpty() ? workerGlobalScope.url().strippedForUseAsReferrer() : referrer, workerGlobalScope.securityOrigin(), workerGlobalScope.contentSecurityPolicy())) { } @@ -62,18 +64,18 @@ WorkerThreadableLoader::~WorkerThreadableLoader() m_bridge.destroy(); } -void WorkerThreadableLoader::loadResourceSynchronously(WorkerGlobalScope* workerGlobalScope, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options) +void WorkerThreadableLoader::loadResourceSynchronously(WorkerGlobalScope& workerGlobalScope, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options) { - WorkerRunLoop& runLoop = workerGlobalScope->thread()->runLoop(); + WorkerRunLoop& runLoop = workerGlobalScope.thread().runLoop(); // Create a unique mode just for this synchronous resource load. String mode = loadResourceSynchronouslyMode; mode.append(String::number(runLoop.createUniqueId())); - RefPtr<WorkerThreadableLoader> loader = WorkerThreadableLoader::create(workerGlobalScope, &client, mode, request, options); + RefPtr<WorkerThreadableLoader> loader = WorkerThreadableLoader::create(workerGlobalScope, client, mode, WTFMove(request), options, String()); MessageQueueWaitResult result = MessageQueueMessageReceived; while (!loader->done() && result != MessageQueueTerminated) - result = runLoop.runInMode(workerGlobalScope, mode); + result = runLoop.runInMode(&workerGlobalScope, mode); if (!loader->done() && result == MessageQueueTerminated) loader->cancel(); @@ -84,41 +86,51 @@ void WorkerThreadableLoader::cancel() m_bridge.cancel(); } -WorkerThreadableLoader::MainThreadBridge::MainThreadBridge(PassRefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, WorkerLoaderProxy& loaderProxy, const String& taskMode, - const ResourceRequest& request, const ThreadableLoaderOptions& options, const String& outgoingReferrer) - : m_workerClientWrapper(workerClientWrapper) - , m_loaderProxy(loaderProxy) - , m_taskMode(taskMode.isolatedCopy()) -{ - ASSERT(m_workerClientWrapper.get()); - m_loaderProxy.postTaskToLoader( - createCallbackTask(&MainThreadBridge::mainThreadCreateLoader, - AllowCrossThreadAccess(this), request, options, outgoingReferrer)); -} +struct LoaderTaskOptions { + LoaderTaskOptions(const ThreadableLoaderOptions&, const String&, Ref<SecurityOrigin>&&); + ThreadableLoaderOptions options; + String referrer; + Ref<SecurityOrigin> origin; +}; -WorkerThreadableLoader::MainThreadBridge::~MainThreadBridge() +LoaderTaskOptions::LoaderTaskOptions(const ThreadableLoaderOptions& options, const String& referrer, Ref<SecurityOrigin>&& origin) + : options(options, options.preflightPolicy, options.contentSecurityPolicyEnforcement, options.initiator.isolatedCopy(), options.filteringPolicy) + , referrer(referrer.isolatedCopy()) + , origin(WTFMove(origin)) { } -void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ScriptExecutionContext* context, MainThreadBridge* thisPtr, PassOwnPtr<CrossThreadResourceRequestData> requestData, ThreadableLoaderOptions options, const String& outgoingReferrer) +WorkerThreadableLoader::MainThreadBridge::MainThreadBridge(ThreadableLoaderClientWrapper& workerClientWrapper, WorkerLoaderProxy& loaderProxy, const String& taskMode, + ResourceRequest&& request, const ThreadableLoaderOptions& options, const String& outgoingReferrer, + const SecurityOrigin* securityOrigin, const ContentSecurityPolicy* contentSecurityPolicy) + : m_workerClientWrapper(&workerClientWrapper) + , m_loaderProxy(loaderProxy) + , m_taskMode(taskMode.isolatedCopy()) { - ASSERT(isMainThread()); - Document* document = toDocument(context); - - OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData)); - request->setHTTPReferrer(outgoingReferrer); - // FIXME: If the a site requests a local resource, then this will return a non-zero value but the sync path - // will return a 0 value. Either this should return 0 or the other code path should do a callback with - // a failure. - thisPtr->m_mainThreadLoader = DocumentThreadableLoader::create(document, thisPtr, *request, options); - ASSERT(thisPtr->m_mainThreadLoader); -} + ASSERT(securityOrigin); + ASSERT(contentSecurityPolicy); -void WorkerThreadableLoader::MainThreadBridge::mainThreadDestroy(ScriptExecutionContext* context, MainThreadBridge* thisPtr) -{ - ASSERT(isMainThread()); - ASSERT_UNUSED(context, context->isDocument()); - delete thisPtr; + auto securityOriginCopy = securityOrigin->isolatedCopy(); + auto contentSecurityPolicyCopy = std::make_unique<ContentSecurityPolicy>(securityOriginCopy); + contentSecurityPolicyCopy->copyStateFrom(contentSecurityPolicy); + contentSecurityPolicyCopy->copyUpgradeInsecureRequestStateFrom(*contentSecurityPolicy); + + auto optionsCopy = std::make_unique<LoaderTaskOptions>(options, request.httpReferrer().isNull() ? outgoingReferrer : request.httpReferrer(), WTFMove(securityOriginCopy)); + + // All loads start out as Document. Inside WorkerThreadableLoader we upgrade this to a Worker load. + ASSERT(optionsCopy->options.initiatorContext == InitiatorContext::Document); + optionsCopy->options.initiatorContext = InitiatorContext::Worker; + + // Can we benefit from request being an r-value to create more efficiently its isolated copy? + m_loaderProxy.postTaskToLoader([this, request = request.isolatedCopy(), options = WTFMove(optionsCopy), contentSecurityPolicyCopy = WTFMove(contentSecurityPolicyCopy)](ScriptExecutionContext& context) mutable { + ASSERT(isMainThread()); + Document& document = downcast<Document>(context); + + // FIXME: If the site requests a local resource, then this will return a non-zero value but the sync path will return a 0 value. + // Either this should return 0 or the other code path should call a failure callback. + m_mainThreadLoader = DocumentThreadableLoader::create(document, *this, WTFMove(request), options->options, WTFMove(options->origin), WTFMove(contentSecurityPolicyCopy), WTFMove(options->referrer), DocumentThreadableLoader::ShouldLogError::No); + ASSERT(m_mainThreadLoader || m_loadingFinished); + }); } void WorkerThreadableLoader::MainThreadBridge::destroy() @@ -127,34 +139,35 @@ void WorkerThreadableLoader::MainThreadBridge::destroy() clearClientWrapper(); // "delete this" and m_mainThreadLoader::deref() on the worker object's thread. - m_loaderProxy.postTaskToLoader( - createCallbackTask(&MainThreadBridge::mainThreadDestroy, AllowCrossThreadAccess(this))); + m_loaderProxy.postTaskToLoader([self = std::unique_ptr<WorkerThreadableLoader::MainThreadBridge>(this)] (ScriptExecutionContext& context) { + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context.isDocument()); + }); } -void WorkerThreadableLoader::MainThreadBridge::mainThreadCancel(ScriptExecutionContext* context, MainThreadBridge* thisPtr) +void WorkerThreadableLoader::MainThreadBridge::cancel() { - ASSERT(isMainThread()); - ASSERT_UNUSED(context, context->isDocument()); + m_loaderProxy.postTaskToLoader([this] (ScriptExecutionContext& context) { + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context.isDocument()); - if (!thisPtr->m_mainThreadLoader) - return; - thisPtr->m_mainThreadLoader->cancel(); - thisPtr->m_mainThreadLoader = 0; -} + if (!m_mainThreadLoader) + return; + m_mainThreadLoader->cancel(); + m_mainThreadLoader = nullptr; + }); -void WorkerThreadableLoader::MainThreadBridge::cancel() -{ - m_loaderProxy.postTaskToLoader( - createCallbackTask(&MainThreadBridge::mainThreadCancel, AllowCrossThreadAccess(this))); - ThreadableLoaderClientWrapper* clientWrapper = m_workerClientWrapper.get(); - if (!clientWrapper->done()) { - // If the client hasn't reached a termination state, then transition it by sending a cancellation error. - // Note: no more client callbacks will be done after this method -- the clearClientWrapper() call ensures that. - ResourceError error(String(), 0, String(), String()); - error.setIsCancellation(true); - clientWrapper->didFail(error); + if (m_workerClientWrapper->done()) { + clearClientWrapper(); + return; } - clearClientWrapper(); + // Taking a ref of client wrapper as call to didFail may take out the last reference of it. + Ref<ThreadableLoaderClientWrapper> protectedWorkerClientWrapper(*m_workerClientWrapper); + // If the client hasn't reached a termination state, then transition it by sending a cancellation error. + // Note: no more client callbacks will be done after this method -- we clear the client wrapper to ensure that. + ResourceError error(ResourceError::Type::Cancellation); + protectedWorkerClientWrapper->didFail(error); + protectedWorkerClientWrapper->clearClient(); } void WorkerThreadableLoader::MainThreadBridge::clearClientWrapper() @@ -162,84 +175,64 @@ void WorkerThreadableLoader::MainThreadBridge::clearClientWrapper() m_workerClientWrapper->clearClient(); } -static void workerGlobalScopeDidSendData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) -{ - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - workerClientWrapper->didSendData(bytesSent, totalBytesToBeSent); -} - void WorkerThreadableLoader::MainThreadBridge::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidSendData, m_workerClientWrapper, bytesSent, totalBytesToBeSent), m_taskMode); -} - -static void workerGlobalScopeDidReceiveResponse(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long identifier, PassOwnPtr<CrossThreadResourceResponseData> responseData) -{ - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData)); - workerClientWrapper->didReceiveResponse(identifier, *response); + m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), bytesSent, totalBytesToBeSent] (ScriptExecutionContext& context) mutable { + ASSERT_UNUSED(context, context.isWorkerGlobalScope()); + protectedWorkerClientWrapper->didSendData(bytesSent, totalBytesToBeSent); + }, m_taskMode); } void WorkerThreadableLoader::MainThreadBridge::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) { - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidReceiveResponse, m_workerClientWrapper, identifier, response), m_taskMode); -} - -static void workerGlobalScopeDidReceiveData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<Vector<char>> vectorData) -{ - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - workerClientWrapper->didReceiveData(vectorData->data(), vectorData->size()); + m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), identifier, responseData = response.crossThreadData()] (ScriptExecutionContext& context) mutable { + ASSERT_UNUSED(context, context.isWorkerGlobalScope()); + protectedWorkerClientWrapper->didReceiveResponse(identifier, ResourceResponse::fromCrossThreadData(WTFMove(responseData))); + }, m_taskMode); } void WorkerThreadableLoader::MainThreadBridge::didReceiveData(const char* data, int dataLength) { - OwnPtr<Vector<char>> vector = adoptPtr(new Vector<char>(dataLength)); // needs to be an OwnPtr for usage with createCallbackTask. - memcpy(vector->data(), data, dataLength); - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidReceiveData, m_workerClientWrapper, vector.release()), m_taskMode); -} - -static void workerGlobalScopeDidFinishLoading(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long identifier, double finishTime) -{ - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - workerClientWrapper->didFinishLoading(identifier, finishTime); + Vector<char> vector(dataLength); + memcpy(vector.data(), data, dataLength); + m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), vector = WTFMove(vector)] (ScriptExecutionContext& context) mutable { + ASSERT_UNUSED(context, context.isWorkerGlobalScope()); + protectedWorkerClientWrapper->didReceiveData(vector.data(), vector.size()); + }, m_taskMode); } void WorkerThreadableLoader::MainThreadBridge::didFinishLoading(unsigned long identifier, double finishTime) { - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidFinishLoading, m_workerClientWrapper, identifier, finishTime), m_taskMode); -} - -static void workerGlobalScopeDidFail(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, const ResourceError& error) -{ - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - workerClientWrapper->didFail(error); + m_loadingFinished = true; + m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), identifier, finishTime] (ScriptExecutionContext& context) mutable { + ASSERT_UNUSED(context, context.isWorkerGlobalScope()); + protectedWorkerClientWrapper->didFinishLoading(identifier, finishTime); + }, m_taskMode); } void WorkerThreadableLoader::MainThreadBridge::didFail(const ResourceError& error) { - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidFail, m_workerClientWrapper, error), m_taskMode); -} + m_loadingFinished = true; + m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = Ref<ThreadableLoaderClientWrapper>(*m_workerClientWrapper), error = error.isolatedCopy()] (ScriptExecutionContext& context) mutable { + ASSERT(context.isWorkerGlobalScope()); -static void workerGlobalScopeDidFailAccessControlCheck(ScriptExecutionContext* context, PassRefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, const ResourceError& error) -{ - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - workerClientWrapper->didFailAccessControlCheck(error); -} + ThreadableLoader::logError(context, error, workerClientWrapper->initiator()); -void WorkerThreadableLoader::MainThreadBridge::didFailAccessControlCheck(const ResourceError& error) -{ - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidFailAccessControlCheck, m_workerClientWrapper, error), m_taskMode); + workerClientWrapper->didFail(error); + }, m_taskMode); } -static void workerGlobalScopeDidFailRedirectCheck(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper) +#if ENABLE(WEB_TIMING) +void WorkerThreadableLoader::MainThreadBridge::didFinishTiming(const ResourceTiming& resourceTiming) { - ASSERT_UNUSED(context, context->isWorkerGlobalScope()); - workerClientWrapper->didFailRedirectCheck(); -} + m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), resourceTiming = resourceTiming.isolatedCopy()] (ScriptExecutionContext& context) mutable { + ASSERT(context.isWorkerGlobalScope()); + ASSERT(!resourceTiming.initiator().isEmpty()); -void WorkerThreadableLoader::MainThreadBridge::didFailRedirectCheck() -{ - m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidFailRedirectCheck, m_workerClientWrapper), m_taskMode); + // No need to notify clients, just add the performance timing entry. + downcast<WorkerGlobalScope>(context).performance().addResourceTiming(WTFMove(resourceTiming)); + }, m_taskMode); } +#endif } // namespace WebCore diff --git a/Source/WebCore/loader/WorkerThreadableLoader.h b/Source/WebCore/loader/WorkerThreadableLoader.h index 4db0f4534..c0a570527 100644 --- a/Source/WebCore/loader/WorkerThreadableLoader.h +++ b/Source/WebCore/loader/WorkerThreadableLoader.h @@ -28,41 +28,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WorkerThreadableLoader_h -#define WorkerThreadableLoader_h +#pragma once #include "ThreadableLoader.h" #include "ThreadableLoaderClient.h" #include "ThreadableLoaderClientWrapper.h" - -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> #include <wtf/Threading.h> #include <wtf/text/WTFString.h> namespace WebCore { + class ContentSecurityPolicy; class ResourceError; class ResourceRequest; + class SecurityOrigin; class WorkerGlobalScope; class WorkerLoaderProxy; - struct CrossThreadResourceResponseData; - struct CrossThreadResourceRequestData; class WorkerThreadableLoader : public RefCounted<WorkerThreadableLoader>, public ThreadableLoader { WTF_MAKE_FAST_ALLOCATED; public: - static void loadResourceSynchronously(WorkerGlobalScope*, const ResourceRequest&, ThreadableLoaderClient&, const ThreadableLoaderOptions&); - static PassRefPtr<WorkerThreadableLoader> create(WorkerGlobalScope* workerGlobalScope, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, const ThreadableLoaderOptions& options) + static void loadResourceSynchronously(WorkerGlobalScope&, ResourceRequest&&, ThreadableLoaderClient&, const ThreadableLoaderOptions&); + static Ref<WorkerThreadableLoader> create(WorkerGlobalScope& workerGlobalScope, ThreadableLoaderClient& client, const String& taskMode, ResourceRequest&& request, const ThreadableLoaderOptions& options, const String& referrer) { - return adoptRef(new WorkerThreadableLoader(workerGlobalScope, client, taskMode, request, options)); + return adoptRef(*new WorkerThreadableLoader(workerGlobalScope, client, taskMode, WTFMove(request), options, referrer)); } ~WorkerThreadableLoader(); - virtual void cancel() override; + void cancel() override; bool done() const { return m_workerClientWrapper->done(); } @@ -70,8 +64,8 @@ namespace WebCore { using RefCounted<WorkerThreadableLoader>::deref; protected: - virtual void refThreadableLoader() override { ref(); } - virtual void derefThreadableLoader() override { deref(); } + void refThreadableLoader() override { ref(); } + void derefThreadableLoader() override { deref(); } private: // Creates a loader on the main thread and bridges communication between @@ -90,13 +84,12 @@ namespace WebCore { // case 2. xhr gets aborted and the worker context continues running. // The ThreadableLoaderClientWrapper has the underlying client cleared, so no more calls // go through it. All tasks posted from the worker object's thread to the worker context's - // thread do "ThreadableLoaderClientWrapper::ref" (automatically inside of the cross thread copy - // done in createCallbackTask), so the ThreadableLoaderClientWrapper instance is there until all - // tasks are executed. + // thread contain the RefPtr<ThreadableLoaderClientWrapper> object, so the + // ThreadableLoaderClientWrapper instance is there until all tasks are executed. class MainThreadBridge : public ThreadableLoaderClient { public: // All executed on the worker context's thread. - MainThreadBridge(PassRefPtr<ThreadableLoaderClientWrapper>, WorkerLoaderProxy&, const String& taskMode, const ResourceRequest&, const ThreadableLoaderOptions&, const String& outgoingReferrer); + MainThreadBridge(ThreadableLoaderClientWrapper&, WorkerLoaderProxy&, const String& taskMode, ResourceRequest&&, const ThreadableLoaderOptions&, const String& outgoingReferrer, const SecurityOrigin*, const ContentSecurityPolicy*); void cancel(); void destroy(); @@ -105,21 +98,19 @@ namespace WebCore { void clearClientWrapper(); // All executed on the main thread. - static void mainThreadDestroy(ScriptExecutionContext*, MainThreadBridge*); - ~MainThreadBridge(); - - static void mainThreadCreateLoader(ScriptExecutionContext*, MainThreadBridge*, PassOwnPtr<CrossThreadResourceRequestData>, ThreadableLoaderOptions, const String& outgoingReferrer); - static void mainThreadCancel(ScriptExecutionContext*, MainThreadBridge*); - virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; - virtual void didReceiveResponse(unsigned long identifier, const ResourceResponse&) override; - virtual void didReceiveData(const char*, int dataLength) override; - virtual void didFinishLoading(unsigned long identifier, double finishTime) override; - virtual void didFail(const ResourceError&) override; - virtual void didFailAccessControlCheck(const ResourceError&) override; - virtual void didFailRedirectCheck() override; + void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + void didReceiveResponse(unsigned long identifier, const ResourceResponse&) override; + void didReceiveData(const char*, int dataLength) override; + void didFinishLoading(unsigned long identifier, double finishTime) override; + void didFail(const ResourceError&) override; + +#if ENABLE(WEB_TIMING) + void didFinishTiming(const ResourceTiming&) override; +#endif // Only to be used on the main thread. RefPtr<ThreadableLoader> m_mainThreadLoader; + bool m_loadingFinished { false }; // ThreadableLoaderClientWrapper is to be used on the worker context thread. // The ref counting is done on either thread. @@ -132,13 +123,11 @@ namespace WebCore { String m_taskMode; }; - WorkerThreadableLoader(WorkerGlobalScope*, ThreadableLoaderClient*, const String& taskMode, const ResourceRequest&, const ThreadableLoaderOptions&); + WorkerThreadableLoader(WorkerGlobalScope&, ThreadableLoaderClient&, const String& taskMode, ResourceRequest&&, const ThreadableLoaderOptions&, const String& referrer); - RefPtr<WorkerGlobalScope> m_workerGlobalScope; - RefPtr<ThreadableLoaderClientWrapper> m_workerClientWrapper; + Ref<WorkerGlobalScope> m_workerGlobalScope; + Ref<ThreadableLoaderClientWrapper> m_workerClientWrapper; MainThreadBridge& m_bridge; }; } // namespace WebCore - -#endif // WorkerThreadableLoader_h diff --git a/Source/WebCore/loader/appcache/ApplicationCache.cpp b/Source/WebCore/loader/appcache/ApplicationCache.cpp index 983becc21..a95ed2cac 100644 --- a/Source/WebCore/loader/appcache/ApplicationCache.cpp +++ b/Source/WebCore/loader/appcache/ApplicationCache.cpp @@ -30,7 +30,6 @@ #include "ApplicationCacheResource.h" #include "ApplicationCacheStorage.h" #include "ResourceRequest.h" -#include "SecurityOrigin.h" #include <algorithm> #include <stdio.h> #include <wtf/text/CString.h> @@ -43,17 +42,13 @@ static inline bool fallbackURLLongerThan(const std::pair<URL, URL>& lhs, const s } ApplicationCache::ApplicationCache() - : m_group(0) - , m_manifest(0) - , m_estimatedSizeInStorage(0) - , m_storageID(0) { } ApplicationCache::~ApplicationCache() { - if (m_group && !m_group->isCopy()) - m_group->cacheDestroyed(this); + if (m_group) + m_group->cacheDestroyed(*this); } void ApplicationCache::setGroup(ApplicationCacheGroup* group) @@ -62,59 +57,41 @@ void ApplicationCache::setGroup(ApplicationCacheGroup* group) m_group = group; } -bool ApplicationCache::isComplete() const +bool ApplicationCache::isComplete() { - return !m_group->cacheIsBeingUpdated(this); + return m_group && m_group->cacheIsComplete(*this); } -void ApplicationCache::setManifestResource(PassRefPtr<ApplicationCacheResource> manifest) +void ApplicationCache::setManifestResource(Ref<ApplicationCacheResource>&& manifest) { - ASSERT(manifest); ASSERT(!m_manifest); ASSERT(manifest->type() & ApplicationCacheResource::Manifest); - - m_manifest = manifest.get(); - - addResource(manifest); + + m_manifest = manifest.ptr(); + + addResource(WTFMove(manifest)); } -void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource) +void ApplicationCache::addResource(Ref<ApplicationCacheResource>&& resource) { - ASSERT(resource); - - const String& url = resource->url(); - + auto& url = resource->url(); + + ASSERT(!URL(ParsedURLString, url).hasFragmentIdentifier()); ASSERT(!m_resources.contains(url)); - + if (m_storageID) { ASSERT(!resource->storageID()); ASSERT(resource->type() & ApplicationCacheResource::Master); - + // Add the resource to the storage. - cacheStorage().store(resource.get(), this); + m_group->storage().store(resource.ptr(), this); } m_estimatedSizeInStorage += resource->estimatedSizeInStorage(); - m_resources.set(url, resource); + m_resources.set(url, WTFMove(resource)); } -unsigned ApplicationCache::removeResource(const String& url) -{ - HashMap<String, RefPtr<ApplicationCacheResource>>::iterator it = m_resources.find(url); - if (it == m_resources.end()) - return 0; - - // The resource exists, get its type so we can return it. - unsigned type = it->value->type(); - - m_estimatedSizeInStorage -= it->value->estimatedSizeInStorage(); - - m_resources.remove(it); - - return type; -} - ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url) { ASSERT(!URL(ParsedURLString, url).hasFragmentIdentifier()); @@ -123,25 +100,17 @@ ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url) bool ApplicationCache::requestIsHTTPOrHTTPSGet(const ResourceRequest& request) { - if (!request.url().protocolIsInHTTPFamily()) - return false; - - if (!equalIgnoringCase(request.httpMethod(), "GET")) - return false; - - return true; -} + return request.url().protocolIsInHTTPFamily() && equalLettersIgnoringASCIICase(request.httpMethod(), "get"); +} ApplicationCacheResource* ApplicationCache::resourceForRequest(const ResourceRequest& request) { // We only care about HTTP/HTTPS GET requests. if (!requestIsHTTPOrHTTPSGet(request)) - return 0; + return nullptr; URL url(request.url()); - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); - + url.removeFragmentIdentifier(); return resourceForURL(url); } @@ -153,9 +122,8 @@ void ApplicationCache::setOnlineWhitelist(const Vector<URL>& onlineWhitelist) bool ApplicationCache::isURLInOnlineWhitelist(const URL& url) { - size_t whitelistSize = m_onlineWhitelist.size(); - for (size_t i = 0; i < whitelistSize; ++i) { - if (protocolHostAndPortAreEqual(url, m_onlineWhitelist[i]) && url.string().startsWith(m_onlineWhitelist[i].string())) + for (auto& whitelistURL : m_onlineWhitelist) { + if (protocolHostAndPortAreEqual(url, whitelistURL) && url.string().startsWith(whitelistURL.string())) return true; } return false; @@ -171,11 +139,10 @@ void ApplicationCache::setFallbackURLs(const FallbackURLVector& fallbackURLs) bool ApplicationCache::urlMatchesFallbackNamespace(const URL& url, URL* fallbackURL) { - size_t fallbackCount = m_fallbackURLs.size(); - for (size_t i = 0; i < fallbackCount; ++i) { - if (protocolHostAndPortAreEqual(url, m_fallbackURLs[i].first) && url.string().startsWith(m_fallbackURLs[i].first.string())) { + for (auto& fallback : m_fallbackURLs) { + if (protocolHostAndPortAreEqual(url, fallback.first) && url.string().startsWith(fallback.first.string())) { if (fallbackURL) - *fallbackURL = m_fallbackURLs[i].second; + *fallbackURL = fallback.second; return true; } } @@ -190,43 +157,12 @@ void ApplicationCache::clearStorageID() resource->clearStorageID(); } -void ApplicationCache::deleteCacheForOrigin(SecurityOrigin* origin) -{ - Vector<URL> urls; - if (!cacheStorage().manifestURLs(&urls)) { - LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs"); - return; - } - - URL originURL(URL(), origin->toString()); - - size_t count = urls.size(); - for (size_t i = 0; i < count; ++i) { - if (protocolHostAndPortAreEqual(urls[i], originURL)) { - ApplicationCacheGroup* group = cacheStorage().findInMemoryCacheGroup(urls[i]); - if (group) - group->makeObsolete(); - else - cacheStorage().deleteCacheGroup(urls[i]); - } - } -} - -int64_t ApplicationCache::diskUsageForOrigin(SecurityOrigin* origin) -{ - int64_t usage = 0; - cacheStorage().calculateUsageForOrigin(origin, usage); - return usage; -} - #ifndef NDEBUG void ApplicationCache::dump() { - HashMap<String, RefPtr<ApplicationCacheResource>>::const_iterator end = m_resources.end(); - - for (HashMap<String, RefPtr<ApplicationCacheResource>>::const_iterator it = m_resources.begin(); it != end; ++it) { - printf("%s ", it->key.ascii().data()); - ApplicationCacheResource::dumpType(it->value->type()); + for (const auto& urlAndResource : m_resources) { + printf("%s ", urlAndResource.key.utf8().data()); + ApplicationCacheResource::dumpType(urlAndResource.value->type()); } } #endif diff --git a/Source/WebCore/loader/appcache/ApplicationCache.h b/Source/WebCore/loader/appcache/ApplicationCache.h index 680635def..5df5f6017 100644 --- a/Source/WebCore/loader/appcache/ApplicationCache.h +++ b/Source/WebCore/loader/appcache/ApplicationCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,45 +23,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ApplicationCache_h -#define ApplicationCache_h +#pragma once #include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> #include <wtf/text/StringHash.h> -#include <wtf/text/WTFString.h> namespace WebCore { class ApplicationCacheGroup; class ApplicationCacheResource; -class DocumentLoader; -class URL; class ResourceRequest; -class SecurityOrigin; +class URL; -typedef Vector<std::pair<URL, URL>> FallbackURLVector; +using FallbackURLVector = Vector<std::pair<URL, URL>>; class ApplicationCache : public RefCounted<ApplicationCache> { public: - static PassRefPtr<ApplicationCache> create() { return adoptRef(new ApplicationCache); } - - static void deleteCacheForOrigin(SecurityOrigin*); - + static Ref<ApplicationCache> create() { return adoptRef(*new ApplicationCache); } + ~ApplicationCache(); - void addResource(PassRefPtr<ApplicationCacheResource> resource); - unsigned removeResource(const String& url); - - void setManifestResource(PassRefPtr<ApplicationCacheResource> manifest); + void addResource(Ref<ApplicationCacheResource>&&); + + void setManifestResource(Ref<ApplicationCacheResource>&&); ApplicationCacheResource* manifestResource() const { return m_manifest; } - + void setGroup(ApplicationCacheGroup*); ApplicationCacheGroup* group() const { return m_group; } - bool isComplete() const; + bool isComplete(); ApplicationCacheResource* resourceForRequest(const ResourceRequest&); ApplicationCacheResource* resourceForURL(const String& url); @@ -74,45 +64,39 @@ public: void setFallbackURLs(const FallbackURLVector&); const FallbackURLVector& fallbackURLs() const { return m_fallbackURLs; } - bool urlMatchesFallbackNamespace(const URL&, URL* fallbackURL = 0); - + bool urlMatchesFallbackNamespace(const URL&, URL* fallbackURL = nullptr); + #ifndef NDEBUG void dump(); #endif - typedef HashMap<String, RefPtr<ApplicationCacheResource>> ResourceMap; - ResourceMap::const_iterator begin() const { return m_resources.begin(); } - ResourceMap::const_iterator end() const { return m_resources.end(); } - + using ResourceMap = HashMap<String, RefPtr<ApplicationCacheResource>>; + const ResourceMap& resources() const { return m_resources; } + void setStorageID(unsigned storageID) { m_storageID = storageID; } unsigned storageID() const { return m_storageID; } void clearStorageID(); - + static bool requestIsHTTPOrHTTPSGet(const ResourceRequest&); - static int64_t diskUsageForOrigin(SecurityOrigin*); - int64_t estimatedSizeInStorage() const { return m_estimatedSizeInStorage; } private: ApplicationCache(); - - ApplicationCacheGroup* m_group; + + ApplicationCacheGroup* m_group { nullptr }; ResourceMap m_resources; - ApplicationCacheResource* m_manifest; + ApplicationCacheResource* m_manifest { nullptr }; - bool m_allowAllNetworkRequests; + bool m_allowAllNetworkRequests { false }; Vector<URL> m_onlineWhitelist; FallbackURLVector m_fallbackURLs; // The total size of the resources belonging to this Application Cache instance. - // This is an estimation of the size this Application Cache occupies in the - // database file. - int64_t m_estimatedSizeInStorage; + // This is an estimation of the size this Application Cache occupies in the database file. + int64_t m_estimatedSizeInStorage { 0 }; - unsigned m_storageID; + unsigned m_storageID { 0 }; }; } // namespace WebCore - -#endif // ApplicationCache_h diff --git a/Source/WebCore/loader/appcache/ApplicationCacheAllInOne.cpp b/Source/WebCore/loader/appcache/ApplicationCacheAllInOne.cpp new file mode 100644 index 000000000..73eaf7214 --- /dev/null +++ b/Source/WebCore/loader/appcache/ApplicationCacheAllInOne.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 Apple 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 APPLE 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. + */ + +// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build. + +#include "ApplicationCache.cpp" +#include "ApplicationCacheGroup.cpp" +#include "ApplicationCacheHost.cpp" +#include "ApplicationCacheResource.cpp" +#include "ApplicationCacheStorage.cpp" diff --git a/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp index 8e67597a6..9f9ab000c 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp +++ b/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -32,161 +32,151 @@ #include "ApplicationCacheStorage.h" #include "Chrome.h" #include "ChromeClient.h" -#include "Console.h" #include "DOMApplicationCache.h" -#include "DOMWindow.h" #include "DocumentLoader.h" +#include "EventNames.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" +#include "HTTPHeaderNames.h" #include "InspectorInstrumentation.h" #include "ManifestParser.h" #include "Page.h" -#include "ResourceBuffer.h" +#include "ProgressTracker.h" #include "ResourceHandle.h" -#include "ScriptProfile.h" #include "SecurityOrigin.h" #include "Settings.h" #include <wtf/HashMap.h> #include <wtf/MainThread.h> -#if ENABLE(INSPECTOR) -#include "ProgressTracker.h" -#endif - namespace WebCore { -ApplicationCacheGroup::ApplicationCacheGroup(const URL& manifestURL, bool isCopy) - : m_manifestURL(manifestURL) +ApplicationCacheGroup::ApplicationCacheGroup(Ref<ApplicationCacheStorage>&& storage, const URL& manifestURL) + : m_storage(WTFMove(storage)) + , m_manifestURL(manifestURL) , m_origin(SecurityOrigin::create(manifestURL)) - , m_updateStatus(Idle) - , m_downloadingPendingMasterResourceLoadersCount(0) - , m_progressTotal(0) - , m_progressDone(0) - , m_frame(0) - , m_storageID(0) - , m_isObsolete(false) - , m_completionType(None) - , m_isCopy(isCopy) - , m_calledReachedMaxAppCacheSize(false) , m_availableSpaceInQuota(ApplicationCacheStorage::unknownQuota()) - , m_originQuotaExceededPreviously(false) { } ApplicationCacheGroup::~ApplicationCacheGroup() { - if (m_isCopy) { - ASSERT(m_newestCache); - ASSERT(m_caches.size() == 1); - ASSERT(m_caches.contains(m_newestCache.get())); - ASSERT(!m_cacheBeingUpdated); - ASSERT(m_associatedDocumentLoaders.isEmpty()); - ASSERT(m_pendingMasterResourceLoaders.isEmpty()); - ASSERT(m_newestCache->group() == this); - - return; - } - ASSERT(!m_newestCache); ASSERT(m_caches.isEmpty()); - + stopLoading(); - - cacheStorage().cacheGroupDestroyed(this); + + m_storage->cacheGroupDestroyed(*this); } -ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader*) +ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader* documentLoader) { if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) - return 0; + return nullptr; URL url(request.url()); - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); + url.removeFragmentIdentifier(); - if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(url)) { - ASSERT(group->newestCache()); - ASSERT(!group->isObsolete()); - - return group->newestCache(); - } - - return 0; + auto* page = documentLoader->frame() ? documentLoader->frame()->page() : nullptr; + if (!page || page->usesEphemeralSession()) + return nullptr; + + auto* group = page->applicationCacheStorage().cacheGroupForURL(url); + if (!group) + return nullptr; + + ASSERT(group->newestCache()); + ASSERT(!group->isObsolete()); + + return group->newestCache(); } -ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const ResourceRequest& request, DocumentLoader*) +ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const ResourceRequest& request, DocumentLoader* documentLoader) { if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) - return 0; + return nullptr; + + auto* frame = documentLoader->frame(); + if (!frame) + return nullptr; + + auto* page = frame->page(); + if (!page) + return nullptr; URL url(request.url()); - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); + url.removeFragmentIdentifier(); - if (ApplicationCacheGroup* group = cacheStorage().fallbackCacheGroupForURL(url)) { - ASSERT(group->newestCache()); - ASSERT(!group->isObsolete()); + auto* group = page->applicationCacheStorage().fallbackCacheGroupForURL(url); + if (!group) + return nullptr; - return group->newestCache(); - } - - return 0; + ASSERT(group->newestCache()); + ASSERT(!group->isObsolete()); + + return group->newestCache(); } -void ApplicationCacheGroup::selectCache(Frame* frame, const URL& passedManifestURL) +void ApplicationCacheGroup::selectCache(Frame& frame, const URL& passedManifestURL) { - ASSERT(frame && frame->page()); - - if (!frame->settings().offlineWebApplicationCacheEnabled()) - return; + ASSERT(frame.document()); + ASSERT(frame.page()); + ASSERT(frame.loader().documentLoader()); - if (!frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) + if (!frame.settings().offlineWebApplicationCacheEnabled()) return; - - DocumentLoader* documentLoader = frame->loader().documentLoader(); - ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); + + auto& documentLoader = *frame.loader().documentLoader(); + ASSERT(!documentLoader.applicationCacheHost().applicationCache()); if (passedManifestURL.isNull()) { - selectCacheWithoutManifestURL(frame); + selectCacheWithoutManifestURL(frame); + return; + } + + // Don't access anything on disk if private browsing is enabled. + if (frame.page()->usesEphemeralSession() || !frame.document()->securityOrigin().canAccessApplicationCache(frame.tree().top().document()->securityOrigin())) { + postListenerTask(eventNames().checkingEvent, documentLoader); + postListenerTask(eventNames().errorEvent, documentLoader); return; } URL manifestURL(passedManifestURL); - if (manifestURL.hasFragmentIdentifier()) - manifestURL.removeFragmentIdentifier(); + manifestURL.removeFragmentIdentifier(); - ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); + auto* mainResourceCache = documentLoader.applicationCacheHost().mainResourceApplicationCache(); if (mainResourceCache) { + ASSERT(mainResourceCache->group()); if (manifestURL == mainResourceCache->group()->m_manifestURL) { // The cache may have gotten obsoleted after we've loaded from it, but before we parsed the document and saw cache manifest. if (mainResourceCache->group()->isObsolete()) return; - mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); + mainResourceCache->group()->associateDocumentLoaderWithCache(&documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. - URL resourceURL(documentLoader->responseURL()); - if (resourceURL.hasFragmentIdentifier()) - resourceURL.removeFragmentIdentifier(); - ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL); - bool inStorage = resource->storageID(); - resource->addType(ApplicationCacheResource::Foreign); + URL resourceURL { documentLoader.responseURL() }; + resourceURL.removeFragmentIdentifier(); + + ASSERT(mainResourceCache->resourceForURL(resourceURL)); + auto& resource = *mainResourceCache->resourceForURL(resourceURL); + + bool inStorage = resource.storageID(); + resource.addType(ApplicationCacheResource::Foreign); if (inStorage) - cacheStorage().storeUpdatedType(resource, mainResourceCache); + frame.page()->applicationCacheStorage().storeUpdatedType(&resource, mainResourceCache); // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. - frame->navigationScheduler().scheduleLocationChange(frame->document()->securityOrigin(), documentLoader->url(), frame->loader().referrer()); + frame.navigationScheduler().scheduleLocationChange(*frame.document(), frame.document()->securityOrigin(), documentLoader.url(), frame.loader().referrer()); } - return; } - + // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. - const ResourceRequest& request = frame->loader().activeDocumentLoader()->request(); + auto& request = frame.loader().activeDocumentLoader()->request(); if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return; @@ -195,49 +185,48 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const URL& passedManifestU if (!protocolHostAndPortAreEqual(manifestURL, request.url())) return; - // Don't change anything on disk if private browsing is enabled. - if (frame->settings().privateBrowsingEnabled()) { - postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader); - postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader); - return; - } - - ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL); + auto& group = *frame.page()->applicationCacheStorage().findOrCreateCacheGroup(manifestURL); - documentLoader->applicationCacheHost()->setCandidateApplicationCacheGroup(group); - group->m_pendingMasterResourceLoaders.add(documentLoader); - group->m_downloadingPendingMasterResourceLoadersCount++; + documentLoader.applicationCacheHost().setCandidateApplicationCacheGroup(&group); + group.m_pendingMasterResourceLoaders.add(&documentLoader); + group.m_downloadingPendingMasterResourceLoadersCount++; - ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle); - group->update(frame, ApplicationCacheUpdateWithBrowsingContext); + ASSERT(!group.m_cacheBeingUpdated || group.m_updateStatus != Idle); + group.update(frame, ApplicationCacheUpdateWithBrowsingContext); } -void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) +void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame& frame) { - if (!frame->settings().offlineWebApplicationCacheEnabled()) - return; - - if (!frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) + if (!frame.settings().offlineWebApplicationCacheEnabled()) return; - DocumentLoader* documentLoader = frame->loader().documentLoader(); - ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); + ASSERT(frame.document()); + ASSERT(frame.page()); + ASSERT(frame.loader().documentLoader()); + auto& documentLoader = *frame.loader().documentLoader(); + ASSERT(!documentLoader.applicationCacheHost().applicationCache()); - ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); + // Don't access anything on disk if private browsing is enabled. + if (frame.page()->usesEphemeralSession() || !frame.document()->securityOrigin().canAccessApplicationCache(frame.tree().top().document()->securityOrigin())) { + postListenerTask(eventNames().checkingEvent, documentLoader); + postListenerTask(eventNames().errorEvent, documentLoader); + return; + } - if (mainResourceCache) { - mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); - mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); + if (auto* mainResourceCache = documentLoader.applicationCacheHost().mainResourceApplicationCache()) { + ASSERT(mainResourceCache->group()); + auto& group = *mainResourceCache->group(); + group.associateDocumentLoaderWithCache(&documentLoader, mainResourceCache); + group.update(frame, ApplicationCacheUpdateWithBrowsingContext); } } -void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) +void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader& loader) { - ASSERT(m_pendingMasterResourceLoaders.contains(loader)); + ASSERT(m_pendingMasterResourceLoaders.contains(&loader)); ASSERT(m_completionType == None || m_pendingEntries.isEmpty()); - URL url = loader->url(); - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); + URL url = loader.url(); + url.removeFragmentIdentifier(); switch (m_completionType) { case None: @@ -245,39 +234,32 @@ void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) return; case NoUpdate: ASSERT(!m_cacheBeingUpdated); - associateDocumentLoaderWithCache(loader, m_newestCache.get()); - - if (ApplicationCacheResource* resource = m_newestCache->resourceForURL(url)) { + associateDocumentLoaderWithCache(&loader, m_newestCache.get()); + if (auto* resource = m_newestCache->resourceForURL(url)) { if (!(resource->type() & ApplicationCacheResource::Master)) { resource->addType(ApplicationCacheResource::Master); ASSERT(!resource->storageID()); } - } else { - RefPtr<ResourceBuffer> buffer = loader->mainResourceData(); - m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, buffer ? buffer->sharedBuffer() : 0)); - } - + } else + m_newestCache->addResource(ApplicationCacheResource::create(url, loader.response(), ApplicationCacheResource::Master, loader.mainResourceData())); break; case Failure: // Cache update has been a failure, so there is no reason to keep the document associated with the incomplete cache // (its main resource was not cached yet, so it is likely that the application changed significantly server-side). ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading(). - loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too. - m_associatedDocumentLoaders.remove(loader); - postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader); + loader.applicationCacheHost().setApplicationCache(nullptr); // Will unset candidate, too. + m_associatedDocumentLoaders.remove(&loader); + postListenerTask(eventNames().errorEvent, loader); break; case Completed: - ASSERT(m_associatedDocumentLoaders.contains(loader)); - - if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) { + ASSERT(m_associatedDocumentLoaders.contains(&loader)); + if (auto* resource = m_cacheBeingUpdated->resourceForURL(url)) { if (!(resource->type() & ApplicationCacheResource::Master)) { resource->addType(ApplicationCacheResource::Master); ASSERT(!resource->storageID()); } - } else { - RefPtr<ResourceBuffer> buffer = loader->mainResourceData(); - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, buffer ? buffer->sharedBuffer() : 0)); - } + } else + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader.response(), ApplicationCacheResource::Master, loader.mainResourceData())); // The "cached" event will be posted to all associated documents once update is complete. break; } @@ -287,9 +269,9 @@ void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) checkIfLoadIsComplete(); } -void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader) +void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader& loader) { - ASSERT(m_pendingMasterResourceLoaders.contains(loader)); + ASSERT(m_pendingMasterResourceLoaders.contains(&loader)); ASSERT(m_completionType == None || m_pendingEntries.isEmpty()); switch (m_completionType) { @@ -298,32 +280,27 @@ void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader) return; case NoUpdate: ASSERT(!m_cacheBeingUpdated); - // The manifest didn't change, and we have a relevant cache - but the main resource download failed mid-way, so it cannot be stored to the cache, // and the loader does not get associated to it. If there are other main resources being downloaded for this cache group, they may still succeed. - postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader); - + postListenerTask(eventNames().errorEvent, loader); break; case Failure: // Cache update failed, too. ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading(). - ASSERT(!loader->applicationCacheHost()->applicationCache() || loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated); - - loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too. - m_associatedDocumentLoaders.remove(loader); - postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader); + ASSERT(!loader.applicationCacheHost().applicationCache() || loader.applicationCacheHost().applicationCache()->group() == this); + loader.applicationCacheHost().setApplicationCache(nullptr); // Will unset candidate, too. + m_associatedDocumentLoaders.remove(&loader); + postListenerTask(eventNames().errorEvent, loader); break; case Completed: // The cache manifest didn't list this main resource, and all cache entries were already updated successfully - but the main resource failed to load, // so it cannot be stored to the cache. If there are other main resources being downloaded for this cache group, they may still succeed. - ASSERT(m_associatedDocumentLoaders.contains(loader)); - ASSERT(loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated); - ASSERT(!loader->applicationCacheHost()->candidateApplicationCacheGroup()); - m_associatedDocumentLoaders.remove(loader); - loader->applicationCacheHost()->setApplicationCache(0); - - postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader); - + ASSERT(m_associatedDocumentLoaders.contains(&loader)); + ASSERT(loader.applicationCacheHost().applicationCache() == m_cacheBeingUpdated); + ASSERT(!loader.applicationCacheHost().candidateApplicationCacheGroup()); + m_associatedDocumentLoaders.remove(&loader); + loader.applicationCacheHost().setApplicationCache(nullptr); + postListenerTask(eventNames().errorEvent, loader); break; } @@ -338,10 +315,10 @@ void ApplicationCacheGroup::stopLoading() ASSERT(!m_currentHandle); ASSERT(m_manifestHandle->client() == this); - m_manifestHandle->setClient(0); + m_manifestHandle->clearClient(); m_manifestHandle->cancel(); - m_manifestHandle = 0; + m_manifestHandle = nullptr; } if (m_currentHandle) { @@ -349,23 +326,24 @@ void ApplicationCacheGroup::stopLoading() ASSERT(m_cacheBeingUpdated); ASSERT(m_currentHandle->client() == this); - m_currentHandle->setClient(0); + m_currentHandle->clearClient(); m_currentHandle->cancel(); - m_currentHandle = 0; + m_currentHandle = nullptr; } // FIXME: Resetting just a tiny part of the state in this function is confusing. Callers have to take care of a lot more. - m_cacheBeingUpdated = 0; + m_cacheBeingUpdated = nullptr; m_pendingEntries.clear(); } -void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader) +void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader& loader) { - m_associatedDocumentLoaders.remove(loader); - m_pendingMasterResourceLoaders.remove(loader); + m_associatedDocumentLoaders.remove(&loader); + m_pendingMasterResourceLoaders.remove(&loader); - loader->applicationCacheHost()->setApplicationCache(0); // Will set candidate to 0, too. + if (auto* host = loader.applicationCacheHostUnlessBeingDestroyed()) + host->setApplicationCache(nullptr); // Will set candidate group to null, too. if (!m_associatedDocumentLoaders.isEmpty() || !m_pendingMasterResourceLoaders.isEmpty()) return; @@ -382,29 +360,29 @@ void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader) // Release our reference to the newest cache. This could cause us to be deleted. // Any ongoing updates will be stopped from destructor. - m_newestCache.release(); + m_newestCache = nullptr; } -void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache) +void ApplicationCacheGroup::cacheDestroyed(ApplicationCache& cache) { - if (m_caches.remove(cache) && m_caches.isEmpty()) { + if (m_caches.remove(&cache) && m_caches.isEmpty()) { ASSERT(m_associatedDocumentLoaders.isEmpty()); ASSERT(m_pendingMasterResourceLoaders.isEmpty()); delete this; } } -void ApplicationCacheGroup::stopLoadingInFrame(Frame* frame) +void ApplicationCacheGroup::stopLoadingInFrame(Frame& frame) { - if (frame != m_frame) + if (&frame != m_frame) return; cacheUpdateFailed(); } -void ApplicationCacheGroup::setNewestCache(PassRefPtr<ApplicationCache> newestCache) +void ApplicationCacheGroup::setNewestCache(Ref<ApplicationCache>&& newestCache) { - m_newestCache = newestCache; + m_newestCache = WTFMove(newestCache); m_caches.add(m_newestCache.get()); m_newestCache->setGroup(this); @@ -416,40 +394,43 @@ void ApplicationCacheGroup::makeObsolete() return; m_isObsolete = true; - cacheStorage().cacheGroupMadeObsolete(this); + m_storage->cacheGroupMadeObsolete(*this); ASSERT(!m_storageID); } -void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption) +void ApplicationCacheGroup::update(Frame& frame, ApplicationCacheUpdateOption updateOption) { + ASSERT(frame.loader().documentLoader()); + auto& documentLoader = *frame.loader().documentLoader(); + if (m_updateStatus == Checking || m_updateStatus == Downloading) { if (updateOption == ApplicationCacheUpdateWithBrowsingContext) { - postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); + postListenerTask(eventNames().checkingEvent, documentLoader); if (m_updateStatus == Downloading) - postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, frame->loader().documentLoader()); + postListenerTask(eventNames().downloadingEvent, documentLoader); } return; } - // Don't change anything on disk if private browsing is enabled. - if (frame->settings().privateBrowsingEnabled()) { + // Don't access anything on disk if private browsing is enabled. + if (frame.page()->usesEphemeralSession() || !frame.document()->securityOrigin().canAccessApplicationCache(frame.tree().top().document()->securityOrigin())) { ASSERT(m_pendingMasterResourceLoaders.isEmpty()); ASSERT(m_pendingEntries.isEmpty()); ASSERT(!m_cacheBeingUpdated); - postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); - postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, frame->loader().documentLoader()); + postListenerTask(eventNames().checkingEvent, documentLoader); + postListenerTask(eventNames().errorEvent, documentLoader); return; } ASSERT(!m_frame); - m_frame = frame; + m_frame = &frame; setUpdateStatus(Checking); - postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders); + postListenerTask(eventNames().checkingEvent, m_associatedDocumentLoaders); if (!m_newestCache) { ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext); - postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader().documentLoader()); + postListenerTask(eventNames().checkingEvent, documentLoader); } ASSERT(!m_manifestHandle); @@ -462,7 +443,7 @@ void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption up m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0); } -void ApplicationCacheGroup::abort(Frame* frame) +void ApplicationCacheGroup::abort(Frame& frame) { if (m_updateStatus == Idle) return; @@ -471,56 +452,51 @@ void ApplicationCacheGroup::abort(Frame* frame) if (m_completionType != None) return; - frame->document()->addConsoleMessage(NetworkMessageSource, DebugMessageLevel, "Application Cache download process was aborted."); + frame.document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Debug, ASCIILiteral("Application Cache download process was aborted.")); cacheUpdateFailed(); } -PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const URL& url, ApplicationCacheResource* newestCachedResource) +RefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const URL& url, ApplicationCacheResource* newestCachedResource) { ResourceRequest request(url); m_frame->loader().applyUserAgent(request); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"); if (newestCachedResource) { - const String& lastModified = newestCachedResource->response().httpHeaderField("Last-Modified"); - const String& eTag = newestCachedResource->response().httpHeaderField("ETag"); + const String& lastModified = newestCachedResource->response().httpHeaderField(HTTPHeaderName::LastModified); + const String& eTag = newestCachedResource->response().httpHeaderField(HTTPHeaderName::ETag); if (!lastModified.isEmpty() || !eTag.isEmpty()) { if (!lastModified.isEmpty()) - request.setHTTPHeaderField("If-Modified-Since", lastModified); + request.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified); if (!eTag.isEmpty()) - request.setHTTPHeaderField("If-None-Match", eTag); + request.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag); } } RefPtr<ResourceHandle> handle = ResourceHandle::create(m_frame->loader().networkingContext(), request, this, false, true); -#if ENABLE(INSPECTOR) + // Because willSendRequest only gets called during redirects, we initialize // the identifier and the first willSendRequest here. m_currentResourceIdentifier = m_frame->page()->progress().createUniqueIdentifier(); ResourceResponse redirectResponse = ResourceResponse(); InspectorInstrumentation::willSendRequest(m_frame, m_currentResourceIdentifier, m_frame->loader().documentLoader(), request, redirectResponse); -#endif return handle; } -void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) +void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, ResourceResponse&& response) { -#if ENABLE(INSPECTOR) - DocumentLoader* loader = (handle == m_manifestHandle) ? 0 : m_frame->loader().documentLoader(); - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_frame, m_currentResourceIdentifier, response); - InspectorInstrumentation::didReceiveResourceResponse(cookie, m_currentResourceIdentifier, loader, response, 0); -#endif + ASSERT(m_frame); + InspectorInstrumentation::didReceiveResourceResponse(*m_frame, m_currentResourceIdentifier, m_frame->loader().documentLoader(), response, nullptr); if (handle == m_manifestHandle) { didReceiveManifestResponse(response); return; } - + ASSERT(handle == m_currentHandle); URL url(handle->firstRequest().url()); - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); + url.removeFragmentIdentifier(); ASSERT(!m_currentResource); ASSERT(m_pendingEntries.contains(url)); @@ -534,10 +510,10 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res if (m_newestCache && response.httpStatusCode() == 304) { // Not modified. ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); if (newestCachedResource) { - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path())); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, &newestCachedResource->data(), newestCachedResource->path())); m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); - m_currentHandle = 0; + m_currentHandle = nullptr; // Load the next resource, if any. startLoadingEntry(); return; @@ -547,14 +523,14 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->firstRequest().url()) { if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { - m_frame->document()->addConsoleMessage(AppCacheMessageSource, ErrorMessageLevel, "Application Cache update failed, because " + m_currentHandle->firstRequest().url().stringCenterEllipsizedToLength() + + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, "Application Cache update failed, because " + m_currentHandle->firstRequest().url().stringCenterEllipsizedToLength() + ((response.httpStatusCode() / 100 != 2) ? " could not be fetched." : " was redirected.")); // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) { // Skip this resource. It is dropped from the cache. m_currentHandle->cancel(); - m_currentHandle = 0; + m_currentHandle = nullptr; m_pendingEntries.remove(url); // Load the next resource, if any. startLoadingEntry(); @@ -564,10 +540,10 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res ASSERT(m_newestCache); ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url()); ASSERT(newestCachedResource); - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path())); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, &newestCachedResource->data(), newestCachedResource->path())); m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); - m_currentHandle = 0; + m_currentHandle = nullptr; // Load the next resource, if any. startLoadingEntry(); } @@ -581,9 +557,7 @@ void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* d { UNUSED_PARAM(encodedDataLength); -#if ENABLE(INSPECTOR) InspectorInstrumentation::didReceiveData(m_frame, m_currentResourceIdentifier, 0, length, 0); -#endif if (handle == m_manifestHandle) { didReceiveManifestData(data, length); @@ -593,16 +567,12 @@ void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* d ASSERT(handle == m_currentHandle); ASSERT(m_currentResource); - m_currentResource->data()->append(data, length); + m_currentResource->data().append(data, length); } void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double finishTime) { -#if ENABLE(INSPECTOR) InspectorInstrumentation::didFinishLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, finishTime); -#else - UNUSED_PARAM(finishTime); -#endif if (handle == m_manifestHandle) { didFinishLoadingManifest(); @@ -616,8 +586,8 @@ void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double fini ASSERT(m_cacheBeingUpdated); - m_cacheBeingUpdated->addResource(m_currentResource.release()); - m_currentHandle = 0; + m_cacheBeingUpdated->addResource(m_currentResource.releaseNonNull()); + m_currentHandle = nullptr; // While downloading check to see if we have exceeded the available quota. // We can stop immediately if we have already previously failed @@ -625,8 +595,8 @@ void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double fini // of the quota being reached and decided not to increase it then. // FIXME: Should we break earlier and prevent redownloading on later page loads? if (m_originQuotaExceededPreviously && m_availableSpaceInQuota < m_cacheBeingUpdated->estimatedSizeInStorage()) { - m_currentResource = 0; - m_frame->document()->addConsoleMessage(AppCacheMessageSource, ErrorMessageLevel, "Application Cache update failed, because size quota was exceeded."); + m_currentResource = nullptr; + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache update failed, because size quota was exceeded.")); cacheUpdateFailed(); return; } @@ -637,11 +607,7 @@ void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double fini void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& error) { -#if ENABLE(INSPECTOR) InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, error); -#else - UNUSED_PARAM(error); -#endif if (handle == m_manifestHandle) { // A network error is logged elsewhere, no need to log again. Also, it's normal for manifest fetching to fail when working offline. @@ -653,15 +619,14 @@ void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->firstRequest().url()); URL url(handle->firstRequest().url()); - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); + url.removeFragmentIdentifier(); ASSERT(!m_currentResource || !m_pendingEntries.contains(url)); - m_currentResource = 0; + m_currentResource = nullptr; m_pendingEntries.remove(url); if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { - m_frame->document()->addConsoleMessage(AppCacheMessageSource, ErrorMessageLevel, "Application Cache update failed, because " + url.stringCenterEllipsizedToLength() + " could not be fetched."); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, "Application Cache update failed, because " + url.stringCenterEllipsizedToLength() + " could not be fetched."); // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); } else { @@ -670,7 +635,7 @@ void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& ASSERT(m_newestCache); ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); ASSERT(newestCachedResource); - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data(), newestCachedResource->path())); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, &newestCachedResource->data(), newestCachedResource->path())); // Load the next resource, if any. startLoadingEntry(); } @@ -682,6 +647,8 @@ void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& r ASSERT(m_manifestHandle); if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) { + InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, m_frame->loader().cancelledError(m_manifestHandle->firstRequest())); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, makeString("Application Cache manifest could not be fetched, because the manifest had a ", String::number(response.httpStatusCode()), " response.")); manifestNotFound(); return; } @@ -690,13 +657,15 @@ void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& r return; if (response.httpStatusCode() / 100 != 2) { - m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be fetched."); + InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, m_frame->loader().cancelledError(m_manifestHandle->firstRequest())); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, makeString("Application Cache manifest could not be fetched, because the manifest had a ", String::number(response.httpStatusCode()), " response.")); cacheUpdateFailed(); return; } if (response.url() != m_manifestHandle->firstRequest().url()) { - m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be fetched, because a redirection was attempted."); + InspectorInstrumentation::didFailLoading(m_frame, m_frame->loader().documentLoader(), m_currentResourceIdentifier, m_frame->loader().cancelledError(m_manifestHandle->firstRequest())); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache manifest could not be fetched, because a redirection was attempted.")); cacheUpdateFailed(); return; } @@ -707,7 +676,7 @@ void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& r void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length) { if (m_manifestResource) - m_manifestResource->data()->append(data, length); + m_manifestResource->data().append(data, length); } void ApplicationCacheGroup::didFinishLoadingManifest() @@ -716,12 +685,12 @@ void ApplicationCacheGroup::didFinishLoadingManifest() if (!isUpgradeAttempt && !m_manifestResource) { // The server returned 304 Not Modified even though we didn't send a conditional request. - m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be fetched because of an unexpected 304 Not Modified server response."); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache manifest could not be fetched because of an unexpected 304 Not Modified server response.")); cacheUpdateFailed(); return; } - m_manifestHandle = 0; + m_manifestHandle = nullptr; // Check if the manifest was not modified. if (isUpgradeAttempt) { @@ -729,10 +698,10 @@ void ApplicationCacheGroup::didFinishLoadingManifest() ASSERT(newestManifest); if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified. - (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size()))) { + (newestManifest->data().size() == m_manifestResource->data().size() && !memcmp(newestManifest->data().data(), m_manifestResource->data().data(), newestManifest->data().size()))) { m_completionType = NoUpdate; - m_manifestResource = 0; + m_manifestResource = nullptr; deliverDelayedMainResources(); return; @@ -740,9 +709,9 @@ void ApplicationCacheGroup::didFinishLoadingManifest() } Manifest manifest; - if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) { + if (!parseManifest(m_manifestURL, m_manifestResource->data().data(), m_manifestResource->data().size(), manifest)) { // At the time of this writing, lack of "CACHE MANIFEST" signature is the only reason for parseManifest to fail. - m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache manifest could not be parsed. Does it start with CACHE MANIFEST?"); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache manifest could not be parsed. Does it start with CACHE MANIFEST?")); cacheUpdateFailed(); return; } @@ -751,19 +720,18 @@ void ApplicationCacheGroup::didFinishLoadingManifest() m_cacheBeingUpdated = ApplicationCache::create(); m_cacheBeingUpdated->setGroup(this); - HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end(); - for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter) - associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get()); + for (auto& loader : m_pendingMasterResourceLoaders) + associateDocumentLoaderWithCache(loader, m_cacheBeingUpdated.get()); // We have the manifest, now download the resources. setUpdateStatus(Downloading); - postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders); + postListenerTask(eventNames().downloadingEvent, m_associatedDocumentLoaders); ASSERT(m_pendingEntries.isEmpty()); if (isUpgradeAttempt) { - for (const auto& urlAndResource : *m_newestCache) { + for (const auto& urlAndResource : m_newestCache->resources()) { unsigned type = urlAndResource.value->type(); if (type & ApplicationCacheResource::Master) addEntry(urlAndResource.key, type); @@ -773,9 +741,8 @@ void ApplicationCacheGroup::didFinishLoadingManifest() for (const auto& explicitURL : manifest.explicitURLs) addEntry(explicitURL, ApplicationCacheResource::Explicit); - size_t fallbackCount = manifest.fallbackURLs.size(); - for (size_t i = 0; i < fallbackCount; ++i) - addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback); + for (auto& fallbackURL : manifest.fallbackURLs) + addEntry(fallbackURL.second, ApplicationCacheResource::Fallback); m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs); @@ -793,7 +760,7 @@ void ApplicationCacheGroup::didReachMaxAppCacheSize() { ASSERT(m_frame); ASSERT(m_cacheBeingUpdated); - m_frame->page()->chrome().client().reachedMaxAppCacheSize(cacheStorage().spaceNeeded(m_cacheBeingUpdated->estimatedSizeInStorage())); + m_frame->page()->chrome().client().reachedMaxAppCacheSize(m_frame->page()->applicationCacheStorage().spaceNeeded(m_cacheBeingUpdated->estimatedSizeInStorage())); m_calledReachedMaxAppCacheSize = true; checkIfLoadIsComplete(); } @@ -808,7 +775,7 @@ void ApplicationCacheGroup::didReachOriginQuota(int64_t totalSpaceNeeded) void ApplicationCacheGroup::cacheUpdateFailed() { stopLoading(); - m_manifestResource = 0; + m_manifestResource = nullptr; // Wait for master resource loads to finish. m_completionType = Failure; @@ -817,7 +784,7 @@ void ApplicationCacheGroup::cacheUpdateFailed() void ApplicationCacheGroup::recalculateAvailableSpaceInQuota() { - if (!cacheStorage().calculateRemainingSizeForOriginExcludingCache(m_origin.get(), m_newestCache.get(), m_availableSpaceInQuota)) { + if (!m_frame->page()->applicationCacheStorage().calculateRemainingSizeForOriginExcludingCache(m_origin, m_newestCache.get(), m_availableSpaceInQuota)) { // Failed to determine what is left in the quota. Fallback to allowing anything. m_availableSpaceInQuota = ApplicationCacheStorage::noQuota(); } @@ -827,27 +794,27 @@ void ApplicationCacheGroup::manifestNotFound() { makeObsolete(); - postListenerTask(ApplicationCacheHost::OBSOLETE_EVENT, m_associatedDocumentLoaders); - postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_pendingMasterResourceLoaders); + postListenerTask(eventNames().obsoleteEvent, m_associatedDocumentLoaders); + postListenerTask(eventNames().errorEvent, m_pendingMasterResourceLoaders); stopLoading(); ASSERT(m_pendingEntries.isEmpty()); - m_manifestResource = 0; + m_manifestResource = nullptr; while (!m_pendingMasterResourceLoaders.isEmpty()) { HashSet<DocumentLoader*>::iterator it = m_pendingMasterResourceLoaders.begin(); - ASSERT((*it)->applicationCacheHost()->candidateApplicationCacheGroup() == this); - ASSERT(!(*it)->applicationCacheHost()->applicationCache()); - (*it)->applicationCacheHost()->setCandidateApplicationCacheGroup(0); + ASSERT((*it)->applicationCacheHost().candidateApplicationCacheGroup() == this); + ASSERT(!(*it)->applicationCacheHost().applicationCache()); + (*it)->applicationCacheHost().setCandidateApplicationCacheGroup(nullptr); m_pendingMasterResourceLoaders.remove(it); } m_downloadingPendingMasterResourceLoadersCount = 0; setUpdateStatus(Idle); - m_frame = 0; - + m_frame = nullptr; + if (m_caches.isEmpty()) { ASSERT(m_associatedDocumentLoaders.isEmpty()); ASSERT(!m_cacheBeingUpdated); @@ -874,13 +841,13 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // The storage could have been manually emptied by the user. if (!m_storageID) - cacheStorage().storeNewestCache(this); + m_storage->storeNewestCache(*this); - postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, m_associatedDocumentLoaders); + postListenerTask(eventNames().noupdateEvent, m_associatedDocumentLoaders); break; case Failure: ASSERT(!m_cacheBeingUpdated); - postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders); + postListenerTask(eventNames().errorEvent, m_associatedDocumentLoaders); if (m_caches.isEmpty()) { ASSERT(m_associatedDocumentLoaders.isEmpty()); delete this; @@ -892,14 +859,14 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() ASSERT(m_cacheBeingUpdated); if (m_manifestResource) - m_cacheBeingUpdated->setManifestResource(m_manifestResource.release()); + m_cacheBeingUpdated->setManifestResource(m_manifestResource.releaseNonNull()); else { // We can get here as a result of retrying the Complete step, following // a failure of the cache storage to save the newest cache due to hitting // the maximum size. In such a case, m_manifestResource may be 0, as // the manifest was already set on the newest cache object. ASSERT(m_cacheBeingUpdated->manifestResource()); - ASSERT(cacheStorage().isMaximumSizeReached()); + ASSERT(m_storage->isMaximumSizeReached()); ASSERT(m_calledReachedMaxAppCacheSize); } @@ -908,22 +875,22 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // If we exceeded the origin quota while downloading we can request a quota // increase now, before we attempt to store the cache. int64_t totalSpaceNeeded; - if (!cacheStorage().checkOriginQuota(this, oldNewestCache.get(), m_cacheBeingUpdated.get(), totalSpaceNeeded)) + if (!m_storage->checkOriginQuota(this, oldNewestCache.get(), m_cacheBeingUpdated.get(), totalSpaceNeeded)) didReachOriginQuota(totalSpaceNeeded); ApplicationCacheStorage::FailureReason failureReason; - setNewestCache(m_cacheBeingUpdated.release()); - if (cacheStorage().storeNewestCache(this, oldNewestCache.get(), failureReason)) { + setNewestCache(m_cacheBeingUpdated.releaseNonNull()); + if (m_storage->storeNewestCache(*this, oldNewestCache.get(), failureReason)) { // New cache stored, now remove the old cache. if (oldNewestCache) - cacheStorage().remove(oldNewestCache.get()); + m_storage->remove(oldNewestCache.get()); // Fire the final progress event. ASSERT(m_progressDone == m_progressTotal); - postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); + postListenerTask(eventNames().progressEvent, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); // Fire the success event. - postListenerTask(isUpgradeAttempt ? ApplicationCacheHost::UPDATEREADY_EVENT : ApplicationCacheHost::CACHED_EVENT, m_associatedDocumentLoaders); + postListenerTask(isUpgradeAttempt ? eventNames().updatereadyEvent : eventNames().cachedEvent, m_associatedDocumentLoaders); // It is clear that the origin quota was not reached, so clear the flag if it was set. m_originQuotaExceededPreviously = false; } else { @@ -931,7 +898,7 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // We ran out of space for this origin. Fall down to the normal error handling // after recording this state. m_originQuotaExceededPreviously = true; - m_frame->document()->addConsoleMessage(OtherMessageSource, ErrorMessageLevel, "Application Cache update failed, because size quota was exceeded."); + m_frame->document()->addConsoleMessage(MessageSource::AppCache, MessageLevel::Error, ASCIILiteral("Application Cache update failed, because size quota was exceeded.")); } if (failureReason == ApplicationCacheStorage::TotalQuotaReached && !m_calledReachedMaxAppCacheSize) { @@ -943,11 +910,9 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // save the new cache. // Save a reference to the new cache. - m_cacheBeingUpdated = m_newestCache.release(); - if (oldNewestCache) { - // Reinstate the oldNewestCache. - setNewestCache(oldNewestCache.release()); - } + m_cacheBeingUpdated = WTFMove(m_newestCache); + if (oldNewestCache) + setNewestCache(oldNewestCache.releaseNonNull()); scheduleReachedMaxAppCacheSizeCallback(); return; } @@ -955,7 +920,7 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // Run the "cache failure steps" // Fire the error events to all pending master entries, as well any other cache hosts // currently associated with a cache in this group. - postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders); + postListenerTask(eventNames().errorEvent, m_associatedDocumentLoaders); // Disassociate the pending master entries from the failed new cache. Note that // all other loaders in the m_associatedDocumentLoaders are still associated with // some other cache in this group. They are not associated with the failed new cache. @@ -963,14 +928,13 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // Need to copy loaders, because the cache group may be destroyed at the end of iteration. Vector<DocumentLoader*> loaders; copyToVector(m_pendingMasterResourceLoaders, loaders); - size_t count = loaders.size(); - for (size_t i = 0; i != count; ++i) - disassociateDocumentLoader(loaders[i]); // This can delete this group. + for (auto& loader : loaders) + disassociateDocumentLoader(*loader); // This can delete this group. // Reinstate the oldNewestCache, if there was one. if (oldNewestCache) { // This will discard the failed new cache. - setNewestCache(oldNewestCache.release()); + setNewestCache(oldNewestCache.releaseNonNull()); } else { // We must have been deleted by the last call to disassociateDocumentLoader(). return; @@ -984,7 +948,7 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() m_pendingMasterResourceLoaders.clear(); m_completionType = None; setUpdateStatus(Idle); - m_frame = 0; + m_frame = nullptr; m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota(); m_calledReachedMaxAppCacheSize = false; } @@ -999,14 +963,13 @@ void ApplicationCacheGroup::startLoadingEntry() return; } - EntryMap::const_iterator it = m_pendingEntries.begin(); + auto firstPendingEntryURL = m_pendingEntries.begin()->key; - postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); + postListenerTask(eventNames().progressEvent, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); m_progressDone++; ASSERT(!m_currentHandle); - - m_currentHandle = createResourceHandle(URL(ParsedURLString, it->key), m_newestCache ? m_newestCache->resourceForURL(it->key) : 0); + m_currentHandle = createResourceHandle(URL(ParsedURLString, firstPendingEntryURL), m_newestCache ? m_newestCache->resourceForURL(firstPendingEntryURL) : 0); } void ApplicationCacheGroup::deliverDelayedMainResources() @@ -1014,19 +977,15 @@ void ApplicationCacheGroup::deliverDelayedMainResources() // Need to copy loaders, because the cache group may be destroyed at the end of iteration. Vector<DocumentLoader*> loaders; copyToVector(m_pendingMasterResourceLoaders, loaders); - size_t count = loaders.size(); - for (size_t i = 0; i != count; ++i) { - DocumentLoader* loader = loaders[i]; + for (auto* loader : loaders) { if (loader->isLoadingMainResource()) continue; - - const ResourceError& error = loader->mainDocumentError(); - if (error.isNull()) - finishedLoadingMainResource(loader); + if (loader->mainDocumentError().isNull()) + finishedLoadingMainResource(*loader); else - failedLoadingMainResource(loader); + failedLoadingMainResource(*loader); } - if (!count) + if (loaders.isEmpty()) checkIfLoadIsComplete(); } @@ -1034,13 +993,12 @@ void ApplicationCacheGroup::addEntry(const String& url, unsigned type) { ASSERT(m_cacheBeingUpdated); ASSERT(!URL(ParsedURLString, url).hasFragmentIdentifier()); - + // Don't add the URL if we already have an master resource in the cache // (i.e., the main resource finished loading before the manifest). - if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) { + if (auto* resource = m_cacheBeingUpdated->resourceForURL(url)) { ASSERT(resource->type() & ApplicationCacheResource::Master); ASSERT(!m_frame->loader().documentLoader()->isLoadingMainResource()); - resource->addType(type); return; } @@ -1051,11 +1009,8 @@ void ApplicationCacheGroup::addEntry(const String& url, unsigned type) m_manifestResource->addType(type); return; } - - EntryMap::AddResult result = m_pendingEntries.add(url, type); - - if (!result.isNewEntry) - result.iterator->value |= type; + + m_pendingEntries.add(url, type).iterator->value |= type; } void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache) @@ -1066,90 +1021,64 @@ void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loa ASSERT(!m_isObsolete); - loader->applicationCacheHost()->setApplicationCache(cache); + loader->applicationCacheHost().setApplicationCache(cache); ASSERT(!m_associatedDocumentLoaders.contains(loader)); m_associatedDocumentLoaders.add(loader); } -class ChromeClientCallbackTimer: public TimerBase { +class ChromeClientCallbackTimer final : public TimerBase { public: - ChromeClientCallbackTimer(ApplicationCacheGroup* cacheGroup) - : m_cacheGroup(cacheGroup) + ChromeClientCallbackTimer(ApplicationCacheGroup& group) + : m_group(group) { } private: - virtual void fired() override + void fired() final { - m_cacheGroup->didReachMaxAppCacheSize(); + m_group.didReachMaxAppCacheSize(); delete this; } - // Note that there is no need to use a RefPtr here. The ApplicationCacheGroup instance is guaranteed - // to be alive when the timer fires since invoking the ChromeClient callback is part of its normal + + // Note that there is no need to use a Ref here. The ApplicationCacheGroup instance is guaranteed + // to be alive when the timer fires since invoking the callback is part of its normal // update machinery and nothing can yet cause it to get deleted. - ApplicationCacheGroup* m_cacheGroup; + ApplicationCacheGroup& m_group; }; void ApplicationCacheGroup::scheduleReachedMaxAppCacheSizeCallback() { ASSERT(isMainThread()); - ChromeClientCallbackTimer* timer = new ChromeClientCallbackTimer(this); + auto* timer = new ChromeClientCallbackTimer(*this); timer->startOneShot(0); // The timer will delete itself once it fires. } -class CallCacheListenerTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<CallCacheListenerTask> create(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone) - { - return adoptPtr(new CallCacheListenerTask(loader, eventID, progressTotal, progressDone)); - } - - virtual void performTask(ScriptExecutionContext* context) override - { - - ASSERT_UNUSED(context, context->isDocument()); - Frame* frame = m_documentLoader->frame(); - if (!frame) - return; - - ASSERT(frame->loader().documentLoader() == m_documentLoader.get()); - - m_documentLoader->applicationCacheHost()->notifyDOMApplicationCache(m_eventID, m_progressTotal, m_progressDone); - } - -private: - CallCacheListenerTask(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone) - : m_documentLoader(loader) - , m_eventID(eventID) - , m_progressTotal(progressTotal) - , m_progressDone(progressDone) - { - } - - RefPtr<DocumentLoader> m_documentLoader; - ApplicationCacheHost::EventID m_eventID; - int m_progressTotal; - int m_progressDone; -}; - -void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, const HashSet<DocumentLoader*>& loaderSet) +void ApplicationCacheGroup::postListenerTask(const AtomicString& eventType, int progressTotal, int progressDone, const HashSet<DocumentLoader*>& loaderSet) { - HashSet<DocumentLoader*>::const_iterator loaderSetEnd = loaderSet.end(); - for (HashSet<DocumentLoader*>::const_iterator iter = loaderSet.begin(); iter != loaderSetEnd; ++iter) - postListenerTask(eventID, progressTotal, progressDone, *iter); + for (auto& loader : loaderSet) + postListenerTask(eventType, progressTotal, progressDone, *loader); } -void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, DocumentLoader* loader) +void ApplicationCacheGroup::postListenerTask(const AtomicString& eventType, int progressTotal, int progressDone, DocumentLoader& loader) { - Frame* frame = loader->frame(); + auto* frame = loader.frame(); if (!frame) return; - ASSERT(frame->loader().documentLoader() == loader); + ASSERT(frame->loader().documentLoader() == &loader); + + RefPtr<DocumentLoader> protectedLoader(&loader); + frame->document()->postTask([protectedLoader, &eventType, progressTotal, progressDone] (ScriptExecutionContext& context) { + ASSERT_UNUSED(context, context.isDocument()); + auto* frame = protectedLoader->frame(); + if (!frame) + return; - frame->document()->postTask(CallCacheListenerTask::create(loader, eventID, progressTotal, progressDone)); + ASSERT(frame->loader().documentLoader() == protectedLoader); + protectedLoader->applicationCacheHost().notifyDOMApplicationCache(eventType, progressTotal, progressDone); + }); } void ApplicationCacheGroup::setUpdateStatus(UpdateStatus status) @@ -1160,8 +1089,7 @@ void ApplicationCacheGroup::setUpdateStatus(UpdateStatus status) void ApplicationCacheGroup::clearStorageID() { m_storageID = 0; - - for (const auto& cache : m_caches) + for (auto& cache : m_caches) cache->clearStorageID(); } diff --git a/Source/WebCore/loader/appcache/ApplicationCacheGroup.h b/Source/WebCore/loader/appcache/ApplicationCacheGroup.h index e1fb198ed..ceb83dfc6 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheGroup.h +++ b/Source/WebCore/loader/appcache/ApplicationCacheGroup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,13 +23,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ApplicationCacheGroup_h -#define ApplicationCacheGroup_h +#pragma once #include "DOMApplicationCache.h" #include "URL.h" #include "ResourceHandleClient.h" -#include "SharedBuffer.h" #include <wtf/Noncopyable.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> @@ -39,6 +37,7 @@ namespace WebCore { class ApplicationCache; class ApplicationCacheResource; +class ApplicationCacheStorage; class Document; class DocumentLoader; class Frame; @@ -50,10 +49,11 @@ enum ApplicationCacheUpdateOption { ApplicationCacheUpdateWithoutBrowsingContext }; -class ApplicationCacheGroup : ResourceHandleClient { - WTF_MAKE_NONCOPYABLE(ApplicationCacheGroup); WTF_MAKE_FAST_ALLOCATED; +class ApplicationCacheGroup final : private ResourceHandleClient { + WTF_MAKE_NONCOPYABLE(ApplicationCacheGroup); + WTF_MAKE_FAST_ALLOCATED; public: - ApplicationCacheGroup(const URL& manifestURL, bool isCopy = false); + explicit ApplicationCacheGroup(Ref<ApplicationCacheStorage>&&, const URL& manifestURL); virtual ~ApplicationCacheGroup(); enum UpdateStatus { Idle, Checking, Downloading }; @@ -61,11 +61,12 @@ public: static ApplicationCache* cacheForMainRequest(const ResourceRequest&, DocumentLoader*); static ApplicationCache* fallbackCacheForMainRequest(const ResourceRequest&, DocumentLoader*); - static void selectCache(Frame*, const URL& manifestURL); - static void selectCacheWithoutManifestURL(Frame*); - + static void selectCache(Frame&, const URL& manifestURL); + static void selectCacheWithoutManifestURL(Frame&); + + ApplicationCacheStorage& storage() { return m_storage; } const URL& manifestURL() const { return m_manifestURL; } - const SecurityOrigin* origin() const { return m_origin.get(); } + const SecurityOrigin& origin() const { return m_origin.get(); } UpdateStatus updateStatus() const { return m_updateStatus; } void setUpdateStatus(UpdateStatus status); @@ -73,46 +74,44 @@ public: unsigned storageID() const { return m_storageID; } void clearStorageID(); - void update(Frame*, ApplicationCacheUpdateOption); // FIXME: Frame should not be needed when updating without browsing context. - void cacheDestroyed(ApplicationCache*); + void update(Frame&, ApplicationCacheUpdateOption); // FIXME: Frame should not be needed when updating without browsing context. + void cacheDestroyed(ApplicationCache&); - void abort(Frame*); + void abort(Frame&); - bool cacheIsBeingUpdated(const ApplicationCache* cache) const { return cache == m_cacheBeingUpdated; } + bool cacheIsComplete(ApplicationCache& cache) { return m_caches.contains(&cache); } - void stopLoadingInFrame(Frame*); + void stopLoadingInFrame(Frame&); ApplicationCache* newestCache() const { return m_newestCache.get(); } - void setNewestCache(PassRefPtr<ApplicationCache>); + void setNewestCache(Ref<ApplicationCache>&&); void makeObsolete(); bool isObsolete() const { return m_isObsolete; } - void finishedLoadingMainResource(DocumentLoader*); - void failedLoadingMainResource(DocumentLoader*); - - void disassociateDocumentLoader(DocumentLoader*); + void finishedLoadingMainResource(DocumentLoader&); + void failedLoadingMainResource(DocumentLoader&); - bool isCopy() const { return m_isCopy; } + void disassociateDocumentLoader(DocumentLoader&); private: - static void postListenerTask(ApplicationCacheHost::EventID id, const HashSet<DocumentLoader*>& set) { postListenerTask(id, 0, 0, set); } - static void postListenerTask(ApplicationCacheHost::EventID id, DocumentLoader* loader) { postListenerTask(id, 0, 0, loader); } - static void postListenerTask(ApplicationCacheHost::EventID, int progressTotal, int progressDone, const HashSet<DocumentLoader*>&); - static void postListenerTask(ApplicationCacheHost::EventID, int progressTotal, int progressDone, DocumentLoader*); + static void postListenerTask(const AtomicString& eventType, const HashSet<DocumentLoader*>& set) { postListenerTask(eventType, 0, 0, set); } + static void postListenerTask(const AtomicString& eventType, DocumentLoader& loader) { postListenerTask(eventType, 0, 0, loader); } + static void postListenerTask(const AtomicString& eventType, int progressTotal, int progressDone, const HashSet<DocumentLoader*>&); + static void postListenerTask(const AtomicString& eventType, int progressTotal, int progressDone, DocumentLoader&); void scheduleReachedMaxAppCacheSizeCallback(); - PassRefPtr<ResourceHandle> createResourceHandle(const URL&, ApplicationCacheResource* newestCachedResource); + RefPtr<ResourceHandle> createResourceHandle(const URL&, ApplicationCacheResource* newestCachedResource); // For normal resource loading, WebKit client is asked about each resource individually. Since application cache does not belong to any particular document, // the existing client callback cannot be used, so assume that any client that enables application cache also wants it to use credential storage. - virtual bool shouldUseCredentialStorage(ResourceHandle*) override { return true; } + bool shouldUseCredentialStorage(ResourceHandle*) override { return true; } - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) override; - virtual void didReceiveData(ResourceHandle*, const char*, unsigned length, int encodedDataLength) override; - virtual void didFinishLoading(ResourceHandle*, double finishTime) override; - virtual void didFail(ResourceHandle*, const ResourceError&) override; + void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override; + void didReceiveData(ResourceHandle*, const char*, unsigned length, int encodedDataLength) override; + void didFinishLoading(ResourceHandle*, double finishTime) override; + void didFail(ResourceHandle*, const ResourceError&) override; void didReceiveManifestResponse(const ResourceResponse&); void didReceiveManifestData(const char*, int); @@ -132,10 +131,12 @@ private: void associateDocumentLoaderWithCache(DocumentLoader*, ApplicationCache*); void stopLoading(); - + + Ref<ApplicationCacheStorage> m_storage; + URL m_manifestURL; - RefPtr<SecurityOrigin> m_origin; - UpdateStatus m_updateStatus; + Ref<SecurityOrigin> m_origin; + UpdateStatus m_updateStatus { Idle }; // This is the newest complete cache in the group. RefPtr<ApplicationCache> m_newestCache; @@ -150,26 +151,25 @@ private: // List of pending master entries, used during the update process to ensure that new master entries are cached. HashSet<DocumentLoader*> m_pendingMasterResourceLoaders; // How many of the above pending master entries have not yet finished downloading. - int m_downloadingPendingMasterResourceLoadersCount; + int m_downloadingPendingMasterResourceLoadersCount { 0 }; // These are all the document loaders that are associated with a cache in this group. HashSet<DocumentLoader*> m_associatedDocumentLoaders; // The URLs and types of pending cache entries. - typedef HashMap<String, unsigned> EntryMap; - EntryMap m_pendingEntries; + HashMap<String, unsigned> m_pendingEntries; // The total number of items to be processed to update the cache group and the number that have been done. - int m_progressTotal; - int m_progressDone; + int m_progressTotal { 0 }; + int m_progressDone { 0 }; // Frame used for fetching resources when updating. // FIXME: An update started by a particular frame should not stop if it is destroyed, but there are other frames associated with the same cache group. - Frame* m_frame; + Frame* m_frame { nullptr }; // An obsolete cache group is never stored, but the opposite is not true - storing may fail for multiple reasons, such as exceeding disk quota. - unsigned m_storageID; - bool m_isObsolete; + unsigned m_storageID { 0 }; + bool m_isObsolete { false }; // During update, this is used to handle asynchronously arriving results. enum CompletionType { @@ -178,32 +178,24 @@ private: Failure, Completed }; - CompletionType m_completionType; - - // Whether this cache group is a copy that's only used for transferring the cache to another file. - bool m_isCopy; + CompletionType m_completionType { None }; // This flag is set immediately after the ChromeClient::reachedMaxAppCacheSize() callback is invoked as a result of the storage layer failing to save a cache // due to reaching the maximum size of the application cache database file. This flag is used by ApplicationCacheGroup::checkIfLoadIsComplete() to decide // the course of action in case of this failure (i.e. call the ChromeClient callback or run the failure steps). - bool m_calledReachedMaxAppCacheSize; + bool m_calledReachedMaxAppCacheSize { false }; RefPtr<ResourceHandle> m_currentHandle; RefPtr<ApplicationCacheResource> m_currentResource; - -#if ENABLE(INSPECTOR) unsigned long m_currentResourceIdentifier; -#endif RefPtr<ApplicationCacheResource> m_manifestResource; RefPtr<ResourceHandle> m_manifestHandle; int64_t m_availableSpaceInQuota; - bool m_originQuotaExceededPreviously; + bool m_originQuotaExceededPreviously { false }; friend class ChromeClientCallbackTimer; }; } // namespace WebCore - -#endif // ApplicationCacheGroup_h diff --git a/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp b/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp index 9cc3f8d85..9114699c8 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp +++ b/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2016 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,33 +29,33 @@ #include "ApplicationCache.h" #include "ApplicationCacheGroup.h" #include "ApplicationCacheResource.h" +#include "ContentSecurityPolicy.h" #include "DocumentLoader.h" #include "DOMApplicationCache.h" +#include "EventNames.h" +#include "FileSystem.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "InspectorInstrumentation.h" +#include "MainFrame.h" +#include "Page.h" #include "ProgressEvent.h" #include "ResourceHandle.h" -#include "ResourceLoader.h" #include "ResourceRequest.h" #include "Settings.h" +#include "SubresourceLoader.h" namespace WebCore { -ApplicationCacheHost::ApplicationCacheHost(DocumentLoader* documentLoader) - : m_domApplicationCache(0) - , m_documentLoader(documentLoader) - , m_defersEvents(true) - , m_candidateApplicationCacheGroup(0) +ApplicationCacheHost::ApplicationCacheHost(DocumentLoader& documentLoader) + : m_documentLoader(documentLoader) { - ASSERT(m_documentLoader); } ApplicationCacheHost::~ApplicationCacheHost() { ASSERT(!m_applicationCache || !m_candidateApplicationCacheGroup || m_applicationCache->group() == m_candidateApplicationCacheGroup); - if (m_applicationCache) m_applicationCache->group()->disassociateDocumentLoader(m_documentLoader); else if (m_candidateApplicationCacheGroup) @@ -64,31 +64,41 @@ ApplicationCacheHost::~ApplicationCacheHost() void ApplicationCacheHost::selectCacheWithoutManifest() { - ApplicationCacheGroup::selectCacheWithoutManifestURL(m_documentLoader->frame()); + ASSERT(m_documentLoader.frame()); + ApplicationCacheGroup::selectCacheWithoutManifestURL(*m_documentLoader.frame()); } void ApplicationCacheHost::selectCacheWithManifest(const URL& manifestURL) { - ApplicationCacheGroup::selectCache(m_documentLoader->frame(), manifestURL); + ASSERT(m_documentLoader.frame()); + ApplicationCacheGroup::selectCache(*m_documentLoader.frame(), manifestURL); } void ApplicationCacheHost::maybeLoadMainResource(ResourceRequest& request, SubstituteData& substituteData) { // Check if this request should be loaded from the application cache - if (!substituteData.isValid() && isApplicationCacheEnabled()) { + if (!substituteData.isValid() && isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) { ASSERT(!m_mainResourceApplicationCache); - m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, m_documentLoader); + m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, &m_documentLoader); if (m_mainResourceApplicationCache) { // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource. ApplicationCacheResource* resource = m_mainResourceApplicationCache->resourceForRequest(request); - substituteData = SubstituteData(resource->data(), - resource->response().mimeType(), - resource->response().textEncodingName(), - URL(), + + // ApplicationCache resources have fragment identifiers stripped off of their URLs, + // but we'll need to restore that for the SubstituteData. + ResourceResponse responseToUse = resource->response(); + if (request.url().hasFragmentIdentifier()) { + URL url = responseToUse.url(); + url.setFragmentIdentifier(request.url().fragmentIdentifier()); + responseToUse.setURL(url); + } + + substituteData = SubstituteData(&resource->data(), URL(), - SubstituteData::ShouldRevealToSessionHistory); + responseToUse, + SubstituteData::SessionHistoryVisibility::Visible); } } } @@ -103,10 +113,10 @@ bool ApplicationCacheHost::maybeLoadFallbackForMainResponse(const ResourceReques { if (r.httpStatusCode() / 100 == 4 || r.httpStatusCode() / 100 == 5) { ASSERT(!m_mainResourceApplicationCache); - if (isApplicationCacheEnabled()) { - m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, documentLoader()); + if (isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) { + m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, &m_documentLoader); - if (scheduleLoadFallbackResourceFromApplicationCache(documentLoader()->mainResourceLoader(), m_mainResourceApplicationCache.get())) + if (scheduleLoadFallbackResourceFromApplicationCache(m_documentLoader.mainResourceLoader(), m_mainResourceApplicationCache.get())) return true; } } @@ -117,10 +127,10 @@ bool ApplicationCacheHost::maybeLoadFallbackForMainError(const ResourceRequest& { if (!error.isCancellation()) { ASSERT(!m_mainResourceApplicationCache); - if (isApplicationCacheEnabled()) { - m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, m_documentLoader); + if (isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) { + m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, &m_documentLoader); - if (scheduleLoadFallbackResourceFromApplicationCache(documentLoader()->mainResourceLoader(), m_mainResourceApplicationCache.get())) + if (scheduleLoadFallbackResourceFromApplicationCache(m_documentLoader.mainResourceLoader(), m_mainResourceApplicationCache.get())) return true; } } @@ -129,12 +139,11 @@ bool ApplicationCacheHost::maybeLoadFallbackForMainError(const ResourceRequest& void ApplicationCacheHost::mainResourceDataReceived(const char*, int, long long, bool) { - // This method is here to facilitate alternate implemetations of this interface by the host browser. } void ApplicationCacheHost::failedLoadingMainResource() { - ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; + auto* group = m_candidateApplicationCacheGroup; if (!group && m_applicationCache) { if (mainResourceApplicationCache()) { // Even when the main resource is being loaded from an application cache, loading can fail if aborted. @@ -149,7 +158,7 @@ void ApplicationCacheHost::failedLoadingMainResource() void ApplicationCacheHost::finishedLoadingMainResource() { - ApplicationCacheGroup* group = candidateApplicationCacheGroup(); + auto* group = candidateApplicationCacheGroup(); if (!group && applicationCache() && !mainResourceApplicationCache()) group = applicationCache()->group(); @@ -157,9 +166,9 @@ void ApplicationCacheHost::finishedLoadingMainResource() group->finishedLoadingMainResource(m_documentLoader); } -bool ApplicationCacheHost::maybeLoadResource(ResourceLoader* loader, ResourceRequest& request, const URL& originalURL) +bool ApplicationCacheHost::maybeLoadResource(ResourceLoader& loader, const ResourceRequest& request, const URL& originalURL) { - if (!isApplicationCacheEnabled()) + if (!isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) return false; if (request.url() != originalURL) @@ -168,33 +177,33 @@ bool ApplicationCacheHost::maybeLoadResource(ResourceLoader* loader, ResourceReq ApplicationCacheResource* resource; if (!shouldLoadResourceFromApplicationCache(request, resource)) return false; - - m_documentLoader->m_pendingSubstituteResources.set(loader, resource); - m_documentLoader->deliverSubstituteResourcesAfterDelay(); - + + m_documentLoader.scheduleSubstituteResourceLoad(loader, *resource); return true; } bool ApplicationCacheHost::maybeLoadFallbackForRedirect(ResourceLoader* resourceLoader, ResourceRequest& request, const ResourceResponse& redirectResponse) { - if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) + if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) { if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader)) return true; + } return false; } bool ApplicationCacheHost::maybeLoadFallbackForResponse(ResourceLoader* resourceLoader, const ResourceResponse& response) { - if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5) + if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5) { if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader)) return true; + } return false; } bool ApplicationCacheHost::maybeLoadFallbackForError(ResourceLoader* resourceLoader, const ResourceError& error) { if (!error.isCancellation()) { - if (resourceLoader == m_documentLoader->mainResourceLoader()) + if (resourceLoader == m_documentLoader.mainResourceLoader()) return maybeLoadFallbackForMainError(resourceLoader->request(), error); if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader)) return true; @@ -202,22 +211,51 @@ bool ApplicationCacheHost::maybeLoadFallbackForError(ResourceLoader* resourceLoa return false; } -bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +URL ApplicationCacheHost::createFileURL(const String& path) +{ + // FIXME: Can we just use fileURLWithFileSystemPath instead? + + // fileURLWithFileSystemPath function is not suitable because URL::setPath uses encodeWithURLEscapeSequences, which it notes + // does not correctly escape '#' and '?'. This function works for our purposes because + // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()). + +#if USE(CF) && PLATFORM(WIN) + URL url(adoptCF(CFURLCreateWithFileSystemPath(0, path.createCFString().get(), kCFURLWindowsPathStyle, false)).get()); +#else + URL url; + url.setProtocol(ASCIILiteral("file")); + url.setPath(path); +#endif + return url; +} + +static inline RefPtr<SharedBuffer> bufferFromResource(ApplicationCacheResource& resource) +{ + // FIXME: Clients probably do not need a copy of the SharedBuffer. + // Remove the call to copy() once we ensure SharedBuffer will not be modified. + if (resource.path().isEmpty()) + return resource.data().copy(); + return SharedBuffer::createWithContentsOfFile(resource.path()); +} + +bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data) { ApplicationCacheResource* resource; - if (shouldLoadResourceFromApplicationCache(request, resource)) { - if (resource) { - response = resource->response(); - data.append(resource->data()->data(), resource->data()->size()); - } else { - error = documentLoader()->frameLoader()->client().cannotShowURLError(request); - } + if (!shouldLoadResourceFromApplicationCache(request, resource)) + return false; + + auto responseData = resource ? bufferFromResource(*resource) : nullptr; + if (!responseData) { + error = m_documentLoader.frameLoader()->client().cannotShowURLError(request); return true; } - return false; + + response = resource->response(); + data = WTFMove(responseData); + return true; } -void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data) { // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry @@ -228,8 +266,9 @@ void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& ApplicationCacheResource* resource; if (getApplicationCacheFallbackResource(request, resource)) { response = resource->response(); - data.clear(); - data.append(resource->data()->data(), resource->data()->size()); + // FIXME: Clients proably do not need a copy of the SharedBuffer. + // Remove the call to copy() once we ensure SharedBuffer will not be modified. + data = resource->data().copy(); } } } @@ -245,20 +284,21 @@ void ApplicationCacheHost::setDOMApplicationCache(DOMApplicationCache* domApplic m_domApplicationCache = domApplicationCache; } -void ApplicationCacheHost::notifyDOMApplicationCache(EventID id, int total, int done) +void ApplicationCacheHost::notifyDOMApplicationCache(const AtomicString& eventType, int total, int done) { - if (id != PROGRESS_EVENT) - InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader->frame()); + if (eventType != eventNames().progressEvent) + InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader.frame()); if (m_defersEvents) { // Event dispatching is deferred until document.onload has fired. - m_deferredEvents.append(DeferredEvent(id, total, done)); + m_deferredEvents.append({ eventType, total, done }); return; } - dispatchDOMEvent(id, total, done); + + dispatchDOMEvent(eventType, total, done); } -void ApplicationCacheHost::stopLoadingInFrame(Frame* frame) +void ApplicationCacheHost::stopLoadingInFrame(Frame& frame) { ASSERT(!m_applicationCache || !m_candidateApplicationCacheGroup || m_applicationCache->group() == m_candidateApplicationCacheGroup); @@ -270,56 +310,69 @@ void ApplicationCacheHost::stopLoadingInFrame(Frame* frame) void ApplicationCacheHost::stopDeferringEvents() { - RefPtr<DocumentLoader> protect(documentLoader()); - for (unsigned i = 0; i < m_deferredEvents.size(); ++i) { - const DeferredEvent& deferred = m_deferredEvents[i]; - dispatchDOMEvent(deferred.eventID, deferred.progressTotal, deferred.progressDone); + Ref<DocumentLoader> protect(m_documentLoader); + + // Note, do not cache the size in a local variable. + // This code needs to properly handle the case where more events are added to + // m_deferredEvents while iterating it. This is why we don't use a modern for loop. + for (size_t i = 0; i < m_deferredEvents.size(); ++i) { + auto& event = m_deferredEvents[i]; + dispatchDOMEvent(event.eventType, event.progressTotal, event.progressDone); } + m_deferredEvents.clear(); m_defersEvents = false; } -#if ENABLE(INSPECTOR) -void ApplicationCacheHost::fillResourceList(ResourceInfoList* resources) +Vector<ApplicationCacheHost::ResourceInfo> ApplicationCacheHost::resourceList() { - ApplicationCache* cache = applicationCache(); + Vector<ResourceInfo> result; + + auto* cache = applicationCache(); if (!cache || !cache->isComplete()) - return; - - for (const auto& urlAndResource : *cache) { - RefPtr<ApplicationCacheResource> resource = urlAndResource.value; - unsigned type = resource->type(); - bool isMaster = type & ApplicationCacheResource::Master; + return result; + + result.reserveInitialCapacity(cache->resources().size()); + + for (auto& urlAndResource : cache->resources()) { + ASSERT(urlAndResource.value); + auto& resource = *urlAndResource.value; + + unsigned type = resource.type(); + bool isMaster = type & ApplicationCacheResource::Master; bool isManifest = type & ApplicationCacheResource::Manifest; bool isExplicit = type & ApplicationCacheResource::Explicit; - bool isForeign = type & ApplicationCacheResource::Foreign; + bool isForeign = type & ApplicationCacheResource::Foreign; bool isFallback = type & ApplicationCacheResource::Fallback; - resources->append(ResourceInfo(resource->url(), isMaster, isManifest, isFallback, isForeign, isExplicit, resource->estimatedSizeInStorage())); + + result.uncheckedAppend({ resource.url(), isMaster, isManifest, isFallback, isForeign, isExplicit, resource.estimatedSizeInStorage() }); } + + return result; } - + ApplicationCacheHost::CacheInfo ApplicationCacheHost::applicationCacheInfo() { - ApplicationCache* cache = applicationCache(); + auto* cache = applicationCache(); if (!cache || !cache->isComplete()) - return CacheInfo(URL(), 0, 0, 0); - + return { { }, 0, 0, 0 }; + // FIXME: Add "Creation Time" and "Update Time" to Application Caches. - return CacheInfo(cache->manifestResource()->url(), 0, 0, cache->estimatedSizeInStorage()); + return { cache->manifestResource()->url(), 0, 0, cache->estimatedSizeInStorage() }; } -#endif -void ApplicationCacheHost::dispatchDOMEvent(EventID id, int total, int done) +static Ref<Event> createApplicationCacheEvent(const AtomicString& eventType, int total, int done) { - if (m_domApplicationCache) { - const AtomicString& eventType = DOMApplicationCache::toEventType(id); - RefPtr<Event> event; - if (id == PROGRESS_EVENT) - event = ProgressEvent::create(eventType, true, done, total); - else - event = Event::create(eventType, false, false); - m_domApplicationCache->dispatchEvent(event, ASSERT_NO_EXCEPTION); - } + if (eventType == eventNames().progressEvent) + return ProgressEvent::create(eventType, true, done, total); + return Event::create(eventType, false, false); +} + +void ApplicationCacheHost::dispatchDOMEvent(const AtomicString& eventType, int total, int done) +{ + if (!m_domApplicationCache) + return; + m_domApplicationCache->dispatchEvent(createApplicationCacheEvent(eventType, total, done)); } void ApplicationCacheHost::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group) @@ -328,25 +381,30 @@ void ApplicationCacheHost::setCandidateApplicationCacheGroup(ApplicationCacheGro m_candidateApplicationCacheGroup = group; } -void ApplicationCacheHost::setApplicationCache(PassRefPtr<ApplicationCache> applicationCache) +void ApplicationCacheHost::setApplicationCache(RefPtr<ApplicationCache>&& applicationCache) { if (m_candidateApplicationCacheGroup) { ASSERT(!m_applicationCache); - m_candidateApplicationCacheGroup = 0; + m_candidateApplicationCacheGroup = nullptr; } - - m_applicationCache = applicationCache; + m_applicationCache = WTFMove(applicationCache); } -bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource) +bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& originalRequest, ApplicationCacheResource*& resource) { - ApplicationCache* cache = applicationCache(); + auto* cache = applicationCache(); if (!cache || !cache->isComplete()) return false; + ResourceRequest request(originalRequest); + if (auto* loaderFrame = m_documentLoader.frame()) { + if (auto* document = loaderFrame->document()) + document->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load); + } + // If the resource is not to be fetched using the HTTP GET mechanism or equivalent, or if its URL has a different // <scheme> component than the application cache's manifest, then fetch the resource normally. - if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringCase(request.url().protocol(), cache->manifestResource()->url().protocol())) + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringASCIICase(request.url().protocol(), cache->manifestResource()->url().protocol())) return false; // If the resource's URL is an master entry, the manifest, an explicit entry, or a fallback entry @@ -385,45 +443,40 @@ bool ApplicationCacheHost::getApplicationCacheFallbackResource(const ResourceReq resource = cache->resourceForURL(fallbackURL); ASSERT(resource); - return true; } bool ApplicationCacheHost::scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader* loader, ApplicationCache* cache) { - if (!isApplicationCacheEnabled()) + if (!isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(loader->request())) return false; ApplicationCacheResource* resource; if (!getApplicationCacheFallbackResource(loader->request(), resource, cache)) return false; - m_documentLoader->m_pendingSubstituteResources.set(loader, resource); - m_documentLoader->deliverSubstituteResourcesAfterDelay(); - - loader->handle()->cancel(); - + loader->willSwitchToSubstituteResource(); + m_documentLoader.scheduleSubstituteResourceLoad(*loader, *resource); return true; } ApplicationCacheHost::Status ApplicationCacheHost::status() const { - ApplicationCache* cache = applicationCache(); + auto* cache = applicationCache(); if (!cache) return UNCACHED; switch (cache->group()->updateStatus()) { - case ApplicationCacheGroup::Checking: - return CHECKING; - case ApplicationCacheGroup::Downloading: - return DOWNLOADING; - case ApplicationCacheGroup::Idle: { - if (cache->group()->isObsolete()) - return OBSOLETE; - if (cache != cache->group()->newestCache()) - return UPDATEREADY; - return IDLE; - } + case ApplicationCacheGroup::Checking: + return CHECKING; + case ApplicationCacheGroup::Downloading: + return DOWNLOADING; + case ApplicationCacheGroup::Idle: + if (cache->group()->isObsolete()) + return OBSOLETE; + if (cache != cache->group()->newestCache()) + return UPDATEREADY; + return IDLE; } ASSERT_NOT_REACHED(); @@ -432,16 +485,19 @@ ApplicationCacheHost::Status ApplicationCacheHost::status() const bool ApplicationCacheHost::update() { - ApplicationCache* cache = applicationCache(); + auto* cache = applicationCache(); if (!cache) return false; - cache->group()->update(m_documentLoader->frame(), ApplicationCacheUpdateWithoutBrowsingContext); + auto* frame = m_documentLoader.frame(); + if (!frame) + return false; + cache->group()->update(*frame, ApplicationCacheUpdateWithoutBrowsingContext); return true; } bool ApplicationCacheHost::swapCache() { - ApplicationCache* cache = applicationCache(); + auto* cache = applicationCache(); if (!cache) return false; @@ -452,31 +508,40 @@ bool ApplicationCacheHost::swapCache() } // If there is no newer cache, raise an INVALID_STATE_ERR exception. - ApplicationCache* newestCache = cache->group()->newestCache(); + auto* newestCache = cache->group()->newestCache(); if (cache == newestCache) return false; ASSERT(cache->group() == newestCache->group()); setApplicationCache(newestCache); - InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader->frame()); + InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader.frame()); return true; } void ApplicationCacheHost::abort() { - ApplicationCacheGroup* cacheGroup = candidateApplicationCacheGroup(); - if (cacheGroup) - cacheGroup->abort(m_documentLoader->frame()); - else { - ApplicationCache* cache = applicationCache(); - if (cache) - cache->group()->abort(m_documentLoader->frame()); - } + auto* frame = m_documentLoader.frame(); + if (!frame) + return; + if (auto* cacheGroup = candidateApplicationCacheGroup()) + cacheGroup->abort(*frame); + else if (auto* cache = applicationCache()) + cache->group()->abort(*frame); } bool ApplicationCacheHost::isApplicationCacheEnabled() { - return m_documentLoader->frame() && m_documentLoader->frame()->settings().offlineWebApplicationCacheEnabled(); + return m_documentLoader.frame() && m_documentLoader.frame()->settings().offlineWebApplicationCacheEnabled() && !m_documentLoader.frame()->page()->usesEphemeralSession(); +} + +bool ApplicationCacheHost::isApplicationCacheBlockedForRequest(const ResourceRequest& request) +{ + auto* frame = m_documentLoader.frame(); + if (!frame) + return false; + if (frame->isMainFrame()) + return false; + return !SecurityOrigin::create(request.url())->canAccessApplicationCache(frame->document()->topOrigin()); } } // namespace WebCore diff --git a/Source/WebCore/loader/appcache/ApplicationCacheHost.h b/Source/WebCore/loader/appcache/ApplicationCacheHost.h index f6e0d0b94..2bfdc62f9 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheHost.h +++ b/Source/WebCore/loader/appcache/ApplicationCacheHost.h @@ -1,6 +1,7 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. - * + * Copyright (c) 2009, Google Inc. All rights reserved. + * Copyright (c) 2017 Apple 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: @@ -28,172 +29,138 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ApplicationCacheHost_h -#define ApplicationCacheHost_h +#pragma once #include "URL.h" #include <wtf/Deque.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> #include <wtf/Vector.h> namespace WebCore { - class DOMApplicationCache; - class DocumentLoader; - class Frame; - class ResourceLoader; - class ResourceError; - class ResourceRequest; - class ResourceResponse; - class SubstituteData; - class ApplicationCache; - class ApplicationCacheGroup; - class ApplicationCacheResource; - class ApplicationCacheStorage; - - class ApplicationCacheHost { - WTF_MAKE_NONCOPYABLE(ApplicationCacheHost); WTF_MAKE_FAST_ALLOCATED; - public: - // The Status numeric values are specified in the HTML5 spec. - enum Status { - UNCACHED = 0, - IDLE = 1, - CHECKING = 2, - DOWNLOADING = 3, - UPDATEREADY = 4, - OBSOLETE = 5 - }; - - enum EventID { - CHECKING_EVENT = 0, - ERROR_EVENT, - NOUPDATE_EVENT, - DOWNLOADING_EVENT, - PROGRESS_EVENT, - UPDATEREADY_EVENT, - CACHED_EVENT, - OBSOLETE_EVENT // Must remain the last value, this is used to size arrays. - }; - -#if ENABLE(INSPECTOR) - struct CacheInfo { - CacheInfo(const URL& manifest, double creationTime, double updateTime, long long size) - : m_manifest(manifest) - , m_creationTime(creationTime) - , m_updateTime(updateTime) - , m_size(size) { } - URL m_manifest; - double m_creationTime; - double m_updateTime; - long long m_size; - }; - - struct ResourceInfo { - ResourceInfo(const URL& resource, bool isMaster, bool isManifest, bool isFallback, bool isForeign, bool isExplicit, long long size) - : m_resource(resource) - , m_isMaster(isMaster) - , m_isManifest(isManifest) - , m_isFallback(isFallback) - , m_isForeign(isForeign) - , m_isExplicit(isExplicit) - , m_size(size) { } - URL m_resource; - bool m_isMaster; - bool m_isManifest; - bool m_isFallback; - bool m_isForeign; - bool m_isExplicit; - long long m_size; - }; - - typedef Vector<ResourceInfo> ResourceInfoList; -#endif - - ApplicationCacheHost(DocumentLoader*); - ~ApplicationCacheHost(); - - void selectCacheWithoutManifest(); - void selectCacheWithManifest(const URL& manifestURL); - - void maybeLoadMainResource(ResourceRequest&, SubstituteData&); - void maybeLoadMainResourceForRedirect(ResourceRequest&, SubstituteData&); - bool maybeLoadFallbackForMainResponse(const ResourceRequest&, const ResourceResponse&); - void mainResourceDataReceived(const char* data, int length, long long encodedDataLength, bool allAtOnce); - void finishedLoadingMainResource(); - void failedLoadingMainResource(); - - bool maybeLoadResource(ResourceLoader*, ResourceRequest&, const URL& originalURL); - bool maybeLoadFallbackForRedirect(ResourceLoader*, ResourceRequest&, const ResourceResponse&); - bool maybeLoadFallbackForResponse(ResourceLoader*, const ResourceResponse&); - bool maybeLoadFallbackForError(ResourceLoader*, const ResourceError&); - - bool maybeLoadSynchronously(ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); - void maybeLoadFallbackSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); - - bool canCacheInPageCache(); - - Status status() const; - bool update(); - bool swapCache(); - void abort(); - - void setDOMApplicationCache(DOMApplicationCache*); - void notifyDOMApplicationCache(EventID, int progressTotal, int progressDone); - - void stopLoadingInFrame(Frame*); - - void stopDeferringEvents(); // Also raises the events that have been queued up. - -#if ENABLE(INSPECTOR) - void fillResourceList(ResourceInfoList*); - CacheInfo applicationCacheInfo(); -#endif - - bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&); - bool getApplicationCacheFallbackResource(const ResourceRequest&, ApplicationCacheResource*&, ApplicationCache* = 0); - - private: - bool isApplicationCacheEnabled(); - DocumentLoader* documentLoader() const { return m_documentLoader; } - - struct DeferredEvent { - EventID eventID; - int progressTotal; - int progressDone; - DeferredEvent(EventID id, int total, int done) : eventID(id), progressTotal(total), progressDone(done) { } - }; - - DOMApplicationCache* m_domApplicationCache; - DocumentLoader* m_documentLoader; - bool m_defersEvents; // Events are deferred until after document onload. - Vector<DeferredEvent> m_deferredEvents; - - void dispatchDOMEvent(EventID, int progressTotal, int progressDone); - - friend class ApplicationCacheGroup; - friend class ApplicationCacheStorage; - - bool scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader*, ApplicationCache* = 0); - void setCandidateApplicationCacheGroup(ApplicationCacheGroup* group); - ApplicationCacheGroup* candidateApplicationCacheGroup() const { return m_candidateApplicationCacheGroup; } - void setApplicationCache(PassRefPtr<ApplicationCache> applicationCache); - ApplicationCache* applicationCache() const { return m_applicationCache.get(); } - ApplicationCache* mainResourceApplicationCache() const { return m_mainResourceApplicationCache.get(); } - bool maybeLoadFallbackForMainError(const ResourceRequest&, const ResourceError&); - - - // The application cache that the document loader is associated with (if any). - RefPtr<ApplicationCache> m_applicationCache; - - // Before an application cache has finished loading, this will be the candidate application - // group that the document loader is associated with. - ApplicationCacheGroup* m_candidateApplicationCacheGroup; - - // This is the application cache the main resource was loaded from (if any). - RefPtr<ApplicationCache> m_mainResourceApplicationCache; + +class ApplicationCache; +class ApplicationCacheGroup; +class ApplicationCacheResource; +class ApplicationCacheStorage; +class DOMApplicationCache; +class DocumentLoader; +class Frame; +class ResourceError; +class ResourceLoader; +class ResourceRequest; +class ResourceResponse; +class SharedBuffer; +class SubstituteData; + +class ApplicationCacheHost { + WTF_MAKE_NONCOPYABLE(ApplicationCacheHost); WTF_MAKE_FAST_ALLOCATED; +public: + // The Status numeric values are specified in the HTML5 spec. + enum Status { + UNCACHED = 0, + IDLE = 1, + CHECKING = 2, + DOWNLOADING = 3, + UPDATEREADY = 4, + OBSOLETE = 5 }; -} // namespace WebCore + struct CacheInfo { + URL manifest; + double creationTime; + double updateTime; + long long size; + }; + + struct ResourceInfo { + URL resource; + bool isMaster; + bool isManifest; + bool isFallback; + bool isForeign; + bool isExplicit; + long long size; + }; + + explicit ApplicationCacheHost(DocumentLoader&); + ~ApplicationCacheHost(); + + static URL createFileURL(const String&); + + void selectCacheWithoutManifest(); + void selectCacheWithManifest(const URL& manifestURL); + + void maybeLoadMainResource(ResourceRequest&, SubstituteData&); + void maybeLoadMainResourceForRedirect(ResourceRequest&, SubstituteData&); + bool maybeLoadFallbackForMainResponse(const ResourceRequest&, const ResourceResponse&); + void mainResourceDataReceived(const char* data, int length, long long encodedDataLength, bool allAtOnce); + void finishedLoadingMainResource(); + void failedLoadingMainResource(); + + WEBCORE_EXPORT bool maybeLoadResource(ResourceLoader&, const ResourceRequest&, const URL& originalURL); + WEBCORE_EXPORT bool maybeLoadFallbackForRedirect(ResourceLoader*, ResourceRequest&, const ResourceResponse&); + WEBCORE_EXPORT bool maybeLoadFallbackForResponse(ResourceLoader*, const ResourceResponse&); + WEBCORE_EXPORT bool maybeLoadFallbackForError(ResourceLoader*, const ResourceError&); + + bool maybeLoadSynchronously(ResourceRequest&, ResourceError&, ResourceResponse&, RefPtr<SharedBuffer>&); + void maybeLoadFallbackSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, RefPtr<SharedBuffer>&); + + bool canCacheInPageCache(); + + Status status() const; + bool update(); + bool swapCache(); + void abort(); + + void setDOMApplicationCache(DOMApplicationCache*); + void notifyDOMApplicationCache(const AtomicString& eventType, int progressTotal, int progressDone); + + void stopLoadingInFrame(Frame&); -#endif // ApplicationCacheHost_h + void stopDeferringEvents(); // Also raises the events that have been queued up. + + Vector<ResourceInfo> resourceList(); + CacheInfo applicationCacheInfo(); + + bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&); + bool getApplicationCacheFallbackResource(const ResourceRequest&, ApplicationCacheResource*&, ApplicationCache* = nullptr); + +private: + friend class ApplicationCacheGroup; + + struct DeferredEvent { + AtomicString eventType; + int progressTotal; + int progressDone; + }; + + bool isApplicationCacheEnabled(); + bool isApplicationCacheBlockedForRequest(const ResourceRequest&); + + void dispatchDOMEvent(const AtomicString& eventType, int progressTotal, int progressDone); + + bool scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader*, ApplicationCache* = nullptr); + void setCandidateApplicationCacheGroup(ApplicationCacheGroup*); + ApplicationCacheGroup* candidateApplicationCacheGroup() const { return m_candidateApplicationCacheGroup; } + void setApplicationCache(RefPtr<ApplicationCache>&&); + ApplicationCache* applicationCache() const { return m_applicationCache.get(); } + ApplicationCache* mainResourceApplicationCache() const { return m_mainResourceApplicationCache.get(); } + bool maybeLoadFallbackForMainError(const ResourceRequest&, const ResourceError&); + + DOMApplicationCache* m_domApplicationCache { nullptr }; + DocumentLoader& m_documentLoader; + + bool m_defersEvents { true }; // Events are deferred until after document onload. + Vector<DeferredEvent> m_deferredEvents; + + // The application cache that the document loader is associated with (if any). + RefPtr<ApplicationCache> m_applicationCache; + + // Before an application cache has finished loading, this will be the candidate application + // group that the document loader is associated with. + ApplicationCacheGroup* m_candidateApplicationCacheGroup { nullptr }; + + // This is the application cache the main resource was loaded from (if any). + RefPtr<ApplicationCache> m_mainResourceApplicationCache; +}; + +} // namespace WebCore diff --git a/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp b/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp index 2d9a8f1e6..f854a7212 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp +++ b/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp @@ -29,8 +29,8 @@ namespace WebCore { -ApplicationCacheResource::ApplicationCacheResource(const URL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> data, const String& path) - : SubstituteResource(url, response, data) +ApplicationCacheResource::ApplicationCacheResource(const URL& url, const ResourceResponse& response, unsigned type, Ref<SharedBuffer>&& data, const String& path) + : SubstituteResource(url, response, WTFMove(data)) , m_type(type) , m_storageID(0) , m_estimatedSizeInStorage(0) @@ -38,6 +38,11 @@ ApplicationCacheResource::ApplicationCacheResource(const URL& url, const Resourc { } +void ApplicationCacheResource::deliver(ResourceLoader& loader) +{ + loader.deliverResponseAndData(response(), m_path.isEmpty() ? data().copy() : SharedBuffer::createWithContentsOfFile(m_path)); +} + void ApplicationCacheResource::addType(unsigned type) { // Caller should take care of storing the new type in database. @@ -49,8 +54,7 @@ int64_t ApplicationCacheResource::estimatedSizeInStorage() if (m_estimatedSizeInStorage) return m_estimatedSizeInStorage; - if (data()) - m_estimatedSizeInStorage = data()->size(); + m_estimatedSizeInStorage = data().size(); for (const auto& headerField : response().httpHeaderFields()) m_estimatedSizeInStorage += (headerField.key.length() + headerField.value.length() + 2) * sizeof(UChar); diff --git a/Source/WebCore/loader/appcache/ApplicationCacheResource.h b/Source/WebCore/loader/appcache/ApplicationCacheResource.h index d8d3f1aa0..a39613fa3 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheResource.h +++ b/Source/WebCore/loader/appcache/ApplicationCacheResource.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ApplicationCacheResource_h -#define ApplicationCacheResource_h +#pragma once #include "SubstituteResource.h" @@ -40,10 +39,12 @@ public: Fallback = 1 << 4 }; - static PassRefPtr<ApplicationCacheResource> create(const URL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create(), const String& path = String()) + static Ref<ApplicationCacheResource> create(const URL& url, const ResourceResponse& response, unsigned type, RefPtr<SharedBuffer> buffer = SharedBuffer::create(), const String& path = String()) { ASSERT(!url.hasFragmentIdentifier()); - return adoptRef(new ApplicationCacheResource(url, response, type, buffer, path)); + if (!buffer) + buffer = SharedBuffer::create(); + return adoptRef(*new ApplicationCacheResource(url, response, type, buffer.releaseNonNull(), path)); } unsigned type() const { return m_type; } @@ -62,7 +63,9 @@ public: #endif private: - ApplicationCacheResource(const URL&, const ResourceResponse&, unsigned type, PassRefPtr<SharedBuffer>, const String& path); + ApplicationCacheResource(const URL&, const ResourceResponse&, unsigned type, Ref<SharedBuffer>&&, const String& path); + + void deliver(ResourceLoader&) override; unsigned m_type; unsigned m_storageID; @@ -71,5 +74,3 @@ private: }; } // namespace WebCore - -#endif // ApplicationCacheResource_h diff --git a/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp index e5751ac59..b6b9d7a7c 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp +++ b/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp @@ -31,13 +31,14 @@ #include "ApplicationCacheHost.h" #include "ApplicationCacheResource.h" #include "FileSystem.h" -#include "NotImplemented.h" #include "SQLiteDatabaseTracker.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" #include "SecurityOrigin.h" +#include "SecurityOriginData.h" #include "URL.h" #include "UUID.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/StringExtras.h> #include <wtf/text/CString.h> @@ -45,16 +46,13 @@ namespace WebCore { -static const char flatFileSubdirectory[] = "ApplicationCache"; - template <class T> class StorageIDJournal { public: ~StorageIDJournal() { - size_t size = m_records.size(); - for (size_t i = 0; i < size; ++i) - m_records[i].restore(); + for (auto& record : m_records) + record.restore(); } void add(T* resource, unsigned storageID) @@ -70,7 +68,7 @@ public: private: class Record { public: - Record() : m_resource(0), m_storageID(0) { } + Record() : m_resource(nullptr), m_storageID(0) { } Record(T* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { } void restore() @@ -105,59 +103,55 @@ ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const URL& manife openDatabase(false); if (!m_database.isOpen()) - return 0; + return nullptr; SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?"); - if (statement.prepare() != SQLResultOk) - return 0; + if (statement.prepare() != SQLITE_OK) + return nullptr; statement.bindText(1, manifestURL); int result = statement.step(); - if (result == SQLResultDone) - return 0; + if (result == SQLITE_DONE) + return nullptr; - if (result != SQLResultRow) { + if (result != SQLITE_ROW) { LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); - return 0; + return nullptr; } unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2)); - RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID); + auto cache = loadCache(newestCacheStorageID); if (!cache) - return 0; + return nullptr; - ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - - group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); - group->setNewestCache(cache.release()); - - return group; + auto& group = *new ApplicationCacheGroup(*this, manifestURL); + group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); + group.setNewestCache(cache.releaseNonNull()); + return &group; } ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const URL& manifestURL) { ASSERT(!manifestURL.hasFragmentIdentifier()); - CacheGroupMap::AddResult result = m_cachesInMemory.add(manifestURL, nullptr); - + auto result = m_cachesInMemory.add(manifestURL, nullptr); if (!result.isNewEntry) { ASSERT(result.iterator->value); return result.iterator->value; } // Look up the group in the database - ApplicationCacheGroup* group = loadCacheGroup(manifestURL); + auto* group = loadCacheGroup(manifestURL); // If the group was not found we need to create it if (!group) { - group = new ApplicationCacheGroup(manifestURL); + group = new ApplicationCacheGroup(*this, manifestURL); m_cacheHostSet.add(urlHostHash(manifestURL)); } - + result.iterator->value = group; - return group; } @@ -185,10 +179,10 @@ void ApplicationCacheStorage::loadManifestHostHashes() // Fetch the host hashes. SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return; - while (statement.step() == SQLResultRow) + while (statement.step() == SQLITE_ROW) m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0))); } @@ -200,7 +194,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const URL& url) // Hash the host name and see if there's a manifest with the same host. if (!m_cacheHostSet.contains(urlHostHash(url))) - return 0; + return nullptr; // Check if a cache already exists in memory. for (const auto& group : m_cachesInMemory.values()) { @@ -220,17 +214,17 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const URL& url) } if (!m_database.isOpen()) - return 0; + return nullptr; SQLiteTransactionInProgressAutoCounter transactionCounter; // Check the database. Look for all cache groups with a newest cache. SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); - if (statement.prepare() != SQLResultOk) - return 0; + if (statement.prepare() != SQLITE_OK) + return nullptr; int result; - while ((result = statement.step()) == SQLResultRow) { + while ((result = statement.step()) == SQLITE_ROW) { URL manifestURL = URL(ParsedURLString, statement.getColumnText(1)); if (m_cachesInMemory.contains(manifestURL)) @@ -242,30 +236,28 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const URL& url) // We found a cache group that matches. Now check if the newest cache has a resource with // a matching URL. unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2)); - RefPtr<ApplicationCache> cache = loadCache(newestCacheID); + auto cache = loadCache(newestCacheID); if (!cache) continue; - ApplicationCacheResource* resource = cache->resourceForURL(url); + auto* resource = cache->resourceForURL(url); if (!resource) continue; if (resource->type() & ApplicationCacheResource::Foreign) continue; - ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - - group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); - group->setNewestCache(cache.release()); - - m_cachesInMemory.set(group->manifestURL(), group); - - return group; + auto& group = *new ApplicationCacheGroup(*this, manifestURL); + group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); + group.setNewestCache(cache.releaseNonNull()); + m_cachesInMemory.set(group.manifestURL(), &group); + + return &group; } - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); - return 0; + return nullptr; } ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const URL& url) @@ -275,10 +267,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const U ASSERT(!url.hasFragmentIdentifier()); // Check if an appropriate cache already exists in memory. - CacheGroupMap::const_iterator end = m_cachesInMemory.end(); - for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { - ApplicationCacheGroup* group = it->value; - + for (auto* group : m_cachesInMemory.values()) { ASSERT(!group->isObsolete()); if (ApplicationCache* cache = group->newestCache()) { @@ -294,15 +283,15 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const U } if (!m_database.isOpen()) - return 0; + return nullptr; // Check the database. Look for all cache groups with a newest cache. SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); - if (statement.prepare() != SQLResultOk) - return 0; + if (statement.prepare() != SQLITE_OK) + return nullptr; int result; - while ((result = statement.step()) == SQLResultRow) { + while ((result = statement.step()) == SQLITE_ROW) { URL manifestURL = URL(ParsedURLString, statement.getColumnText(1)); if (m_cachesInMemory.contains(manifestURL)) @@ -315,7 +304,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const U // We found a cache group that matches. Now check if the newest cache has a resource with // a matching fallback namespace. unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2)); - RefPtr<ApplicationCache> cache = loadCache(newestCacheID); + auto cache = loadCache(newestCacheID); URL fallbackURL; if (cache->isURLInOnlineWhitelist(url)) @@ -325,57 +314,48 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const U if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign) continue; - ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - - group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); - group->setNewestCache(cache.release()); - - m_cachesInMemory.set(group->manifestURL(), group); - - return group; + auto& group = *new ApplicationCacheGroup(*this, manifestURL); + group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); + group.setNewestCache(cache.releaseNonNull()); + + m_cachesInMemory.set(group.manifestURL(), &group); + + return &group; } - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); - return 0; + return nullptr; } -void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group) +void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup& group) { - if (group->isObsolete()) { - ASSERT(!group->storageID()); - ASSERT(m_cachesInMemory.get(group->manifestURL()) != group); + if (group.isObsolete()) { + ASSERT(!group.storageID()); + ASSERT(m_cachesInMemory.get(group.manifestURL()) != &group); return; } - ASSERT(m_cachesInMemory.get(group->manifestURL()) == group); + ASSERT(m_cachesInMemory.get(group.manifestURL()) == &group); - m_cachesInMemory.remove(group->manifestURL()); + m_cachesInMemory.remove(group.manifestURL()); // If the cache group is half-created, we don't want it in the saved set (as it is not stored in database). - if (!group->storageID()) - m_cacheHostSet.remove(urlHostHash(group->manifestURL())); + if (!group.storageID()) + m_cacheHostSet.remove(urlHostHash(group.manifestURL())); } -void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup* group) +void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup& group) { - ASSERT(m_cachesInMemory.get(group->manifestURL()) == group); - ASSERT(m_cacheHostSet.contains(urlHostHash(group->manifestURL()))); + ASSERT(m_cachesInMemory.get(group.manifestURL()) == &group); + ASSERT(m_cacheHostSet.contains(urlHostHash(group.manifestURL()))); - if (ApplicationCache* newestCache = group->newestCache()) + if (auto* newestCache = group.newestCache()) remove(newestCache); - m_cachesInMemory.remove(group->manifestURL()); - m_cacheHostSet.remove(urlHostHash(group->manifestURL())); -} - -void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory) -{ - ASSERT(m_cacheDirectory.isNull()); - ASSERT(!cacheDirectory.isNull()); - - m_cacheDirectory = cacheDirectory; + m_cachesInMemory.remove(group.manifestURL()); + m_cacheHostSet.remove(urlHostHash(group.manifestURL())); } const String& ApplicationCacheStorage::cacheDirectory() const @@ -441,7 +421,7 @@ void ApplicationCacheStorage::setDefaultOriginQuota(int64_t quota) m_defaultOriginQuota = quota; } -bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin* origin, int64_t& quota) +bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin& origin, int64_t& quota) { SQLiteTransactionInProgressAutoCounter transactionCounter; @@ -449,14 +429,14 @@ bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin* orig // Using the count to determine if a record existed or not is a safe way to determine // if a quota of 0 is real, from the record, or from null. SQLiteStatement statement(m_database, "SELECT COUNT(quota), quota FROM Origins WHERE origin=?"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return false; - statement.bindText(1, origin->databaseIdentifier()); + statement.bindText(1, SecurityOriginData::fromSecurityOrigin(origin).databaseIdentifier()); int result = statement.step(); // Return the quota, or if it was null the default. - if (result == SQLResultRow) { + if (result == SQLITE_ROW) { bool wasNoRecord = statement.getColumnInt64(0) == 0; quota = wasNoRecord ? m_defaultOriginQuota : statement.getColumnInt64(1); return true; @@ -477,13 +457,13 @@ bool ApplicationCacheStorage::calculateUsageForOrigin(const SecurityOrigin* orig " INNER JOIN Origins ON CacheGroups.origin = Origins.origin" " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup" " WHERE Origins.origin=?"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return false; - statement.bindText(1, origin->databaseIdentifier()); + statement.bindText(1, SecurityOriginData::fromSecurityOrigin(*origin).databaseIdentifier()); int result = statement.step(); - if (result == SQLResultRow) { + if (result == SQLITE_ROW) { usage = statement.getColumnInt64(0); return true; } @@ -492,7 +472,7 @@ bool ApplicationCacheStorage::calculateUsageForOrigin(const SecurityOrigin* orig return false; } -bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin* origin, ApplicationCache* cache, int64_t& remainingSize) +bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin& origin, ApplicationCache* cache, int64_t& remainingSize) { SQLiteTransactionInProgressAutoCounter transactionCounter; @@ -520,17 +500,17 @@ bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(cons } SQLiteStatement statement(m_database, query); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return false; - statement.bindText(1, origin->databaseIdentifier()); + statement.bindText(1, SecurityOriginData::fromSecurityOrigin(origin).databaseIdentifier()); if (excludingCacheIdentifier != 0) statement.bindInt64(2, excludingCacheIdentifier); int result = statement.step(); // If the count was 0 that then we have to query the origin table directly // for its quota. Otherwise we can use the calculated value. - if (result == SQLResultRow) { + if (result == SQLITE_ROW) { int64_t numberOfCaches = statement.getColumnInt64(0); if (numberOfCaches == 0) calculateQuotaForOrigin(origin, remainingSize); @@ -555,11 +535,11 @@ bool ApplicationCacheStorage::storeUpdatedQuotaForOrigin(const SecurityOrigin* o return false; SQLiteStatement updateStatement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); - if (updateStatement.prepare() != SQLResultOk) + if (updateStatement.prepare() != SQLITE_OK) return false; updateStatement.bindInt64(1, quota); - updateStatement.bindText(2, origin->databaseIdentifier()); + updateStatement.bindText(2, SecurityOriginData::fromSecurityOrigin(*origin).databaseIdentifier()); return executeStatement(updateStatement); } @@ -590,7 +570,9 @@ void ApplicationCacheStorage::verifySchemaVersion() if (version == schemaVersion) return; - deleteTables(); + // Version will be 0 if we just created an empty file. Trying to delete tables would cause errors, because they don't exist yet. + if (version) + deleteTables(); // Update user version. SQLiteTransaction setDatabaseVersion(m_database); @@ -601,7 +583,7 @@ void ApplicationCacheStorage::verifySchemaVersion() ASSERT_UNUSED(unusedNumBytes, static_cast<int>(sizeof(userVersionSQL)) >= unusedNumBytes); SQLiteStatement statement(m_database, userVersionSQL); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return; executeStatement(statement); @@ -701,19 +683,19 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageID deleteCacheGroupRecord(group->manifestURL()); SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL, origin) VALUES (?, ?, ?)"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return false; statement.bindInt64(1, urlHostHash(group->manifestURL())); statement.bindText(2, group->manifestURL()); - statement.bindText(3, group->origin()->databaseIdentifier()); + statement.bindText(3, SecurityOriginData::fromSecurityOrigin(group->origin()).databaseIdentifier()); if (!executeStatement(statement)) return false; unsigned groupStorageID = static_cast<unsigned>(m_database.lastInsertRowID()); - if (!ensureOriginRecord(group->origin())) + if (!ensureOriginRecord(&group->origin())) return false; group->setStorageID(groupStorageID); @@ -729,7 +711,7 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo ASSERT(storageIDJournal); SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup, size) VALUES (?, ?)"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return false; statement.bindInt64(1, cache->group()->storageID()); @@ -741,28 +723,24 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo unsigned cacheStorageID = static_cast<unsigned>(m_database.lastInsertRowID()); // Store all resources - { - ApplicationCache::ResourceMap::const_iterator end = cache->end(); - for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { - unsigned oldStorageID = it->value->storageID(); - if (!store(it->value.get(), cacheStorageID)) - return false; + for (auto& resource : cache->resources().values()) { + unsigned oldStorageID = resource->storageID(); + if (!store(resource.get(), cacheStorageID)) + return false; - // Storing the resource succeeded. Log its old storageID in case - // it needs to be restored later. - storageIDJournal->add(it->value.get(), oldStorageID); - } + // Storing the resource succeeded. Log its old storageID in case + // it needs to be restored later. + storageIDJournal->add(resource.get(), oldStorageID); } // Store the online whitelist const Vector<URL>& onlineWhitelist = cache->onlineWhitelist(); { - size_t whitelistSize = onlineWhitelist.size(); - for (size_t i = 0; i < whitelistSize; ++i) { + for (auto& whitelistURL : onlineWhitelist) { SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)"); statement.prepare(); - statement.bindText(1, onlineWhitelist[i]); + statement.bindText(1, whitelistURL); statement.bindInt64(2, cacheStorageID); if (!executeStatement(statement)) @@ -785,13 +763,12 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJo // Store fallback URLs. const FallbackURLVector& fallbackURLs = cache->fallbackURLs(); { - size_t fallbackCount = fallbackURLs.size(); - for (size_t i = 0; i < fallbackCount; ++i) { + for (auto& fallbackURL : fallbackURLs) { SQLiteStatement statement(m_database, "INSERT INTO FallbackURLs (namespace, fallbackURL, cache) VALUES (?, ?, ?)"); statement.prepare(); - statement.bindText(1, fallbackURLs[i].first); - statement.bindText(2, fallbackURLs[i].second); + statement.bindText(1, fallbackURL.first); + statement.bindText(2, fallbackURL.second); statement.bindInt64(3, cacheStorageID); if (!executeStatement(statement)) @@ -817,7 +794,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned // First, insert the data SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data, path) VALUES (?, ?)"); - if (dataStatement.prepare() != SQLResultOk) + if (dataStatement.prepare() != SQLITE_OK) return false; @@ -827,12 +804,12 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned else if (shouldStoreResourceAsFlatFile(resource)) { // First, check to see if creating the flat file would violate the maximum total quota. We don't need // to check the per-origin quota here, as it was already checked in storeNewestCache(). - if (m_database.totalSize() + flatFileAreaSize() + resource->data()->size() > m_maximumSize) { + if (m_database.totalSize() + flatFileAreaSize() + resource->data().size() > m_maximumSize) { m_isMaximumSizeReached = true; return false; } - String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory); + String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName); makeAllDirectories(flatFileDirectory); String extension; @@ -850,8 +827,8 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned resource->setPath(fullPath); dataStatement.bindText(2, path); } else { - if (resource->data()->size()) - dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size()); + if (resource->data().size()) + dataStatement.bindBlob(1, resource->data().data(), resource->data().size()); } if (!dataStatement.executeCommand()) { @@ -869,18 +846,17 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned // Serialize the headers StringBuilder stringBuilder; - HTTPHeaderMap::const_iterator end = resource->response().httpHeaderFields().end(); - for (HTTPHeaderMap::const_iterator it = resource->response().httpHeaderFields().begin(); it!= end; ++it) { - stringBuilder.append(it->key); + for (const auto& header : resource->response().httpHeaderFields()) { + stringBuilder.append(header.key); stringBuilder.append(':'); - stringBuilder.append(it->value); + stringBuilder.append(header.value); stringBuilder.append('\n'); } String headers = stringBuilder.toString(); SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)"); - if (resourceStatement.prepare() != SQLResultOk) + if (resourceStatement.prepare() != SQLITE_OK) return false; // The same ApplicationCacheResource are used in ApplicationCacheResource::size() @@ -901,7 +877,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned // Finally, insert the cache entry SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)"); - if (entryStatement.prepare() != SQLResultOk) + if (entryStatement.prepare() != SQLITE_OK) return false; entryStatement.bindInt64(1, cacheStorageID); @@ -915,7 +891,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned // release the resource's data and free up a potentially large amount // of memory: if (!fullPath.isEmpty()) - resource->data()->clear(); + resource->data().clear(); resource->setStorageID(resourceId); return true; @@ -930,7 +906,7 @@ bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resourc // First, insert the data SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?"); - if (entryStatement.prepare() != SQLResultOk) + if (entryStatement.prepare() != SQLITE_OK) return false; entryStatement.bindInt64(1, resource->type()); @@ -963,7 +939,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, Applicat // A resource was added to the cache. Update the total data size for the cache. SQLiteStatement sizeUpdateStatement(m_database, "UPDATE Caches SET size=size+? WHERE id=?"); - if (sizeUpdateStatement.prepare() != SQLResultOk) + if (sizeUpdateStatement.prepare() != SQLITE_OK) return false; sizeUpdateStatement.bindInt64(1, resource->estimatedSizeInStorage()); @@ -980,10 +956,10 @@ bool ApplicationCacheStorage::ensureOriginRecord(const SecurityOrigin* origin) { ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress()); SQLiteStatement insertOriginStatement(m_database, "INSERT INTO Origins (origin, quota) VALUES (?, ?)"); - if (insertOriginStatement.prepare() != SQLResultOk) + if (insertOriginStatement.prepare() != SQLITE_OK) return false; - insertOriginStatement.bindText(1, origin->databaseIdentifier()); + insertOriginStatement.bindText(1, SecurityOriginData::fromSecurityOrigin(*origin).databaseIdentifier()); insertOriginStatement.bindInt64(2, m_defaultOriginQuota); if (!executeStatement(insertOriginStatement)) return false; @@ -995,7 +971,7 @@ bool ApplicationCacheStorage::checkOriginQuota(ApplicationCacheGroup* group, App { // Check if the oldCache with the newCache would reach the per-origin quota. int64_t remainingSpaceInOrigin; - const SecurityOrigin* origin = group->origin(); + auto& origin = group->origin(); if (calculateRemainingSizeForOriginExcludingCache(origin, oldCache, remainingSpaceInOrigin)) { if (remainingSpaceInOrigin < newCache->estimatedSizeInStorage()) { int64_t quota; @@ -1013,7 +989,7 @@ bool ApplicationCacheStorage::checkOriginQuota(ApplicationCacheGroup* group, App return true; } -bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, ApplicationCache* oldCache, FailureReason& failureReason) +bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup& group, ApplicationCache* oldCache, FailureReason& failureReason) { openDatabase(true); @@ -1029,24 +1005,24 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App // Check if this would reach the per-origin quota. int64_t totalSpaceNeededIgnored; - if (!checkOriginQuota(group, oldCache, group->newestCache(), totalSpaceNeededIgnored)) { + if (!checkOriginQuota(&group, oldCache, group.newestCache(), totalSpaceNeededIgnored)) { failureReason = OriginQuotaReached; return false; } GroupStorageIDJournal groupStorageIDJournal; - if (!group->storageID()) { + if (!group.storageID()) { // Store the group - if (!store(group, &groupStorageIDJournal)) { + if (!store(&group, &groupStorageIDJournal)) { checkForMaxSizeReached(); failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure; return false; } } - ASSERT(group->newestCache()); - ASSERT(!group->isObsolete()); - ASSERT(!group->newestCache()->storageID()); + ASSERT(group.newestCache()); + ASSERT(!group.isObsolete()); + ASSERT(!group.newestCache()->storageID()); // Log the storageID changes to the in-memory resource objects. The journal // object will roll them back automatically in case a database operation @@ -1054,7 +1030,7 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App ResourceStorageIDJournal resourceStorageIDJournal; // Store the newest cache - if (!store(group->newestCache(), &resourceStorageIDJournal)) { + if (!store(group.newestCache(), &resourceStorageIDJournal)) { checkForMaxSizeReached(); failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure; return false; @@ -1063,13 +1039,13 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App // Update the newest cache in the group. SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?"); - if (statement.prepare() != SQLResultOk) { + if (statement.prepare() != SQLITE_OK) { failureReason = DiskOrOperationFailure; return false; } - statement.bindInt64(1, group->newestCache()->storageID()); - statement.bindInt64(2, group->storageID()); + statement.bindInt64(1, group.newestCache()->storageID()); + statement.bindInt64(2, group.storageID()); if (!executeStatement(statement)) { failureReason = DiskOrOperationFailure; @@ -1082,22 +1058,24 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, App return true; } -bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) +bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup& group) { // Ignore the reason for failing, just attempt the store. FailureReason ignoredFailureReason; - return storeNewestCache(group, 0, ignoredFailureReason); + return storeNewestCache(group, nullptr, ignoredFailureReason); } -template <typename CharacterType> -static inline void parseHeader(const CharacterType* header, size_t headerLength, ResourceResponse& response) +template<typename CharacterType> +static inline void parseHeader(const CharacterType* header, unsigned headerLength, ResourceResponse& response) { - size_t pos = find(header, headerLength, ':'); - ASSERT(pos != notFound); - - AtomicString headerName = AtomicString(header, pos); - String headerValue = String(header + pos + 1, headerLength - pos - 1); - + ASSERT(find(header, headerLength, ':') != notFound); + unsigned colonPosition = find(header, headerLength, ':'); + + // Save memory by putting the header names into atomic strings so each is stored only once, + // even though the setHTTPHeaderField function does not require an atomic string. + AtomicString headerName { header, colonPosition }; + String headerValue { header + colonPosition + 1, headerLength - colonPosition - 1 }; + response.setHTTPHeaderField(headerName, headerValue); } @@ -1124,25 +1102,25 @@ static inline void parseHeaders(const String& headers, ResourceResponse& respons } } -PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID) +RefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID) { ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress()); SQLiteStatement cacheStatement(m_database, "SELECT url, statusCode, type, mimeType, textEncodingName, headers, CacheResourceData.data, CacheResourceData.path FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id " "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?"); - if (cacheStatement.prepare() != SQLResultOk) { + if (cacheStatement.prepare() != SQLITE_OK) { LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg()); - return 0; + return nullptr; } cacheStatement.bindInt64(1, storageID); - RefPtr<ApplicationCache> cache = ApplicationCache::create(); + auto cache = ApplicationCache::create(); - String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory); + String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName); int result; - while ((result = cacheStatement.step()) == SQLResultRow) { + while ((result = cacheStatement.step()) == SQLITE_ROW) { URL url(ParsedURLString, cacheStatement.getColumnText(0)); int httpStatusCode = cacheStatement.getColumnInt(1); @@ -1152,7 +1130,7 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage Vector<char> blob; cacheStatement.getColumnBlobAsVector(6, blob); - RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob); + auto data = SharedBuffer::adoptVector(blob); String path = cacheStatement.getColumnText(7); long long size = 0; @@ -1166,21 +1144,21 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage String mimeType = cacheStatement.getColumnText(3); String textEncodingName = cacheStatement.getColumnText(4); - ResourceResponse response(url, mimeType, size, textEncodingName, ""); + ResourceResponse response(url, mimeType, size, textEncodingName); response.setHTTPStatusCode(httpStatusCode); String headers = cacheStatement.getColumnText(5); parseHeaders(headers, response); - RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release(), path); + auto resource = ApplicationCacheResource::create(url, response, type, WTFMove(data), path); if (type & ApplicationCacheResource::Manifest) - cache->setManifestResource(resource.release()); + cache->setManifestResource(WTFMove(resource)); else - cache->addResource(resource.release()); + cache->addResource(WTFMove(resource)); } - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg()); if (!cache->manifestResource()) { @@ -1190,52 +1168,52 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage // Load the online whitelist SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?"); - if (whitelistStatement.prepare() != SQLResultOk) - return 0; + if (whitelistStatement.prepare() != SQLITE_OK) + return nullptr; whitelistStatement.bindInt64(1, storageID); Vector<URL> whitelist; - while ((result = whitelistStatement.step()) == SQLResultRow) + while ((result = whitelistStatement.step()) == SQLITE_ROW) whitelist.append(URL(ParsedURLString, whitelistStatement.getColumnText(0))); - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg()); cache->setOnlineWhitelist(whitelist); // Load online whitelist wildcard flag. SQLiteStatement whitelistWildcardStatement(m_database, "SELECT wildcard FROM CacheAllowsAllNetworkRequests WHERE cache=?"); - if (whitelistWildcardStatement.prepare() != SQLResultOk) - return 0; + if (whitelistWildcardStatement.prepare() != SQLITE_OK) + return nullptr; whitelistWildcardStatement.bindInt64(1, storageID); result = whitelistWildcardStatement.step(); - if (result != SQLResultRow) + if (result != SQLITE_ROW) LOG_ERROR("Could not load cache online whitelist wildcard flag, error \"%s\"", m_database.lastErrorMsg()); cache->setAllowsAllNetworkRequests(whitelistWildcardStatement.getColumnInt64(0)); - if (whitelistWildcardStatement.step() != SQLResultDone) + if (whitelistWildcardStatement.step() != SQLITE_DONE) LOG_ERROR("Too many rows for online whitelist wildcard flag"); // Load fallback URLs. SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?"); - if (fallbackStatement.prepare() != SQLResultOk) - return 0; + if (fallbackStatement.prepare() != SQLITE_OK) + return nullptr; fallbackStatement.bindInt64(1, storageID); FallbackURLVector fallbackURLs; - while ((result = fallbackStatement.step()) == SQLResultRow) + while ((result = fallbackStatement.step()) == SQLITE_ROW) fallbackURLs.append(std::make_pair(URL(ParsedURLString, fallbackStatement.getColumnText(0)), URL(ParsedURLString, fallbackStatement.getColumnText(1)))); - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg()); cache->setFallbackURLs(fallbackURLs); cache->setStorageID(storageID); - return cache.release(); + return WTFMove(cache); } void ApplicationCacheStorage::remove(ApplicationCache* cache) @@ -1254,7 +1232,7 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache) // All associated data will be deleted by database triggers. SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return; statement.bindInt64(1, cache->storageID()); @@ -1265,7 +1243,7 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache) if (cache->group()->newestCache() == cache) { // Currently, there are no triggers on the cache group, which is why the cache had to be removed separately above. SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?"); - if (groupStatement.prepare() != SQLResultOk) + if (groupStatement.prepare() != SQLITE_OK) return; groupStatement.bindInt64(1, cache->group()->storageID()); @@ -1294,10 +1272,9 @@ void ApplicationCacheStorage::empty() // Clear the storage IDs for the caches in memory. // The caches will still work, but cached resources will not be saved to disk // until a cache update process has been initiated. - CacheGroupMap::const_iterator end = m_cachesInMemory.end(); - for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) - it->value->clearStorageID(); - + for (auto* group : m_cachesInMemory.values()) + group->clearStorageID(); + checkForDeletedResources(); } @@ -1313,7 +1290,7 @@ bool ApplicationCacheStorage::shouldStoreResourceAsFlatFile(ApplicationCacheReso || resource->response().mimeType().startsWith("video/", false); } -bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer* data, const String& directory, String& path, const String& fileExtension) +bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer& data, const String& directory, String& path, const String& fileExtension) { String fullPath; @@ -1332,10 +1309,10 @@ bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer* dat if (!handle) return false; - int64_t writtenBytes = writeToFile(handle, data->data(), data->size()); + int64_t writtenBytes = writeToFile(handle, data.data(), data.size()); closeFile(handle); - if (writtenBytes != static_cast<int64_t>(data->size())) { + if (writtenBytes != static_cast<int64_t>(data.size())) { deleteFile(fullPath); return false; } @@ -1343,45 +1320,7 @@ bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer* dat return true; } -bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost* cacheHost) -{ - SQLiteTransactionInProgressAutoCounter transactionCounter; - - ApplicationCache* cache = cacheHost->applicationCache(); - if (!cache) - return true; - - // Create a new cache. - RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create(); - - cacheCopy->setOnlineWhitelist(cache->onlineWhitelist()); - cacheCopy->setFallbackURLs(cache->fallbackURLs()); - - // Traverse the cache and add copies of all resources. - ApplicationCache::ResourceMap::const_iterator end = cache->end(); - for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { - ApplicationCacheResource* resource = it->value.get(); - - RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data(), resource->path()); - - cacheCopy->addResource(resourceCopy.release()); - } - - // Now create a new cache group. - OwnPtr<ApplicationCacheGroup> groupCopy(adoptPtr(new ApplicationCacheGroup(cache->group()->manifestURL(), true))); - - groupCopy->setNewestCache(cacheCopy); - - ApplicationCacheStorage copyStorage; - copyStorage.setCacheDirectory(cacheDirectory); - - // Empty the cache in case something was there before. - copyStorage.empty(); - - return copyStorage.storeNewestCache(groupCopy.get()); -} - -bool ApplicationCacheStorage::manifestURLs(Vector<URL>* urls) +bool ApplicationCacheStorage::getManifestURLs(Vector<URL>* urls) { SQLiteTransactionInProgressAutoCounter transactionCounter; @@ -1392,10 +1331,10 @@ bool ApplicationCacheStorage::manifestURLs(Vector<URL>* urls) SQLiteStatement selectURLs(m_database, "SELECT manifestURL FROM CacheGroups"); - if (selectURLs.prepare() != SQLResultOk) + if (selectURLs.prepare() != SQLITE_OK) return false; - while (selectURLs.step() == SQLResultRow) + while (selectURLs.step() == SQLITE_ROW) urls->append(URL(ParsedURLString, selectURLs.getColumnText(0))); return true; @@ -1411,16 +1350,16 @@ bool ApplicationCacheStorage::cacheGroupSize(const String& manifestURL, int64_t* return false; SQLiteStatement statement(m_database, "SELECT sum(Caches.size) FROM Caches INNER JOIN CacheGroups ON Caches.cacheGroup=CacheGroups.id WHERE CacheGroups.manifestURL=?"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLITE_OK) return false; statement.bindText(1, manifestURL); int result = statement.step(); - if (result == SQLResultDone) + if (result == SQLITE_DONE) return false; - if (result != SQLResultRow) { + if (result != SQLITE_ROW) { LOG_ERROR("Could not get the size of the cache group, error \"%s\"", m_database.lastErrorMsg()); return false; } @@ -1433,23 +1372,23 @@ bool ApplicationCacheStorage::deleteCacheGroupRecord(const String& manifestURL) { ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress()); SQLiteStatement idStatement(m_database, "SELECT id FROM CacheGroups WHERE manifestURL=?"); - if (idStatement.prepare() != SQLResultOk) + if (idStatement.prepare() != SQLITE_OK) return false; idStatement.bindText(1, manifestURL); int result = idStatement.step(); - if (result != SQLResultRow) + if (result != SQLITE_ROW) return false; int64_t groupId = idStatement.getColumnInt64(0); SQLiteStatement cacheStatement(m_database, "DELETE FROM Caches WHERE cacheGroup=?"); - if (cacheStatement.prepare() != SQLResultOk) + if (cacheStatement.prepare() != SQLITE_OK) return false; SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?"); - if (groupStatement.prepare() != SQLResultOk) + if (groupStatement.prepare() != SQLITE_OK) return false; cacheStatement.bindInt64(1, groupId); @@ -1464,10 +1403,11 @@ bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL) SQLiteTransactionInProgressAutoCounter transactionCounter; SQLiteTransaction deleteTransaction(m_database); + // Check to see if the group is in memory. - ApplicationCacheGroup* group = m_cachesInMemory.get(manifestURL); + auto* group = m_cachesInMemory.get(manifestURL); if (group) - cacheGroupMadeObsolete(group); + cacheGroupMadeObsolete(*group); else { // The cache group is not in memory, so remove it from the disk. openDatabase(false); @@ -1480,9 +1420,9 @@ bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL) } deleteTransaction.commit(); - + checkForDeletedResources(); - + return true; } @@ -1499,7 +1439,7 @@ void ApplicationCacheStorage::vacuumDatabaseFile() void ApplicationCacheStorage::checkForMaxSizeReached() { - if (m_database.lastError() == SQLResultFull) + if (m_database.lastError() == SQLITE_FULL) m_isMaximumSizeReached = true; } @@ -1516,10 +1456,10 @@ void ApplicationCacheStorage::checkForDeletedResources() "ON DeletedCacheResources.path = CacheResourceData.path " "WHERE (SELECT DeletedCacheResources.path == CacheResourceData.path) IS NULL"); - if (selectPaths.prepare() != SQLResultOk) + if (selectPaths.prepare() != SQLITE_OK) return; - if (selectPaths.step() != SQLResultRow) + if (selectPaths.step() != SQLITE_ROW) return; do { @@ -1527,7 +1467,7 @@ void ApplicationCacheStorage::checkForDeletedResources() if (path.isEmpty()) continue; - String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory); + String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName); String fullPath = pathByAppendingComponent(flatFileDirectory, path); // Don't exit the flatFileDirectory! This should only happen if the "path" entry contains a directory @@ -1536,7 +1476,7 @@ void ApplicationCacheStorage::checkForDeletedResources() continue; deleteFile(fullPath); - } while (selectPaths.step() == SQLResultRow); + } while (selectPaths.step() == SQLITE_ROW); executeSQLCommand("DELETE FROM DeletedCacheResources"); } @@ -1549,14 +1489,14 @@ long long ApplicationCacheStorage::flatFileAreaSize() SQLiteStatement selectPaths(m_database, "SELECT path FROM CacheResourceData WHERE path NOT NULL"); - if (selectPaths.prepare() != SQLResultOk) { + if (selectPaths.prepare() != SQLITE_OK) { LOG_ERROR("Could not load flat file cache resource data, error \"%s\"", m_database.lastErrorMsg()); return 0; } long long totalSize = 0; - String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, flatFileSubdirectory); - while (selectPaths.step() == SQLResultRow) { + String flatFileDirectory = pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName); + while (selectPaths.step() == SQLITE_ROW) { String path = selectPaths.getColumnText(0); String fullPath = pathByAppendingComponent(flatFileDirectory, path); long long pathSize = 0; @@ -1571,18 +1511,12 @@ long long ApplicationCacheStorage::flatFileAreaSize() void ApplicationCacheStorage::getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>>& origins) { Vector<URL> urls; - if (!manifestURLs(&urls)) { - LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs"); - return; - } + getManifestURLs(&urls); // Multiple manifest URLs might share the same SecurityOrigin, so we might be creating extra, wasted origins here. // The current schema doesn't allow for a more efficient way of building this list. - size_t count = urls.size(); - for (size_t i = 0; i < count; ++i) { - RefPtr<SecurityOrigin> origin = SecurityOrigin::create(urls[i]); - origins.add(origin); - } + for (auto& url : urls) + origins.add(SecurityOrigin::create(url)); } void ApplicationCacheStorage::deleteAllEntries() @@ -1591,18 +1525,57 @@ void ApplicationCacheStorage::deleteAllEntries() vacuumDatabaseFile(); } -ApplicationCacheStorage::ApplicationCacheStorage() - : m_maximumSize(ApplicationCacheStorage::noQuota()) +void ApplicationCacheStorage::deleteAllCaches() +{ + HashSet<RefPtr<SecurityOrigin>> origins; + + getOriginsWithCache(origins); + for (auto& origin : origins) + deleteCacheForOrigin(*origin); + + vacuumDatabaseFile(); +} + +void ApplicationCacheStorage::deleteCacheForOrigin(const SecurityOrigin& securityOrigin) +{ + Vector<URL> urls; + if (!getManifestURLs(&urls)) { + LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs"); + return; + } + + URL originURL(URL(), securityOrigin.toString()); + + for (const auto& url : urls) { + if (!protocolHostAndPortAreEqual(url, originURL)) + continue; + + if (auto* group = findInMemoryCacheGroup(url)) + group->makeObsolete(); + else + deleteCacheGroup(url); + } +} + +int64_t ApplicationCacheStorage::diskUsageForOrigin(const SecurityOrigin& securityOrigin) +{ + int64_t usage = 0; + calculateUsageForOrigin(&securityOrigin, usage); + return usage; +} + +ApplicationCacheStorage::ApplicationCacheStorage(const String& cacheDirectory, const String& flatFileSubdirectoryName) + : m_cacheDirectory(cacheDirectory) + , m_flatFileSubdirectoryName(flatFileSubdirectoryName) + , m_maximumSize(ApplicationCacheStorage::noQuota()) , m_isMaximumSizeReached(false) , m_defaultOriginQuota(ApplicationCacheStorage::noQuota()) { } -ApplicationCacheStorage& cacheStorage() +Ref<ApplicationCacheStorage> ApplicationCacheStorage::create(const String& cacheDirectory, const String& flatFileSubdirectoryName) { - DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ()); - - return storage; + return adoptRef(*new ApplicationCacheStorage(cacheDirectory, flatFileSubdirectoryName)); } -} // namespace WebCore +} diff --git a/Source/WebCore/loader/appcache/ApplicationCacheStorage.h b/Source/WebCore/loader/appcache/ApplicationCacheStorage.h index 76eaa3ecf..9d17a1bcd 100644 --- a/Source/WebCore/loader/appcache/ApplicationCacheStorage.h +++ b/Source/WebCore/loader/appcache/ApplicationCacheStorage.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2010, 2011 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ApplicationCacheStorage_h -#define ApplicationCacheStorage_h +#pragma once #include "SecurityOriginHash.h" #include "SQLiteDatabase.h" @@ -42,10 +41,9 @@ class ApplicationCacheResource; class URL; class SecurityOrigin; class SharedBuffer; -template <class T> class StorageIDJournal; +template<typename> class StorageIDJournal; -class ApplicationCacheStorage { - WTF_MAKE_NONCOPYABLE(ApplicationCacheStorage); WTF_MAKE_FAST_ALLOCATED; +class ApplicationCacheStorage : public RefCounted<ApplicationCacheStorage> { public: enum FailureReason { OriginQuotaReached, @@ -53,20 +51,21 @@ public: DiskOrOperationFailure }; - void setCacheDirectory(const String&); + WEBCORE_EXPORT static Ref<ApplicationCacheStorage> create(const String& cacheDirectory, const String& flatFileSubdirectoryName); + const String& cacheDirectory() const; - void setMaximumSize(int64_t size); - int64_t maximumSize() const; + WEBCORE_EXPORT void setMaximumSize(int64_t size); + WEBCORE_EXPORT int64_t maximumSize() const; bool isMaximumSizeReached() const; int64_t spaceNeeded(int64_t cacheToSave); int64_t defaultOriginQuota() const { return m_defaultOriginQuota; } - void setDefaultOriginQuota(int64_t quota); - bool calculateUsageForOrigin(const SecurityOrigin*, int64_t& usage); - bool calculateQuotaForOrigin(const SecurityOrigin*, int64_t& quota); - bool calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin*, ApplicationCache*, int64_t& remainingSize); - bool storeUpdatedQuotaForOrigin(const SecurityOrigin*, int64_t quota); + WEBCORE_EXPORT void setDefaultOriginQuota(int64_t quota); + WEBCORE_EXPORT bool calculateUsageForOrigin(const SecurityOrigin*, int64_t& usage); + WEBCORE_EXPORT bool calculateQuotaForOrigin(const SecurityOrigin&, int64_t& quota); + bool calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin&, ApplicationCache*, int64_t& remainingSize); + WEBCORE_EXPORT bool storeUpdatedQuotaForOrigin(const SecurityOrigin*, int64_t quota); bool checkOriginQuota(ApplicationCacheGroup*, ApplicationCache* oldCache, ApplicationCache* newCache, int64_t& totalSpaceNeeded); ApplicationCacheGroup* cacheGroupForURL(const URL&); // Cache to load a main resource from. @@ -74,38 +73,47 @@ public: ApplicationCacheGroup* findOrCreateCacheGroup(const URL& manifestURL); ApplicationCacheGroup* findInMemoryCacheGroup(const URL& manifestURL) const; - void cacheGroupDestroyed(ApplicationCacheGroup*); - void cacheGroupMadeObsolete(ApplicationCacheGroup*); - - bool storeNewestCache(ApplicationCacheGroup*, ApplicationCache* oldCache, FailureReason& failureReason); - bool storeNewestCache(ApplicationCacheGroup*); // Updates the cache group, but doesn't remove old cache. + void cacheGroupDestroyed(ApplicationCacheGroup&); + void cacheGroupMadeObsolete(ApplicationCacheGroup&); + + bool storeNewestCache(ApplicationCacheGroup&, ApplicationCache* oldCache, FailureReason&); + bool storeNewestCache(ApplicationCacheGroup&); // Updates the cache group, but doesn't remove old cache. bool store(ApplicationCacheResource*, ApplicationCache*); bool storeUpdatedType(ApplicationCacheResource*, ApplicationCache*); // Removes the group if the cache to be removed is the newest one (so, storeNewestCache() needs to be called beforehand when updating). void remove(ApplicationCache*); - - void empty(); - - static bool storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost*); - bool manifestURLs(Vector<URL>* urls); + WEBCORE_EXPORT void empty(); + + bool getManifestURLs(Vector<URL>* urls); bool cacheGroupSize(const String& manifestURL, int64_t* size); bool deleteCacheGroup(const String& manifestURL); - void vacuumDatabaseFile(); + WEBCORE_EXPORT void vacuumDatabaseFile(); + + WEBCORE_EXPORT void getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash>&); + WEBCORE_EXPORT void deleteAllEntries(); + + // FIXME: This should be consolidated with deleteAllEntries(). + WEBCORE_EXPORT void deleteAllCaches(); + + // FIXME: This should be consolidated with deleteCacheGroup(). + WEBCORE_EXPORT void deleteCacheForOrigin(const SecurityOrigin&); - void getOriginsWithCache(HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash>&); - void deleteAllEntries(); + // FIXME: This should be consolidated with calculateUsageForOrigin(). + WEBCORE_EXPORT int64_t diskUsageForOrigin(const SecurityOrigin&); static int64_t unknownQuota() { return -1; } static int64_t noQuota() { return std::numeric_limits<int64_t>::max(); } + private: - ApplicationCacheStorage(); - PassRefPtr<ApplicationCache> loadCache(unsigned storageID); + ApplicationCacheStorage(const String& cacheDirectory, const String& flatFileSubdirectoryName); + + RefPtr<ApplicationCache> loadCache(unsigned storageID); ApplicationCacheGroup* loadCacheGroup(const URL& manifestURL); - typedef StorageIDJournal<ApplicationCacheResource> ResourceStorageIDJournal; - typedef StorageIDJournal<ApplicationCacheGroup> GroupStorageIDJournal; + using ResourceStorageIDJournal = StorageIDJournal<ApplicationCacheResource>; + using GroupStorageIDJournal = StorageIDJournal<ApplicationCacheGroup>; bool store(ApplicationCacheGroup*, GroupStorageIDJournal*); bool store(ApplicationCache*, ResourceStorageIDJournal*); @@ -115,7 +123,7 @@ private: bool ensureOriginRecord(const SecurityOrigin*); bool shouldStoreResourceAsFlatFile(ApplicationCacheResource*); void deleteTables(); - bool writeDataToUniqueFileInDirectory(SharedBuffer*, const String& directory, String& outFilename, const String& fileExtension); + bool writeDataToUniqueFileInDirectory(SharedBuffer&, const String& directory, String& outFilename, const String& fileExtension); void loadManifestHostHashes(); @@ -129,8 +137,9 @@ private: void checkForMaxSizeReached(); void checkForDeletedResources(); long long flatFileAreaSize(); - - String m_cacheDirectory; + + const String m_cacheDirectory; + const String m_flatFileSubdirectoryName; String m_cacheFile; int64_t m_maximumSize; @@ -144,14 +153,9 @@ private: // we keep a hash set of the hosts of the manifest URLs of all non-obsolete cache groups. HashCountedSet<unsigned, AlreadyHashed> m_cacheHostSet; - typedef HashMap<String, ApplicationCacheGroup*> CacheGroupMap; - CacheGroupMap m_cachesInMemory; // Excludes obsolete cache groups. + HashMap<String, ApplicationCacheGroup*> m_cachesInMemory; // Excludes obsolete cache groups. - friend ApplicationCacheStorage& cacheStorage(); + friend class WTF::NeverDestroyed<ApplicationCacheStorage>; }; - -ApplicationCacheStorage& cacheStorage(); - -} // namespace WebCore -#endif // ApplicationCacheStorage_h +} // namespace WebCore diff --git a/Source/WebCore/loader/appcache/DOMApplicationCache.cpp b/Source/WebCore/loader/appcache/DOMApplicationCache.cpp index 56437a80d..ed8fad8f6 100644 --- a/Source/WebCore/loader/appcache/DOMApplicationCache.cpp +++ b/Source/WebCore/loader/appcache/DOMApplicationCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,110 +29,84 @@ #include "ApplicationCacheHost.h" #include "Document.h" #include "DocumentLoader.h" -#include "Event.h" -#include "EventException.h" -#include "EventListener.h" -#include "EventNames.h" #include "ExceptionCode.h" #include "Frame.h" -#include "FrameLoader.h" namespace WebCore { -DOMApplicationCache::DOMApplicationCache(Frame* frame) - : DOMWindowProperty(frame) +DOMApplicationCache::DOMApplicationCache(Frame& frame) + : DOMWindowProperty(&frame) { - ApplicationCacheHost* cacheHost = applicationCacheHost(); - if (cacheHost) - cacheHost->setDOMApplicationCache(this); + if (auto* host = applicationCacheHost()) + host->setDOMApplicationCache(this); } -void DOMApplicationCache::disconnectFrameForPageCache() +void DOMApplicationCache::disconnectFrameForDocumentSuspension() { - if (ApplicationCacheHost* cacheHost = applicationCacheHost()) - cacheHost->setDOMApplicationCache(0); - DOMWindowProperty::disconnectFrameForPageCache(); + if (auto* host = applicationCacheHost()) + host->setDOMApplicationCache(nullptr); + DOMWindowProperty::disconnectFrameForDocumentSuspension(); } -void DOMApplicationCache::reconnectFrameFromPageCache(Frame* frame) +void DOMApplicationCache::reconnectFrameFromDocumentSuspension(Frame* frame) { - DOMWindowProperty::reconnectFrameFromPageCache(frame); - if (ApplicationCacheHost* cacheHost = applicationCacheHost()) - cacheHost->setDOMApplicationCache(this); + DOMWindowProperty::reconnectFrameFromDocumentSuspension(frame); + if (auto* host = applicationCacheHost()) + host->setDOMApplicationCache(this); } void DOMApplicationCache::willDestroyGlobalObjectInFrame() { - if (ApplicationCacheHost* cacheHost = applicationCacheHost()) - cacheHost->setDOMApplicationCache(0); + if (auto* host = applicationCacheHost()) + host->setDOMApplicationCache(nullptr); DOMWindowProperty::willDestroyGlobalObjectInFrame(); } ApplicationCacheHost* DOMApplicationCache::applicationCacheHost() const { - if (!m_frame || !m_frame->loader().documentLoader()) - return 0; - return m_frame->loader().documentLoader()->applicationCacheHost(); + if (!m_frame) + return nullptr; + auto* documentLoader = m_frame->loader().documentLoader(); + if (!documentLoader) + return nullptr; + return &documentLoader->applicationCacheHost(); } unsigned short DOMApplicationCache::status() const { - ApplicationCacheHost* cacheHost = applicationCacheHost(); - if (!cacheHost) + auto* host = applicationCacheHost(); + if (!host) return ApplicationCacheHost::UNCACHED; - return cacheHost->status(); + return host->status(); } -void DOMApplicationCache::update(ExceptionCode& ec) +ExceptionOr<void> DOMApplicationCache::update() { - ApplicationCacheHost* cacheHost = applicationCacheHost(); - if (!cacheHost || !cacheHost->update()) - ec = INVALID_STATE_ERR; + auto* host = applicationCacheHost(); + if (!host || !host->update()) + return Exception { INVALID_STATE_ERR }; + return { }; } -void DOMApplicationCache::swapCache(ExceptionCode& ec) +ExceptionOr<void> DOMApplicationCache::swapCache() { - ApplicationCacheHost* cacheHost = applicationCacheHost(); - if (!cacheHost || !cacheHost->swapCache()) - ec = INVALID_STATE_ERR; + auto* host = applicationCacheHost(); + if (!host || !host->swapCache()) + return Exception { INVALID_STATE_ERR }; + return { }; } void DOMApplicationCache::abort() { - ApplicationCacheHost* cacheHost = applicationCacheHost(); - if (cacheHost) - cacheHost->abort(); + if (auto* host = applicationCacheHost()) + host->abort(); } ScriptExecutionContext* DOMApplicationCache::scriptExecutionContext() const { - if (m_frame) - return m_frame->document(); - return 0; -} - -const AtomicString& DOMApplicationCache::toEventType(ApplicationCacheHost::EventID id) -{ - switch (id) { - case ApplicationCacheHost::CHECKING_EVENT: - return eventNames().checkingEvent; - case ApplicationCacheHost::ERROR_EVENT: - return eventNames().errorEvent; - case ApplicationCacheHost::NOUPDATE_EVENT: - return eventNames().noupdateEvent; - case ApplicationCacheHost::DOWNLOADING_EVENT: - return eventNames().downloadingEvent; - case ApplicationCacheHost::PROGRESS_EVENT: - return eventNames().progressEvent; - case ApplicationCacheHost::UPDATEREADY_EVENT: - return eventNames().updatereadyEvent; - case ApplicationCacheHost::CACHED_EVENT: - return eventNames().cachedEvent; - case ApplicationCacheHost::OBSOLETE_EVENT: - return eventNames().obsoleteEvent; - } - ASSERT_NOT_REACHED(); - return eventNames().errorEvent; + if (!m_frame) + return nullptr; + return m_frame->document(); } } // namespace WebCore diff --git a/Source/WebCore/loader/appcache/DOMApplicationCache.h b/Source/WebCore/loader/appcache/DOMApplicationCache.h index 910cb7502..cf6d2a9d9 100644 --- a/Source/WebCore/loader/appcache/DOMApplicationCache.h +++ b/Source/WebCore/loader/appcache/DOMApplicationCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,68 +23,43 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMApplicationCache_h -#define DOMApplicationCache_h +#pragma once -#include "ApplicationCacheHost.h" #include "DOMWindowProperty.h" -#include "EventNames.h" #include "EventTarget.h" -#include "ScriptWrappable.h" -#include <wtf/Forward.h> -#include <wtf/HashMap.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> -#include <wtf/text/AtomicStringHash.h> namespace WebCore { +class ApplicationCacheHost; class Frame; -class URL; -class DOMApplicationCache final : public ScriptWrappable, public RefCounted<DOMApplicationCache>, public EventTargetWithInlineData, public DOMWindowProperty { +class DOMApplicationCache final : public RefCounted<DOMApplicationCache>, public EventTargetWithInlineData, public DOMWindowProperty { public: - static PassRefPtr<DOMApplicationCache> create(Frame* frame) { return adoptRef(new DOMApplicationCache(frame)); } + static Ref<DOMApplicationCache> create(Frame& frame) { return adoptRef(*new DOMApplicationCache(frame)); } virtual ~DOMApplicationCache() { ASSERT(!m_frame); } - virtual void disconnectFrameForPageCache() override; - virtual void reconnectFrameFromPageCache(Frame*) override; - virtual void willDestroyGlobalObjectInFrame() override; - unsigned short status() const; - void update(ExceptionCode&); - void swapCache(ExceptionCode&); + ExceptionOr<void> update(); + ExceptionOr<void> swapCache(); void abort(); - using RefCounted<DOMApplicationCache>::ref; - using RefCounted<DOMApplicationCache>::deref; - - // Explicitly named attribute event listener helpers - - DEFINE_ATTRIBUTE_EVENT_LISTENER(checking); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - DEFINE_ATTRIBUTE_EVENT_LISTENER(noupdate); - DEFINE_ATTRIBUTE_EVENT_LISTENER(downloading); - DEFINE_ATTRIBUTE_EVENT_LISTENER(progress); - DEFINE_ATTRIBUTE_EVENT_LISTENER(updateready); - DEFINE_ATTRIBUTE_EVENT_LISTENER(cached); - DEFINE_ATTRIBUTE_EVENT_LISTENER(obsolete); + using RefCounted::ref; + using RefCounted::deref; - virtual EventTargetInterface eventTargetInterface() const override { return DOMApplicationCacheEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override; +private: + explicit DOMApplicationCache(Frame&); - static const AtomicString& toEventType(ApplicationCacheHost::EventID id); + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } -private: - explicit DOMApplicationCache(Frame*); + EventTargetInterface eventTargetInterface() const final { return DOMApplicationCacheEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final; - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } + void disconnectFrameForDocumentSuspension() final; + void reconnectFrameFromDocumentSuspension(Frame*) final; + void willDestroyGlobalObjectInFrame() final; ApplicationCacheHost* applicationCacheHost() const; }; } // namespace WebCore - -#endif // DOMApplicationCache_h diff --git a/Source/WebCore/loader/appcache/DOMApplicationCache.idl b/Source/WebCore/loader/appcache/DOMApplicationCache.idl index 9622c128b..1799f2e38 100644 --- a/Source/WebCore/loader/appcache/DOMApplicationCache.idl +++ b/Source/WebCore/loader/appcache/DOMApplicationCache.idl @@ -24,11 +24,10 @@ */ [ - NoInterfaceObject, - EventTarget, DoNotCheckConstants, GenerateIsReachable=ImplFrame, -] interface DOMApplicationCache { + InterfaceName=ApplicationCache, +] interface DOMApplicationCache : EventTarget { // update status const unsigned short UNCACHED = 0; const unsigned short IDLE = 1; @@ -38,27 +37,17 @@ const unsigned short OBSOLETE = 5; readonly attribute unsigned short status; - [RaisesException] void update(); - [RaisesException] void swapCache(); + [MayThrowException] void update(); + [MayThrowException] void swapCache(); void abort(); - // events - attribute EventListener onchecking; - attribute EventListener onerror; - attribute EventListener onnoupdate; - attribute EventListener ondownloading; - attribute EventListener onprogress; - attribute EventListener onupdateready; - attribute EventListener oncached; - attribute EventListener onobsolete; - - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); + attribute EventHandler onchecking; + attribute EventHandler onerror; + attribute EventHandler onnoupdate; + attribute EventHandler ondownloading; + attribute EventHandler onprogress; + attribute EventHandler onupdateready; + attribute EventHandler oncached; + attribute EventHandler onobsolete; }; diff --git a/Source/WebCore/loader/appcache/ManifestParser.cpp b/Source/WebCore/loader/appcache/ManifestParser.cpp index a47aa150e..5c151359d 100644 --- a/Source/WebCore/loader/appcache/ManifestParser.cpp +++ b/Source/WebCore/loader/appcache/ManifestParser.cpp @@ -28,6 +28,7 @@ #include "TextResourceDecoder.h" #include "URL.h" +#include <wtf/text/StringView.h> #include <wtf/unicode/CharacterNames.h> namespace WebCore { @@ -43,9 +44,7 @@ bool parseManifest(const URL& manifestURL, const char* data, int length, Manifes Mode mode = Explicit; - RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/cache-manifest", "UTF-8"); - String s = decoder->decode(data, length); - s.append(decoder->flush()); + String s = TextResourceDecoder::create("text/cache-manifest", "UTF-8")->decodeAndFlush(data, length); // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by TextResourceDecoder). // Example: "CACHE MANIFEST #comment" is a valid signature. @@ -53,8 +52,10 @@ bool parseManifest(const URL& manifestURL, const char* data, int length, Manifes if (!s.startsWith("CACHE MANIFEST")) return false; - const UChar* end = s.deprecatedCharacters() + s.length(); - const UChar* p = s.deprecatedCharacters() + 14; // "CACHE MANIFEST" is 14 characters. + StringView manifestAfterSignature = StringView(s).substring(14); // "CACHE MANIFEST" is 14 characters. + auto upconvertedCharacters = manifestAfterSignature.upconvertedCharacters(); + const UChar* p = upconvertedCharacters; + const UChar* end = p + manifestAfterSignature.length(); if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') return false; @@ -99,28 +100,28 @@ bool parseManifest(const URL& manifestURL, const char* data, int length, Manifes else if (mode == Unknown) continue; else if (mode == Explicit || mode == OnlineWhitelist) { - const UChar* p = line.deprecatedCharacters(); + auto upconvertedLineCharacters = StringView(line).upconvertedCharacters(); + const UChar* p = upconvertedLineCharacters; const UChar* lineEnd = p + line.length(); // Look for whitespace separating the URL from subsequent ignored tokens. while (p < lineEnd && *p != '\t' && *p != ' ') p++; - if (mode == OnlineWhitelist && p - line.deprecatedCharacters() == 1 && *line.deprecatedCharacters() == '*') { + if (mode == OnlineWhitelist && p - upconvertedLineCharacters == 1 && line[0] == '*') { // Wildcard was found. manifest.allowAllNetworkRequests = true; continue; } - URL url(manifestURL, String(line.deprecatedCharacters(), p - line.deprecatedCharacters())); + URL url(manifestURL, line.substring(0, p - upconvertedLineCharacters)); if (!url.isValid()) continue; - if (url.hasFragmentIdentifier()) - url.removeFragmentIdentifier(); + url.removeFragmentIdentifier(); - if (!equalIgnoringCase(url.protocol(), manifestURL.protocol())) + if (!equalIgnoringASCIICase(url.protocol(), manifestURL.protocol())) continue; if (mode == Explicit && manifestURL.protocolIs("https") && !protocolHostAndPortAreEqual(manifestURL, url)) @@ -132,7 +133,8 @@ bool parseManifest(const URL& manifestURL, const char* data, int length, Manifes manifest.onlineWhitelistedURLs.append(url); } else if (mode == Fallback) { - const UChar* p = line.deprecatedCharacters(); + auto upconvertedLineCharacters = StringView(line).upconvertedCharacters(); + const UChar* p = upconvertedLineCharacters; const UChar* lineEnd = p + line.length(); // Look for whitespace separating the two URLs @@ -144,11 +146,10 @@ bool parseManifest(const URL& manifestURL, const char* data, int length, Manifes continue; } - URL namespaceURL(manifestURL, String(line.deprecatedCharacters(), p - line.deprecatedCharacters())); + URL namespaceURL(manifestURL, line.substring(0, p - upconvertedLineCharacters)); if (!namespaceURL.isValid()) continue; - if (namespaceURL.hasFragmentIdentifier()) - namespaceURL.removeFragmentIdentifier(); + namespaceURL.removeFragmentIdentifier(); if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL)) continue; @@ -165,8 +166,7 @@ bool parseManifest(const URL& manifestURL, const char* data, int length, Manifes URL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart)); if (!fallbackURL.isValid()) continue; - if (fallbackURL.hasFragmentIdentifier()) - fallbackURL.removeFragmentIdentifier(); + fallbackURL.removeFragmentIdentifier(); if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL)) continue; diff --git a/Source/WebCore/loader/appcache/ManifestParser.h b/Source/WebCore/loader/appcache/ManifestParser.h index 28315dde6..b28249ebe 100644 --- a/Source/WebCore/loader/appcache/ManifestParser.h +++ b/Source/WebCore/loader/appcache/ManifestParser.h @@ -23,24 +23,22 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ManifestParser_h -#define ManifestParser_h +#pragma once #include "ApplicationCache.h" +#include <wtf/HashSet.h> namespace WebCore { - class URL; +class URL; - struct Manifest { - Vector<URL> onlineWhitelistedURLs; - HashSet<String> explicitURLs; - FallbackURLVector fallbackURLs; - bool allowAllNetworkRequests; // Wildcard found in NETWORK section. - }; +struct Manifest { + Vector<URL> onlineWhitelistedURLs; + HashSet<String> explicitURLs; + FallbackURLVector fallbackURLs; + bool allowAllNetworkRequests; // Wildcard found in NETWORK section. +}; - bool parseManifest(const URL& manifestURL, const char* data, int length, Manifest&); +bool parseManifest(const URL& manifestURL, const char* data, int length, Manifest&); -} - -#endif // ManifestParser_h +} // namespace WebCore diff --git a/Source/WebCore/loader/archive/Archive.cpp b/Source/WebCore/loader/archive/Archive.cpp index 86d7558e1..5e05be22c 100644 --- a/Source/WebCore/loader/archive/Archive.cpp +++ b/Source/WebCore/loader/archive/Archive.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -37,17 +37,17 @@ Archive::~Archive() void Archive::clearAllSubframeArchives() { - Vector<RefPtr<Archive>> clearedArchives; - clearAllSubframeArchivesImpl(&clearedArchives); + HashSet<Archive*> clearedArchives; + clearedArchives.add(this); + clearAllSubframeArchives(clearedArchives); } -void Archive::clearAllSubframeArchivesImpl(Vector<RefPtr<Archive>>* clearedArchives) +void Archive::clearAllSubframeArchives(HashSet<Archive*>& clearedArchives) { - for (Vector<RefPtr<Archive>>::iterator it = m_subframeArchives.begin(); it != m_subframeArchives.end(); ++it) { - if (!clearedArchives->contains(*it)) { - clearedArchives->append(*it); - (*it)->clearAllSubframeArchivesImpl(clearedArchives); - } + ASSERT(clearedArchives.contains(this)); + for (auto& archive : m_subframeArchives) { + if (clearedArchives.add(archive.ptr())) + archive->clearAllSubframeArchives(clearedArchives); } m_subframeArchives.clear(); } diff --git a/Source/WebCore/loader/archive/Archive.h b/Source/WebCore/loader/archive/Archive.h index 15f6b7563..ff74ce283 100644 --- a/Source/WebCore/loader/archive/Archive.h +++ b/Source/WebCore/loader/archive/Archive.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -26,47 +26,41 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Archive_h -#define Archive_h +#pragma once #include "ArchiveResource.h" - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> +#include <wtf/HashSet.h> namespace WebCore { class Archive : public RefCounted<Archive> { public: - enum Type { - WebArchive, - MHTML - }; virtual ~Archive(); - virtual Type type() const = 0; + + virtual bool shouldLoadFromArchiveOnly() const = 0; + virtual bool shouldOverrideBaseURL() const = 0; + virtual bool shouldUseMainResourceEncoding() const = 0; + virtual bool shouldUseMainResourceURL() const = 0; + ArchiveResource* mainResource() { return m_mainResource.get(); } - const Vector<RefPtr<ArchiveResource>>& subresources() const { return m_subresources; } - const Vector<RefPtr<Archive>>& subframeArchives() const { return m_subframeArchives; } + const Vector<Ref<ArchiveResource>>& subresources() const { return m_subresources; } + const Vector<Ref<Archive>>& subframeArchives() const { return m_subframeArchives; } protected: // These methods are meant for subclasses for different archive types to add resources in to the archive, // and should not be exposed as archives should be immutable to clients - void setMainResource(PassRefPtr<ArchiveResource> mainResource) { m_mainResource = mainResource; } - void addSubresource(PassRefPtr<ArchiveResource> subResource) { m_subresources.append(subResource); } - void addSubframeArchive(PassRefPtr<Archive> subframeArchive) { m_subframeArchives.append(subframeArchive); } + void setMainResource(Ref<ArchiveResource>&& mainResource) { m_mainResource = WTFMove(mainResource); } + void addSubresource(Ref<ArchiveResource>&& resource) { m_subresources.append(WTFMove(resource)); } + void addSubframeArchive(Ref<Archive>&& subframeArchive) { m_subframeArchives.append(WTFMove(subframeArchive)); } void clearAllSubframeArchives(); private: - void clearAllSubframeArchivesImpl(Vector<RefPtr<Archive>>* clearedArchives); + void clearAllSubframeArchives(HashSet<Archive*>&); RefPtr<ArchiveResource> m_mainResource; - Vector<RefPtr<ArchiveResource>> m_subresources; - Vector<RefPtr<Archive>> m_subframeArchives; + Vector<Ref<ArchiveResource>> m_subresources; + Vector<Ref<Archive>> m_subframeArchives; }; -} - -#endif // Archive +} // namespace WebCore diff --git a/Source/WebCore/loader/archive/ArchiveFactory.cpp b/Source/WebCore/loader/archive/ArchiveFactory.cpp index adbd5da44..417ac47a7 100644 --- a/Source/WebCore/loader/archive/ArchiveFactory.cpp +++ b/Source/WebCore/loader/archive/ArchiveFactory.cpp @@ -10,7 +10,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. * @@ -40,41 +40,42 @@ #include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/text/WTFString.h> namespace WebCore { -typedef PassRefPtr<Archive> RawDataCreationFunction(const URL&, SharedBuffer*); -typedef HashMap<String, RawDataCreationFunction*, CaseFoldingHash> ArchiveMIMETypesMap; +typedef RefPtr<Archive> RawDataCreationFunction(const URL&, SharedBuffer&); +typedef HashMap<String, RawDataCreationFunction*, ASCIICaseInsensitiveHash> ArchiveMIMETypesMap; -// The create functions in the archive classes return PassRefPtr to concrete subclasses +// The create functions in the archive classes return RefPtr to concrete subclasses // of Archive. This adaptor makes the functions have a uniform return type. -template <typename ArchiveClass> static PassRefPtr<Archive> archiveFactoryCreate(const URL& url, SharedBuffer* buffer) +template<typename ArchiveClass> static RefPtr<Archive> archiveFactoryCreate(const URL& url, SharedBuffer& buffer) { return ArchiveClass::create(url, buffer); } -static ArchiveMIMETypesMap& archiveMIMETypes() +static ArchiveMIMETypesMap createArchiveMIMETypesMap() { - DEFINE_STATIC_LOCAL(ArchiveMIMETypesMap, mimeTypes, ()); - static bool initialized = false; - - if (initialized) - return mimeTypes; + ArchiveMIMETypesMap map; - // FIXME: Remove unnecessary 'static_cast<RawDataCreationFunction*>' from the following 'mimeTypes.set' operations - // once we switch to a non-broken Visual Studio compiler. https://bugs.webkit.org/show_bug.cgi?id=121235 #if ENABLE(WEB_ARCHIVE) && USE(CF) - mimeTypes.set("application/x-webarchive", static_cast<RawDataCreationFunction*>(&archiveFactoryCreate<LegacyWebArchive>)); + map.add(ASCIILiteral { "application/x-webarchive" }, archiveFactoryCreate<LegacyWebArchive>); #endif + #if ENABLE(MHTML) - mimeTypes.set("multipart/related", static_cast<RawDataCreationFunction*>(&archiveFactoryCreate<MHTMLArchive>)); - mimeTypes.set("application/x-mimearchive", static_cast<RawDataCreationFunction*>(&archiveFactoryCreate<MHTMLArchive>)); + map.add(ASCIILiteral { "multipart/related" }, archiveFactoryCreate<MHTMLArchive>); + map.add(ASCIILiteral { "application/x-mimearchive" }, archiveFactoryCreate<MHTMLArchive>); #endif - initialized = true; - return mimeTypes; + return map; +} + +static ArchiveMIMETypesMap& archiveMIMETypes() +{ + static NeverDestroyed<ArchiveMIMETypesMap> map = createArchiveMIMETypesMap(); + return map; } bool ArchiveFactory::isArchiveMimeType(const String& mimeType) @@ -82,17 +83,22 @@ bool ArchiveFactory::isArchiveMimeType(const String& mimeType) return !mimeType.isEmpty() && archiveMIMETypes().contains(mimeType); } -PassRefPtr<Archive> ArchiveFactory::create(const URL& url, SharedBuffer* data, const String& mimeType) +RefPtr<Archive> ArchiveFactory::create(const URL& url, SharedBuffer* data, const String& mimeType) { - RawDataCreationFunction* function = mimeType.isEmpty() ? 0 : archiveMIMETypes().get(mimeType); - return function ? function(url, data) : PassRefPtr<Archive>(0); + if (!data) + return nullptr; + if (mimeType.isEmpty()) + return nullptr; + auto* function = archiveMIMETypes().get(mimeType); + if (!function) + return nullptr; + return function(url, *data); } void ArchiveFactory::registerKnownArchiveMIMETypes() { - HashSet<String>& mimeTypes = MIMETypeRegistry::getSupportedNonImageMIMETypes(); - - for (const auto& mimeType : archiveMIMETypes().keys()) + auto& mimeTypes = MIMETypeRegistry::getSupportedNonImageMIMETypes(); + for (auto& mimeType : archiveMIMETypes().keys()) mimeTypes.add(mimeType); } diff --git a/Source/WebCore/loader/archive/ArchiveFactory.h b/Source/WebCore/loader/archive/ArchiveFactory.h index 54b64dd77..f37b3c51d 100644 --- a/Source/WebCore/loader/archive/ArchiveFactory.h +++ b/Source/WebCore/loader/archive/ArchiveFactory.h @@ -10,7 +10,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. * @@ -26,13 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ArchiveFactory_h -#define ArchiveFactory_h +#pragma once #include "Archive.h" #include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> namespace WebCore { @@ -41,10 +39,8 @@ class SharedBuffer; class ArchiveFactory { public: static bool isArchiveMimeType(const String&); - static PassRefPtr<Archive> create(const URL&, SharedBuffer* data, const String& mimeType); + static RefPtr<Archive> create(const URL&, SharedBuffer* data, const String& mimeType); static void registerKnownArchiveMIMETypes(); }; -} - -#endif // ArchiveFactory_h +} // namespace WebCore diff --git a/Source/WebCore/loader/archive/ArchiveResource.cpp b/Source/WebCore/loader/archive/ArchiveResource.cpp index 21000c828..bf1e8461b 100644 --- a/Source/WebCore/loader/archive/ArchiveResource.cpp +++ b/Source/WebCore/loader/archive/ArchiveResource.cpp @@ -10,7 +10,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. * @@ -33,8 +33,8 @@ namespace WebCore { -inline ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const URL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) - : SubstituteResource(url, response, data) +inline ArchiveResource::ArchiveResource(Ref<SharedBuffer>&& data, const URL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) + : SubstituteResource(url, response, WTFMove(data)) , m_mimeType(mimeType) , m_textEncoding(textEncoding) , m_frameName(frameName) @@ -42,21 +42,21 @@ inline ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const URL { } -PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const URL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) +RefPtr<ArchiveResource> ArchiveResource::create(RefPtr<SharedBuffer>&& data, const URL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) { if (!data) - return 0; + return nullptr; if (response.isNull()) { unsigned dataSize = data->size(); - return adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, - ResourceResponse(url, mimeType, dataSize, textEncoding, String()))); + return adoptRef(*new ArchiveResource(data.releaseNonNull(), url, mimeType, textEncoding, frameName, + ResourceResponse(url, mimeType, dataSize, textEncoding))); } - return adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, response)); + return adoptRef(*new ArchiveResource(data.releaseNonNull(), url, mimeType, textEncoding, frameName, response)); } -PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const URL& url, const ResourceResponse& response) +RefPtr<ArchiveResource> ArchiveResource::create(RefPtr<SharedBuffer>&& data, const URL& url, const ResourceResponse& response) { - return create(data, url, response.mimeType(), response.textEncodingName(), String(), response); + return create(WTFMove(data), url, response.mimeType(), response.textEncodingName(), String(), response); } } diff --git a/Source/WebCore/loader/archive/ArchiveResource.h b/Source/WebCore/loader/archive/ArchiveResource.h index 9b4b01b9c..209e9717a 100644 --- a/Source/WebCore/loader/archive/ArchiveResource.h +++ b/Source/WebCore/loader/archive/ArchiveResource.h @@ -10,7 +10,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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ArchiveResource_h -#define ArchiveResource_h +#pragma once #include "SubstituteResource.h" @@ -35,8 +34,8 @@ namespace WebCore { class ArchiveResource : public SubstituteResource { public: - static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const URL&, const ResourceResponse&); - static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const URL&, + static RefPtr<ArchiveResource> create(RefPtr<SharedBuffer>&&, const URL&, const ResourceResponse&); + WEBCORE_EXPORT static RefPtr<ArchiveResource> create(RefPtr<SharedBuffer>&&, const URL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& = ResourceResponse()); @@ -48,7 +47,7 @@ public: bool shouldIgnoreWhenUnarchiving() const { return m_shouldIgnoreWhenUnarchiving; } private: - ArchiveResource(PassRefPtr<SharedBuffer>, const URL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&); + ArchiveResource(Ref<SharedBuffer>&&, const URL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&); String m_mimeType; String m_textEncoding; @@ -57,6 +56,4 @@ private: bool m_shouldIgnoreWhenUnarchiving; }; -} - -#endif // ArchiveResource_h +} // namespace WebCore diff --git a/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp b/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp index ac9e377b5..a3705ec77 100644 --- a/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp +++ b/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -29,64 +29,43 @@ #include "config.h" #include "ArchiveResourceCollection.h" -namespace WebCore { +#include "Archive.h" -ArchiveResourceCollection::ArchiveResourceCollection() -{ -} +namespace WebCore { -void ArchiveResourceCollection::addAllResources(Archive* archive) +void ArchiveResourceCollection::addAllResources(Archive& archive) { - ASSERT(archive); - if (!archive) - return; + for (auto& subresource : archive.subresources()) + m_subresources.set(subresource->url(), subresource.ptr()); - const Vector<RefPtr<ArchiveResource>>& subresources = archive->subresources(); - for (Vector<RefPtr<ArchiveResource>>::const_iterator iterator = subresources.begin(); iterator != subresources.end(); ++iterator) - m_subresources.set((*iterator)->url(), iterator->get()); - - const Vector<RefPtr<Archive>>& subframes = archive->subframeArchives(); - for (Vector<RefPtr<Archive>>::const_iterator iterator = subframes.begin(); iterator != subframes.end(); ++iterator) { - RefPtr<Archive> archive = *iterator; - ASSERT(archive->mainResource()); - - const String& frameName = archive->mainResource()->frameName(); - if (!frameName.isNull()) - m_subframes.set(frameName, archive.get()); - else { - // In the MHTML case, frames don't have a name so we use the URL instead. - m_subframes.set(archive->mainResource()->url().string(), archive.get()); + for (auto& subframeArchive : archive.subframeArchives()) { + ASSERT(subframeArchive->mainResource()); + auto frameName = subframeArchive->mainResource()->frameName(); + if (frameName.isNull()) { + // In the MHTML case, frames don't have a name, so we use the URL instead. + frameName = subframeArchive->mainResource()->url().string(); } + m_subframes.set(frameName, subframeArchive.ptr()); } } // 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 ArchiveResourceCollection::addResource(PassRefPtr<ArchiveResource> resource) +void ArchiveResourceCollection::addResource(Ref<ArchiveResource>&& resource) { - ASSERT(resource); - if (!resource) - return; - - const URL& url = resource->url(); // get before passing PassRefPtr (which sets it to 0) - m_subresources.set(url, resource); + auto& url = resource->url(); + m_subresources.set(url, WTFMove(resource)); } ArchiveResource* ArchiveResourceCollection::archiveResourceForURL(const URL& url) { - ArchiveResource* resource = m_subresources.get(url); - if (!resource) - return 0; - - return resource; + return m_subresources.get(url); } -PassRefPtr<Archive> ArchiveResourceCollection::popSubframeArchive(const String& frameName, const URL& url) +RefPtr<Archive> ArchiveResourceCollection::popSubframeArchive(const String& frameName, const URL& url) { - RefPtr<Archive> archive = m_subframes.take(frameName); - if (archive) - return archive.release(); - + if (auto archive = m_subframes.take(frameName)) + return archive; return m_subframes.take(url.string()); } diff --git a/Source/WebCore/loader/archive/ArchiveResourceCollection.h b/Source/WebCore/loader/archive/ArchiveResourceCollection.h index 1927a8952..37df68b36 100644 --- a/Source/WebCore/loader/archive/ArchiveResourceCollection.h +++ b/Source/WebCore/loader/archive/ArchiveResourceCollection.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,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. * @@ -26,35 +26,35 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ArchiveResourceCollection_h -#define ArchiveResourceCollection_h - -#include "Archive.h" -#include "ArchiveResource.h" -#include "URL.h" -#include <wtf/text/WTFString.h> +#pragma once +#include <wtf/Forward.h> #include <wtf/HashMap.h> -#include <wtf/RefCounted.h> +#include <wtf/Noncopyable.h> +#include <wtf/text/StringHash.h> namespace WebCore { +class Archive; +class ArchiveResource; +class URL; + class ArchiveResourceCollection { - WTF_MAKE_NONCOPYABLE(ArchiveResourceCollection); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(ArchiveResourceCollection); + WTF_MAKE_FAST_ALLOCATED; + public: - ArchiveResourceCollection(); + ArchiveResourceCollection() = default; - void addResource(PassRefPtr<ArchiveResource>); - void addAllResources(Archive*); + void addResource(Ref<ArchiveResource>&&); + void addAllResources(Archive&); - ArchiveResource* archiveResourceForURL(const URL&); - PassRefPtr<Archive> popSubframeArchive(const String& frameName, const URL&); + WEBCORE_EXPORT ArchiveResource* archiveResourceForURL(const URL&); + RefPtr<Archive> popSubframeArchive(const String& frameName, const URL&); private: HashMap<String, RefPtr<ArchiveResource>> m_subresources; HashMap<String, RefPtr<Archive>> m_subframes; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/archive/mhtml/MHTMLArchive.cpp b/Source/WebCore/loader/archive/mhtml/MHTMLArchive.cpp index 4708bf11e..2ca67e27b 100644 --- a/Source/WebCore/loader/archive/mhtml/MHTMLArchive.cpp +++ b/Source/WebCore/loader/archive/mhtml/MHTMLArchive.cpp @@ -59,7 +59,6 @@ namespace WebCore { const char* const quotedPrintable = "quoted-printable"; const char* const base64 = "base64"; -const char* const binary = "binary"; static String generateRandomBoundary() { @@ -102,50 +101,40 @@ MHTMLArchive::~MHTMLArchive() clearAllSubframeArchives(); } -PassRefPtr<MHTMLArchive> MHTMLArchive::create() +Ref<MHTMLArchive> MHTMLArchive::create() { - return adoptRef(new MHTMLArchive); + return adoptRef(*new MHTMLArchive); } -PassRefPtr<MHTMLArchive> MHTMLArchive::create(const URL& url, SharedBuffer* data) +RefPtr<MHTMLArchive> MHTMLArchive::create(const URL& url, SharedBuffer& data) { // For security reasons we only load MHTML pages from local URLs. - if (!SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol())) - return 0; + if (!SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol().toString())) + return nullptr; - MHTMLParser parser(data); + MHTMLParser parser(&data); RefPtr<MHTMLArchive> mainArchive = parser.parseArchive(); if (!mainArchive) - return 0; // Invalid MHTML file. + return nullptr; // Invalid MHTML file. // Since MHTML is a flat format, we need to make all frames aware of all resources. for (size_t i = 0; i < parser.frameCount(); ++i) { RefPtr<MHTMLArchive> archive = parser.frameAt(i); for (size_t j = 1; j < parser.frameCount(); ++j) { if (i != j) - archive->addSubframeArchive(parser.frameAt(j)); + archive->addSubframeArchive(*parser.frameAt(j)); } for (size_t j = 0; j < parser.subResourceCount(); ++j) - archive->addSubresource(parser.subResourceAt(j)); + archive->addSubresource(*parser.subResourceAt(j)); } - return mainArchive.release(); + return mainArchive; } -PassRefPtr<SharedBuffer> MHTMLArchive::generateMHTMLData(Page* page) -{ - return generateMHTMLData(page, false); -} - -PassRefPtr<SharedBuffer> MHTMLArchive::generateMHTMLDataUsingBinaryEncoding(Page* page) -{ - return generateMHTMLData(page, true); -} - -PassRefPtr<SharedBuffer> MHTMLArchive::generateMHTMLData(Page* page, bool useBinaryEncoding) +RefPtr<SharedBuffer> MHTMLArchive::generateMHTMLData(Page* page) { Vector<PageSerializer::Resource> resources; - PageSerializer pageSerializer(&resources); - pageSerializer.serialize(page); + PageSerializer pageSerializer(resources); + pageSerializer.serialize(*page); String boundary = generateRandomBoundary(); String endOfResourceBoundary = makeString("--", boundary, "\r\n"); @@ -176,18 +165,14 @@ PassRefPtr<SharedBuffer> MHTMLArchive::generateMHTMLData(Page* page, bool useBin RefPtr<SharedBuffer> mhtmlData = SharedBuffer::create(); mhtmlData->append(asciiString.data(), asciiString.length()); - for (size_t i = 0; i < resources.size(); ++i) { - const PageSerializer::Resource& resource = resources[i]; - + for (auto& resource : resources) { stringBuilder.clear(); stringBuilder.append(endOfResourceBoundary); stringBuilder.append("Content-Type: "); stringBuilder.append(resource.mimeType); - const char* contentEncoding = 0; - if (useBinaryEncoding) - contentEncoding = binary; - else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(resource.mimeType) || MIMETypeRegistry::isSupportedNonImageMIMEType(resource.mimeType)) + const char* contentEncoding = nullptr; + if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(resource.mimeType) || MIMETypeRegistry::isSupportedNonImageMIMEType(resource.mimeType)) contentEncoding = quotedPrintable; else contentEncoding = base64; @@ -201,36 +186,27 @@ PassRefPtr<SharedBuffer> MHTMLArchive::generateMHTMLData(Page* page, bool useBin asciiString = stringBuilder.toString().utf8(); mhtmlData->append(asciiString.data(), asciiString.length()); - if (!strcmp(contentEncoding, binary)) { - const char* data; - size_t position = 0; - while (size_t length = resource.data->getSomeData(data, position)) { - mhtmlData->append(data, length); - position += length; - } + // FIXME: ideally we would encode the content as a stream without having to fetch it all. + const char* data = resource.data->data(); + size_t dataLength = resource.data->size(); + Vector<char> encodedData; + if (!strcmp(contentEncoding, quotedPrintable)) { + quotedPrintableEncode(data, dataLength, encodedData); + mhtmlData->append(encodedData.data(), encodedData.size()); + mhtmlData->append("\r\n", 2); } else { - // FIXME: ideally we would encode the content as a stream without having to fetch it all. - const char* data = resource.data->data(); - size_t dataLength = resource.data->size(); - Vector<char> encodedData; - if (!strcmp(contentEncoding, quotedPrintable)) { - quotedPrintableEncode(data, dataLength, encodedData); - mhtmlData->append(encodedData.data(), encodedData.size()); + ASSERT(!strcmp(contentEncoding, base64)); + // We are not specifying insertLFs = true below as it would cut the lines with LFs and MHTML requires CRLFs. + base64Encode(data, dataLength, encodedData); + const size_t maximumLineLength = 76; + size_t index = 0; + size_t encodedDataLength = encodedData.size(); + do { + size_t lineLength = std::min(encodedDataLength - index, maximumLineLength); + mhtmlData->append(encodedData.data() + index, lineLength); mhtmlData->append("\r\n", 2); - } else { - ASSERT(!strcmp(contentEncoding, base64)); - // We are not specifying insertLFs = true below as it would cut the lines with LFs and MHTML requires CRLFs. - base64Encode(data, dataLength, encodedData); - const size_t maximumLineLength = 76; - size_t index = 0; - size_t encodedDataLength = encodedData.size(); - do { - size_t lineLength = std::min(encodedDataLength - index, maximumLineLength); - mhtmlData->append(encodedData.data() + index, lineLength); - mhtmlData->append("\r\n", 2); - index += maximumLineLength; - } while (index < encodedDataLength); - } + index += maximumLineLength; + } while (index < encodedDataLength); } } diff --git a/Source/WebCore/loader/archive/mhtml/MHTMLArchive.h b/Source/WebCore/loader/archive/mhtml/MHTMLArchive.h index 14cd87572..8524659aa 100644 --- a/Source/WebCore/loader/archive/mhtml/MHTMLArchive.h +++ b/Source/WebCore/loader/archive/mhtml/MHTMLArchive.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MHTMLArchive_h -#define MHTMLArchive_h +#pragma once #if ENABLE(MHTML) @@ -41,27 +40,26 @@ class MHTMLParser; class Page; class SharedBuffer; -class MHTMLArchive : public Archive { +class MHTMLArchive final : public Archive { public: - virtual Type type() const { return MHTML; } + static Ref<MHTMLArchive> create(); + static RefPtr<MHTMLArchive> create(const URL&, SharedBuffer&); - static PassRefPtr<MHTMLArchive> create(); - static PassRefPtr<MHTMLArchive> create(const URL&, SharedBuffer*); - - static PassRefPtr<SharedBuffer> generateMHTMLData(Page*); - // Binary encoding results in smaller MHTML files but they might not work in other browsers. - static PassRefPtr<SharedBuffer> generateMHTMLDataUsingBinaryEncoding(Page*); + static RefPtr<SharedBuffer> generateMHTMLData(Page*); virtual ~MHTMLArchive(); private: - static PassRefPtr<SharedBuffer> generateMHTMLData(Page*, bool useBinaryEncoding); - friend class MHTMLParser; + MHTMLArchive(); + + bool shouldLoadFromArchiveOnly() const final { return true; } + bool shouldOverrideBaseURL() const final { return true; } + bool shouldUseMainResourceEncoding() const final { return false; } + bool shouldUseMainResourceURL() const final { return false; } }; -} +} // namespace WebCore -#endif -#endif +#endif // ENABLE(MHTML) diff --git a/Source/WebCore/loader/archive/mhtml/MHTMLParser.cpp b/Source/WebCore/loader/archive/mhtml/MHTMLParser.cpp index d8760f9ed..b0414ebc9 100644 --- a/Source/WebCore/loader/archive/mhtml/MHTMLParser.cpp +++ b/Source/WebCore/loader/archive/mhtml/MHTMLParser.cpp @@ -37,7 +37,6 @@ #include "MIMEHeader.h" #include "MIMETypeRegistry.h" #include "QuotedPrintable.h" -#include <wtf/HashMap.h> #include <wtf/text/Base64.h> namespace WebCore { @@ -57,17 +56,16 @@ MHTMLParser::MHTMLParser(SharedBuffer* data) { } -PassRefPtr<MHTMLArchive> MHTMLParser::parseArchive() +RefPtr<MHTMLArchive> MHTMLParser::parseArchive() { - RefPtr<MIMEHeader> header = MIMEHeader::parseHeader(&m_lineReader); - return parseArchiveWithHeader(header.get()); + return parseArchiveWithHeader(MIMEHeader::parseHeader(m_lineReader).get()); } -PassRefPtr<MHTMLArchive> MHTMLParser::parseArchiveWithHeader(MIMEHeader* header) +RefPtr<MHTMLArchive> MHTMLParser::parseArchiveWithHeader(MIMEHeader* header) { if (!header) { LOG_ERROR("Failed to parse MHTML part: no header."); - return 0; + return nullptr; } RefPtr<MHTMLArchive> archive = MHTMLArchive::create(); @@ -76,8 +74,8 @@ PassRefPtr<MHTMLArchive> MHTMLParser::parseArchiveWithHeader(MIMEHeader* header) bool endOfArchiveReached = false; RefPtr<ArchiveResource> resource = parseNextPart(*header, String(), String(), endOfArchiveReached); if (!resource) - return 0; - archive->setMainResource(resource); + return nullptr; + archive->setMainResource(resource.releaseNonNull()); return archive; } @@ -86,31 +84,31 @@ PassRefPtr<MHTMLArchive> MHTMLParser::parseArchiveWithHeader(MIMEHeader* header) bool endOfArchive = false; while (!endOfArchive) { - RefPtr<MIMEHeader> resourceHeader = MIMEHeader::parseHeader(&m_lineReader); + RefPtr<MIMEHeader> resourceHeader = MIMEHeader::parseHeader(m_lineReader); if (!resourceHeader) { LOG_ERROR("Failed to parse MHTML, invalid MIME header."); - return 0; + return nullptr; } if (resourceHeader->contentType() == "multipart/alternative") { // Ignore IE nesting which makes little sense (IE seems to nest only some of the frames). RefPtr<MHTMLArchive> subframeArchive = parseArchiveWithHeader(resourceHeader.get()); if (!subframeArchive) { LOG_ERROR("Failed to parse MHTML subframe."); - return 0; + return nullptr; } bool endOfPartReached = skipLinesUntilBoundaryFound(m_lineReader, header->endOfPartBoundary()); ASSERT_UNUSED(endOfPartReached, endOfPartReached); // The top-frame is the first frame found, regardless of the nesting level. if (subframeArchive->mainResource()) addResourceToArchive(subframeArchive->mainResource(), archive.get()); - archive->addSubframeArchive(subframeArchive); + archive->addSubframeArchive(subframeArchive.releaseNonNull()); continue; } RefPtr<ArchiveResource> resource = parseNextPart(*resourceHeader, header->endOfPartBoundary(), header->endOfDocumentBoundary(), endOfArchive); if (!resource) { LOG_ERROR("Failed to parse MHTML part."); - return 0; + return nullptr; } addResourceToArchive(resource.get(), archive.get()); } @@ -128,17 +126,17 @@ void MHTMLParser::addResourceToArchive(ArchiveResource* resource, MHTMLArchive* // The first document suitable resource is the main frame. if (!archive->mainResource()) { - archive->setMainResource(resource); + archive->setMainResource(*resource); m_frames.append(archive); return; } RefPtr<MHTMLArchive> subframe = MHTMLArchive::create(); - subframe->setMainResource(resource); + subframe->setMainResource(*resource); m_frames.append(subframe); } -PassRefPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHeader, const String& endOfPartBoundary, const String& endOfDocumentBoundary, bool& endOfArchiveReached) +RefPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHeader, const String& endOfPartBoundary, const String& endOfDocumentBoundary, bool& endOfArchiveReached) { ASSERT(endOfPartBoundary.isEmpty() == endOfDocumentBoundary.isEmpty()); @@ -148,31 +146,31 @@ PassRefPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHea if (mimeHeader.contentTransferEncoding() == MIMEHeader::Binary) { if (!checkBoundary) { LOG_ERROR("Binary contents requires end of part"); - return 0; + return nullptr; } m_lineReader.setSeparator(endOfPartBoundary.utf8().data()); Vector<char> part; if (!m_lineReader.nextChunk(part)) { LOG_ERROR("Binary contents requires end of part"); - return 0; - } - content->append(part); - m_lineReader.setSeparator("\r\n"); - Vector<char> nextChars; - if (m_lineReader.peek(nextChars, 2) != 2) { - LOG_ERROR("Invalid seperator."); - return 0; - } - endOfPartReached = true; - ASSERT(nextChars.size() == 2); - endOfArchiveReached = (nextChars[0] == '-' && nextChars[1] == '-'); - if (!endOfArchiveReached) { - String line = m_lineReader.nextChunkAsUTF8StringWithLatin1Fallback(); - if (!line.isEmpty()) { - LOG_ERROR("No CRLF at end of binary section."); - return 0; - } - } + return nullptr; + } + content->append(part); + m_lineReader.setSeparator("\r\n"); + Vector<char> nextChars; + if (m_lineReader.peek(nextChars, 2) != 2) { + LOG_ERROR("Invalid seperator."); + return nullptr; + } + endOfPartReached = true; + ASSERT(nextChars.size() == 2); + endOfArchiveReached = (nextChars[0] == '-' && nextChars[1] == '-'); + if (!endOfArchiveReached) { + String line = m_lineReader.nextChunkAsUTF8StringWithLatin1Fallback(); + if (!line.isEmpty()) { + LOG_ERROR("No CRLF at end of binary section."); + return nullptr; + } + } } else { String line; while (!(line = m_lineReader.nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { @@ -191,7 +189,7 @@ PassRefPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHea } if (!endOfPartReached && checkBoundary) { LOG_ERROR("No bounday found for MHTML part."); - return 0; + return nullptr; } Vector<char> data; @@ -199,7 +197,7 @@ PassRefPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHea case MIMEHeader::Base64: if (!base64Decode(content->data(), content->size(), data)) { LOG_ERROR("Invalid base64 content for MHTML part."); - return 0; + return nullptr; } break; case MIMEHeader::QuotedPrintable: @@ -211,14 +209,14 @@ PassRefPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHea break; default: LOG_ERROR("Invalid encoding for MHTML part."); - return 0; + return nullptr; } RefPtr<SharedBuffer> contentBuffer = SharedBuffer::adoptVector(data); // FIXME: the URL in the MIME header could be relative, we should resolve it if it is. // The specs mentions 5 ways to resolve a URL: http://tools.ietf.org/html/rfc2557#section-5 // IE and Firefox (UNMht) seem to generate only absolute URLs. URL location = URL(URL(), mimeHeader.contentLocation()); - return ArchiveResource::create(contentBuffer, location, mimeHeader.contentType(), mimeHeader.charset(), String()); + return ArchiveResource::create(WTFMove(contentBuffer), location, mimeHeader.contentType(), mimeHeader.charset(), String()); } size_t MHTMLParser::frameCount() const diff --git a/Source/WebCore/loader/archive/mhtml/MHTMLParser.h b/Source/WebCore/loader/archive/mhtml/MHTMLParser.h index 4f1b126bc..1e9bd4d77 100644 --- a/Source/WebCore/loader/archive/mhtml/MHTMLParser.h +++ b/Source/WebCore/loader/archive/mhtml/MHTMLParser.h @@ -28,10 +28,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MHTMLParser_h -#define MHTMLParser_h +#pragma once #if ENABLE(MHTML) + #include "SharedBufferChunkReader.h" #include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> @@ -48,7 +48,7 @@ class MHTMLParser { public: explicit MHTMLParser(SharedBuffer*); - PassRefPtr<MHTMLArchive> parseArchive(); + RefPtr<MHTMLArchive> parseArchive(); size_t frameCount() const; MHTMLArchive* frameAt(size_t) const; @@ -57,8 +57,8 @@ public: ArchiveResource* subResourceAt(size_t) const; private: - PassRefPtr<MHTMLArchive> parseArchiveWithHeader(MIMEHeader*); - PassRefPtr<ArchiveResource> parseNextPart(const MIMEHeader&, const String& endOfPartBoundary, const String& endOfDocumentBoundary, bool& endOfArchiveReached); + RefPtr<MHTMLArchive> parseArchiveWithHeader(MIMEHeader*); + RefPtr<ArchiveResource> parseNextPart(const MIMEHeader&, const String& endOfPartBoundary, const String& endOfDocumentBoundary, bool& endOfArchiveReached); void addResourceToArchive(ArchiveResource*, MHTMLArchive*); @@ -67,8 +67,6 @@ private: Vector<RefPtr<MHTMLArchive>> m_frames; }; -} - -#endif -#endif +} // namespace WebCore +#endif // ENABLE(MHTML) diff --git a/Source/WebCore/loader/cache/CachePolicy.h b/Source/WebCore/loader/cache/CachePolicy.h index 0b9010b63..b8a1013c3 100644 --- a/Source/WebCore/loader/cache/CachePolicy.h +++ b/Source/WebCore/loader/cache/CachePolicy.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,19 +23,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachePolicy_h -#define CachePolicy_h +#pragma once namespace WebCore { - enum CachePolicy { - CachePolicyCache, - CachePolicyVerify, - CachePolicyRevalidate, - CachePolicyReload, - CachePolicyHistoryBuffer - }; +enum CachePolicy { + CachePolicyVerify, + CachePolicyRevalidate, + CachePolicyReload, + CachePolicyHistoryBuffer +}; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp b/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp index 69bce8f40..af40cc204 100644 --- a/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp +++ b/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp @@ -3,7 +3,7 @@ Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + Copyright (C) 2004-2017 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -19,9 +19,6 @@ along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - This class provides all functionality needed for loading images, style sheets and html - pages from the web. It has a memory cache for these objects. */ #include "config.h" @@ -29,24 +26,22 @@ #include "CSSStyleSheet.h" #include "CachedResourceClientWalker.h" +#include "CachedResourceRequest.h" #include "CachedStyleSheetClient.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" #include "MemoryCache.h" -#include "ResourceBuffer.h" +#include "SharedBuffer.h" #include "StyleSheetContents.h" #include "TextResourceDecoder.h" #include <wtf/CurrentTime.h> -#include <wtf/Vector.h> namespace WebCore { -CachedCSSStyleSheet::CachedCSSStyleSheet(const ResourceRequest& resourceRequest, const String& charset) - : CachedResource(resourceRequest, CSSStyleSheet) - , m_decoder(TextResourceDecoder::create("text/css", charset)) +CachedCSSStyleSheet::CachedCSSStyleSheet(CachedResourceRequest&& request, SessionID sessionID) + : CachedResource(WTFMove(request), CSSStyleSheet, sessionID) + , m_decoder(TextResourceDecoder::create("text/css", request.charset())) { - // Prefer text/css but accept any type (dell.com serves a stylesheet - // as text/html; see <http://bugs.webkit.org/show_bug.cgi?id=11451>). - setAccept("text/css,*/*;q=0.1"); } CachedCSSStyleSheet::~CachedCSSStyleSheet() @@ -55,16 +50,16 @@ CachedCSSStyleSheet::~CachedCSSStyleSheet() m_parsedStyleSheetCache->removedFromMemoryCache(); } -void CachedCSSStyleSheet::didAddClient(CachedResourceClient* c) +void CachedCSSStyleSheet::didAddClient(CachedResourceClient& client) { - ASSERT(c->resourceClientType() == CachedStyleSheetClient::expectedType()); + ASSERT(client.resourceClientType() == CachedStyleSheetClient::expectedType()); // CachedResource::didAddClient() must be before setCSSStyleSheet(), // because setCSSStyleSheet() may cause scripts to be executed, which could destroy 'c' if it is an instance of HTMLLinkElement. // see the comment of HTMLLinkElement::setCSSStyleSheet. - CachedResource::didAddClient(c); + CachedResource::didAddClient(client); if (!isLoading()) - static_cast<CachedStyleSheetClient*>(c)->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this); + static_cast<CachedStyleSheetClient&>(client).setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this); } void CachedCSSStyleSheet::setEncoding(const String& chs) @@ -76,32 +71,39 @@ String CachedCSSStyleSheet::encoding() const { return m_decoder->encoding().name(); } - -const String CachedCSSStyleSheet::sheetText(bool enforceMIMEType, bool* hasValidMIMEType) const -{ - ASSERT(!isPurgeable()); - if (!m_data || m_data->isEmpty() || !canUseSheet(enforceMIMEType, hasValidMIMEType)) +const String CachedCSSStyleSheet::sheetText(MIMETypeCheck mimeTypeCheck, bool* hasValidMIMEType) const +{ + if (!m_data || m_data->isEmpty() || !canUseSheet(mimeTypeCheck, hasValidMIMEType)) return String(); - + if (!m_decodedSheetText.isNull()) return m_decodedSheetText; - + // Don't cache the decoded text, regenerating is cheap and it can use quite a bit of memory - String sheetText = m_decoder->decode(m_data->data(), m_data->size()); - sheetText.append(m_decoder->flush()); - return sheetText; + return m_decoder->decodeAndFlush(m_data->data(), m_data->size()); } -void CachedCSSStyleSheet::finishLoading(ResourceBuffer* data) +void CachedCSSStyleSheet::setBodyDataFrom(const CachedResource& resource) +{ + ASSERT(resource.type() == type()); + const CachedCSSStyleSheet& sheet = static_cast<const CachedCSSStyleSheet&>(resource); + + CachedResource::setBodyDataFrom(resource); + + m_decoder = sheet.m_decoder; + m_decodedSheetText = sheet.m_decodedSheetText; + if (sheet.m_parsedStyleSheetCache) + saveParsedStyleSheet(*sheet.m_parsedStyleSheetCache); +} + +void CachedCSSStyleSheet::finishLoading(SharedBuffer* data) { m_data = data; - setEncodedSize(m_data.get() ? m_data->size() : 0); + setEncodedSize(data ? data->size() : 0); // Decode the data to find out the encoding and keep the sheet text around during checkNotify() - if (m_data) { - m_decodedSheetText = m_decoder->decode(m_data->data(), m_data->size()); - m_decodedSheetText.append(m_decoder->flush()); - } + if (data) + m_decodedSheetText = m_decoder->decodeAndFlush(data->data(), data->size()); setLoading(false); checkNotify(); // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate. @@ -118,12 +120,12 @@ void CachedCSSStyleSheet::checkNotify() c->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this); } -bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const +bool CachedCSSStyleSheet::canUseSheet(MIMETypeCheck mimeTypeCheck, bool* hasValidMIMEType) const { if (errorOccurred()) return false; - - if (!enforceMIMEType && !hasValidMIMEType) + + if (mimeTypeCheck == MIMETypeCheck::Lax) return true; // This check exactly matches Firefox. Note that we grab the Content-Type @@ -133,12 +135,10 @@ bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType, bool* hasValidMIMETy // // This code defaults to allowing the stylesheet for non-HTTP protocols so // folks can use standards mode for local HTML documents. - String mimeType = extractMIMETypeFromMediaType(response().httpHeaderField("Content-Type")); - bool typeOK = mimeType.isEmpty() || equalIgnoringCase(mimeType, "text/css") || equalIgnoringCase(mimeType, "application/x-unknown-content-type"); + String mimeType = extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType)); + bool typeOK = mimeType.isEmpty() || equalLettersIgnoringASCIICase(mimeType, "text/css") || equalLettersIgnoringASCIICase(mimeType, "application/x-unknown-content-type"); if (hasValidMIMEType) *hasValidMIMEType = typeOK; - if (!enforceMIMEType) - return true; return typeOK; } @@ -148,22 +148,19 @@ void CachedCSSStyleSheet::destroyDecodedData() return; m_parsedStyleSheetCache->removedFromMemoryCache(); - m_parsedStyleSheetCache.clear(); + m_parsedStyleSheetCache = nullptr; setDecodedSize(0); - - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable()) - makePurgeable(true); } -PassRefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context) +RefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context, CachePolicy cachePolicy) { if (!m_parsedStyleSheetCache) - return 0; - if (m_parsedStyleSheetCache->hasFailedOrCanceledSubresources()) { + return nullptr; + if (!m_parsedStyleSheetCache->subresourcesAllowReuse(cachePolicy)) { m_parsedStyleSheetCache->removedFromMemoryCache(); - m_parsedStyleSheetCache.clear(); - return 0; + m_parsedStyleSheetCache = nullptr; + return nullptr; } ASSERT(m_parsedStyleSheetCache->isCacheable()); @@ -171,20 +168,20 @@ PassRefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(cons // Contexts must be identical so we know we would get the same exact result if we parsed again. if (m_parsedStyleSheetCache->parserContext() != context) - return 0; + return nullptr; didAccessDecodedData(monotonicallyIncreasingTime()); return m_parsedStyleSheetCache; } -void CachedCSSStyleSheet::saveParsedStyleSheet(PassRef<StyleSheetContents> sheet) +void CachedCSSStyleSheet::saveParsedStyleSheet(Ref<StyleSheetContents>&& sheet) { - ASSERT(sheet.get().isCacheable()); + ASSERT(sheet->isCacheable()); if (m_parsedStyleSheetCache) m_parsedStyleSheetCache->removedFromMemoryCache(); - m_parsedStyleSheetCache = std::move(sheet); + m_parsedStyleSheetCache = WTFMove(sheet); m_parsedStyleSheetCache->addedToMemoryCache(); setDecodedSize(m_parsedStyleSheetCache->estimatedSizeInBytes()); diff --git a/Source/WebCore/loader/cache/CachedCSSStyleSheet.h b/Source/WebCore/loader/cache/CachedCSSStyleSheet.h index 19ad61939..c33aa2e40 100644 --- a/Source/WebCore/loader/cache/CachedCSSStyleSheet.h +++ b/Source/WebCore/loader/cache/CachedCSSStyleSheet.h @@ -2,7 +2,7 @@ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2001 Dirk Mueller <mueller@kde.org> Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + Copyright (C) 2004-2017 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -18,55 +18,53 @@ along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - This class provides all functionality needed for loading images, style sheets and html - pages from the web. It has a memory cache for these objects. */ -#ifndef CachedCSSStyleSheet_h -#define CachedCSSStyleSheet_h +#pragma once #include "CachedResource.h" -#include <wtf/Vector.h> namespace WebCore { - class CachedResourceClient; - class StyleSheetContents; - class TextResourceDecoder; - struct CSSParserContext; +class StyleSheetContents; +class TextResourceDecoder; + +struct CSSParserContext; + +class CachedCSSStyleSheet final : public CachedResource { +public: + CachedCSSStyleSheet(CachedResourceRequest&&, SessionID); + virtual ~CachedCSSStyleSheet(); - class CachedCSSStyleSheet final : public CachedResource { - public: - CachedCSSStyleSheet(const ResourceRequest&, const String& charset); - virtual ~CachedCSSStyleSheet(); + enum class MIMETypeCheck { Strict, Lax }; + const String sheetText(MIMETypeCheck = MIMETypeCheck::Strict, bool* hasValidMIMEType = nullptr) const; - const String sheetText(bool enforceMIMEType = true, bool* hasValidMIMEType = 0) const; + RefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&, CachePolicy); + void saveParsedStyleSheet(Ref<StyleSheetContents>&&); - PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&); - void saveParsedStyleSheet(PassRef<StyleSheetContents>); +private: + bool canUseSheet(MIMETypeCheck, bool* hasValidMIMEType) const; + bool mayTryReplaceEncodedData() const final { return true; } - private: - bool canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const; - virtual PurgePriority purgePriority() const override { return PurgeLast; } - virtual bool mayTryReplaceEncodedData() const override { return true; } + void didAddClient(CachedResourceClient&) final; - virtual void didAddClient(CachedResourceClient*) override; + void setEncoding(const String&) final; + String encoding() const final; + const TextResourceDecoder* textResourceDecoder() const final { return m_decoder.get(); } + void finishLoading(SharedBuffer*) final; + void destroyDecodedData() final; - virtual void setEncoding(const String&) override; - virtual String encoding() const override; - virtual void finishLoading(ResourceBuffer*) override; - virtual void destroyDecodedData() override; + void setBodyDataFrom(const CachedResource&) final; - protected: - virtual void checkNotify() override; +protected: + void checkNotify() final; - RefPtr<TextResourceDecoder> m_decoder; - String m_decodedSheetText; + RefPtr<TextResourceDecoder> m_decoder; + String m_decodedSheetText; - RefPtr<StyleSheetContents> m_parsedStyleSheetCache; - }; + RefPtr<StyleSheetContents> m_parsedStyleSheetCache; +}; -} +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedCSSStyleSheet, CachedResource::CSSStyleSheet) diff --git a/Source/WebCore/loader/cache/CachedFont.cpp b/Source/WebCore/loader/cache/CachedFont.cpp index fbf658cc0..91929778b 100644 --- a/Source/WebCore/loader/cache/CachedFont.cpp +++ b/Source/WebCore/loader/cache/CachedFont.cpp @@ -11,10 +11,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -31,27 +31,22 @@ #include "CachedResourceClientWalker.h" #include "CachedResourceLoader.h" #include "FontCustomPlatformData.h" +#include "FontDescription.h" #include "FontPlatformData.h" -#include "MemoryCache.h" -#include "ResourceBuffer.h" #include "SharedBuffer.h" #include "TextResourceDecoder.h" +#include "TypedElementDescendantIterator.h" #include "WOFFFileFormat.h" #include <wtf/Vector.h> -#if ENABLE(SVG_FONTS) -#include "NodeList.h" -#include "SVGDocument.h" -#include "SVGElement.h" -#include "SVGFontElement.h" -#include "SVGGElement.h" -#include "SVGNames.h" +#if USE(DIRECT2D) +#include <dwrite.h> #endif namespace WebCore { -CachedFont::CachedFont(const ResourceRequest& resourceRequest) - : CachedResource(resourceRequest, FontResource) +CachedFont::CachedFont(CachedResourceRequest&& request, SessionID sessionID, Type type) + : CachedResource(WTFMove(request), type, sessionID) , m_loadInitiated(false) , m_hasCreatedFontDataWrappingResource(false) { @@ -61,21 +56,20 @@ CachedFont::~CachedFont() { } -void CachedFont::load(CachedResourceLoader*, const ResourceLoaderOptions& options) +void CachedFont::load(CachedResourceLoader&) { // Don't load the file yet. Wait for an access before triggering the load. setLoading(true); - m_options = options; } -void CachedFont::didAddClient(CachedResourceClient* c) +void CachedFont::didAddClient(CachedResourceClient& client) { - ASSERT(c->resourceClientType() == CachedFontClient::expectedType()); + ASSERT(client.resourceClientType() == CachedFontClient::expectedType()); if (!isLoading()) - static_cast<CachedFontClient*>(c)->fontLoaded(this); + static_cast<CachedFontClient&>(client).fontLoaded(*this); } -void CachedFont::finishLoading(ResourceBuffer* data) +void CachedFont::finishLoading(SharedBuffer* data) { m_data = data; setEncodedSize(m_data.get() ? m_data->size() : 0); @@ -83,103 +77,76 @@ void CachedFont::finishLoading(ResourceBuffer* data) checkNotify(); } -void CachedFont::beginLoadIfNeeded(CachedResourceLoader* dl) +void CachedFont::beginLoadIfNeeded(CachedResourceLoader& loader) { if (!m_loadInitiated) { m_loadInitiated = true; - CachedResource::load(dl, m_options); + CachedResource::load(loader); } } -bool CachedFont::ensureCustomFontData() +bool CachedFont::ensureCustomFontData(const AtomicString&) { - if (!m_fontData && !errorOccurred() && !isLoading() && m_data) { - SharedBuffer* buffer = m_data.get()->sharedBuffer(); - ASSERT(buffer); - - RefPtr<SharedBuffer> sfntBuffer; - - bool fontIsWOFF = isWOFF(buffer); - if (fontIsWOFF) { - Vector<char> sfnt; - if (convertWOFFToSfnt(buffer, sfnt)) { - sfntBuffer = SharedBuffer::adoptVector(sfnt); - buffer = sfntBuffer.get(); - } else - buffer = nullptr; - } - - m_fontData = buffer ? createFontCustomPlatformData(*buffer) : nullptr; - if (m_fontData) - m_hasCreatedFontDataWrappingResource = !fontIsWOFF; - else - setStatus(DecodeError); - } - return m_fontData.get(); + return ensureCustomFontData(m_data.get()); } -FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant widthVariant, FontRenderingMode renderingMode) +bool CachedFont::ensureCustomFontData(SharedBuffer* data) { -#if ENABLE(SVG_FONTS) - if (m_externalSVGDocument) - return FontPlatformData(size, bold, italic); -#endif - ASSERT(m_fontData); - return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, orientation, widthVariant, renderingMode); -} - -#if ENABLE(SVG_FONTS) -bool CachedFont::ensureSVGFontData() -{ - if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) { - m_externalSVGDocument = SVGDocument::create(0, URL()); - - RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml"); - String svgSource = decoder->decode(m_data->data(), m_data->size()); - svgSource.append(decoder->flush()); - - m_externalSVGDocument->setContent(svgSource); - - if (decoder->sawError()) - m_externalSVGDocument = 0; + if (!m_fontCustomPlatformData && !errorOccurred() && !isLoading() && data) { + bool wrapping; + m_fontCustomPlatformData = createCustomFontData(*data, wrapping); + m_hasCreatedFontDataWrappingResource = m_fontCustomPlatformData && wrapping; + if (!m_fontCustomPlatformData) + setStatus(DecodeError); } - return m_externalSVGDocument; + return m_fontCustomPlatformData.get(); } -SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const +std::unique_ptr<FontCustomPlatformData> CachedFont::createCustomFontData(SharedBuffer& bytes, bool& wrapping) { - RefPtr<NodeList> list = m_externalSVGDocument->getElementsByTagNameNS(SVGNames::fontTag.namespaceURI(), SVGNames::fontTag.localName()); - if (!list) - return 0; - - unsigned listLength = list->length(); - if (!listLength) - return 0; - -#ifndef NDEBUG - for (unsigned i = 0; i < listLength; ++i) { - ASSERT(list->item(i)); - ASSERT(isSVGFontElement(list->item(i))); + wrapping = true; + +#if !PLATFORM(COCOA) + if (isWOFF(bytes)) { + wrapping = false; + Vector<char> convertedFont; + if (!convertWOFFToSfnt(bytes, convertedFont)) + return nullptr; + + auto buffer = SharedBuffer::adoptVector(convertedFont); + return createFontCustomPlatformData(buffer); } #endif - if (fontName.isEmpty()) - return toSVGFontElement(list->item(0)); + return createFontCustomPlatformData(bytes); +} - for (unsigned i = 0; i < listLength; ++i) { - SVGFontElement* element = toSVGFontElement(list->item(i)); - if (element->getIdAttribute() == fontName) - return element; - } +RefPtr<Font> CachedFont::createFont(const FontDescription& fontDescription, const AtomicString&, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings) +{ + return Font::create(platformDataFromCustomData(fontDescription, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings), true); +} - return 0; +FontPlatformData CachedFont::platformDataFromCustomData(const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings) +{ + ASSERT(m_fontCustomPlatformData); + return platformDataFromCustomData(*m_fontCustomPlatformData, fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings); } + +FontPlatformData CachedFont::platformDataFromCustomData(FontCustomPlatformData& fontCustomPlatformData, const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings) +{ +#if PLATFORM(COCOA) + return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings); +#else + UNUSED_PARAM(fontFaceFeatures); + UNUSED_PARAM(fontFaceVariantSettings); + return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic); #endif +} void CachedFont::allClientsRemoved() { - m_fontData = nullptr; + m_fontCustomPlatformData = nullptr; } void CachedFont::checkNotify() @@ -187,9 +154,9 @@ void CachedFont::checkNotify() if (isLoading()) return; - CachedResourceClientWalker<CachedFontClient> w(m_clients); - while (CachedFontClient* c = w.next()) - c->fontLoaded(this); + CachedResourceClientWalker<CachedFontClient> walker(m_clients); + while (CachedFontClient* client = walker.next()) + client->fontLoaded(*this); } bool CachedFont::mayTryReplaceEncodedData() const diff --git a/Source/WebCore/loader/cache/CachedFont.h b/Source/WebCore/loader/cache/CachedFont.h index f19582b96..25d292f0f 100644 --- a/Source/WebCore/loader/cache/CachedFont.h +++ b/Source/WebCore/loader/cache/CachedFont.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,61 +23,63 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedFont_h -#define CachedFont_h +#pragma once #include "CachedResource.h" #include "CachedResourceClient.h" -#include "FontOrientation.h" -#include "FontRenderingMode.h" -#include "FontWidthVariant.h" +#include "Font.h" +#include "TextFlags.h" namespace WebCore { class CachedResourceLoader; +class FontDescription; class FontPlatformData; class SVGDocument; class SVGFontElement; struct FontCustomPlatformData; -class CachedFont final : public CachedResource { +template <typename T> class FontTaggedSettings; +typedef FontTaggedSettings<int> FontFeatureSettings; + +class CachedFont : public CachedResource { public: - CachedFont(const ResourceRequest&); + CachedFont(CachedResourceRequest&&, SessionID, Type = FontResource); virtual ~CachedFont(); - void beginLoadIfNeeded(CachedResourceLoader* dl); - virtual bool stillNeedsLoad() const override { return !m_loadInitiated; } + void beginLoadIfNeeded(CachedResourceLoader&); + bool stillNeedsLoad() const override { return !m_loadInitiated; } + + virtual bool ensureCustomFontData(const AtomicString& remoteURI); + static std::unique_ptr<FontCustomPlatformData> createCustomFontData(SharedBuffer&, bool& wrapping); + static FontPlatformData platformDataFromCustomData(FontCustomPlatformData&, const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&); - bool ensureCustomFontData(); - FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); + virtual RefPtr<Font> createFont(const FontDescription&, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings&, const FontVariantSettings&); -#if ENABLE(SVG_FONTS) - bool ensureSVGFontData(); - SVGFontElement* getSVGFontById(const String&) const; -#endif +protected: + FontPlatformData platformDataFromCustomData(const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&); + + bool ensureCustomFontData(SharedBuffer* data); private: - virtual void checkNotify() override; - virtual bool mayTryReplaceEncodedData() const override; + void checkNotify() override; + bool mayTryReplaceEncodedData() const override; - virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&) override; + void load(CachedResourceLoader&) override; + NO_RETURN_DUE_TO_ASSERT void setBodyDataFrom(const CachedResource&) final { ASSERT_NOT_REACHED(); } - virtual void didAddClient(CachedResourceClient*) override; - virtual void finishLoading(ResourceBuffer*) override; + void didAddClient(CachedResourceClient&) override; + void finishLoading(SharedBuffer*) override; - virtual void allClientsRemoved() override; + void allClientsRemoved() override; - std::unique_ptr<FontCustomPlatformData> m_fontData; + std::unique_ptr<FontCustomPlatformData> m_fontCustomPlatformData; bool m_loadInitiated; bool m_hasCreatedFontDataWrappingResource; -#if ENABLE(SVG_FONTS) - RefPtr<SVGDocument> m_externalSVGDocument; -#endif - friend class MemoryCache; }; } // namespace WebCore -#endif // CachedFont_h +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedFont, CachedResource::FontResource) diff --git a/Source/WebCore/loader/cache/CachedFontClient.h b/Source/WebCore/loader/cache/CachedFontClient.h index e87040ea2..a1388fb88 100644 --- a/Source/WebCore/loader/cache/CachedFontClient.h +++ b/Source/WebCore/loader/cache/CachedFontClient.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedFontClient_h -#define CachedFontClient_h +#pragma once #include "CachedResourceClient.h" @@ -36,10 +35,8 @@ class CachedFontClient : public CachedResourceClient { public: virtual ~CachedFontClient() { } static CachedResourceClientType expectedType() { return FontType; } - virtual CachedResourceClientType resourceClientType() const override { return expectedType(); } - virtual void fontLoaded(CachedFont*) { } + CachedResourceClientType resourceClientType() const override { return expectedType(); } + virtual void fontLoaded(CachedFont&) { } }; } // namespace WebCore - -#endif // CachedFontClient_h diff --git a/Source/WebCore/loader/cache/CachedImage.cpp b/Source/WebCore/loader/cache/CachedImage.cpp index 5525fb1f3..65f0bf79c 100644 --- a/Source/WebCore/loader/cache/CachedImage.cpp +++ b/Source/WebCore/loader/cache/CachedImage.cpp @@ -35,15 +35,15 @@ #include "FrameLoaderTypes.h" #include "FrameView.h" #include "MemoryCache.h" -#include "Page.h" #include "RenderElement.h" -#include "ResourceBuffer.h" +#include "SVGImage.h" #include "SecurityOrigin.h" #include "Settings.h" +#include "SharedBuffer.h" #include "SubresourceLoader.h" #include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> -#include <wtf/Vector.h> #if PLATFORM(IOS) #include "SystemMemory.h" @@ -53,40 +53,28 @@ #include "PDFDocumentImage.h" #endif -#if ENABLE(SVG) -#include "SVGImage.h" -#endif - -#if ENABLE(DISK_IMAGE_CACHE) -#include "DiskImageCacheIOS.h" -#endif - namespace WebCore { -CachedImage::CachedImage(const ResourceRequest& resourceRequest) - : CachedResource(resourceRequest, ImageResource) - , m_image(0) - , m_shouldPaintBrokenImage(true) +CachedImage::CachedImage(CachedResourceRequest&& request, SessionID sessionID) + : CachedResource(WTFMove(request), ImageResource, sessionID) { setStatus(Unknown); } -CachedImage::CachedImage(Image* image) - : CachedResource(ResourceRequest(), ImageResource) +CachedImage::CachedImage(Image* image, SessionID sessionID) + : CachedResource(URL(), ImageResource, sessionID) , m_image(image) - , m_shouldPaintBrokenImage(true) { - setStatus(Cached); - setLoading(false); } -CachedImage::CachedImage(const URL& url, Image* image) - : CachedResource(ResourceRequest(url), ImageResource) +CachedImage::CachedImage(const URL& url, Image* image, SessionID sessionID) + : CachedResource(url, ImageResource, sessionID) , m_image(image) - , m_shouldPaintBrokenImage(true) + , m_isManuallyCached(true) { - setStatus(Cached); - setLoading(false); + // Use the incoming URL in the response field. This ensures that code using the response directly, + // such as origin checks for security, actually see something. + m_response.setURL(url); } CachedImage::~CachedImage() @@ -94,56 +82,69 @@ CachedImage::~CachedImage() clearImage(); } -void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options) +void CachedImage::load(CachedResourceLoader& loader) { - if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages()) - CachedResource::load(cachedResourceLoader, options); + if (loader.shouldPerformImageLoad(url())) + CachedResource::load(loader); else setLoading(false); } -void CachedImage::didAddClient(CachedResourceClient* c) +void CachedImage::setBodyDataFrom(const CachedResource& resource) +{ + ASSERT(resource.type() == type()); + const CachedImage& image = static_cast<const CachedImage&>(resource); + + CachedResource::setBodyDataFrom(resource); + + m_image = image.m_image; + m_imageObserver = image.m_imageObserver; + if (m_imageObserver) + m_imageObserver->add(*this); + + if (m_image && is<SVGImage>(*m_image)) + m_svgImageCache = std::make_unique<SVGImageCache>(&downcast<SVGImage>(*m_image)); +} + +void CachedImage::didAddClient(CachedResourceClient& client) { if (m_data && !m_image && !errorOccurred()) { createImage(); - m_image->setData(m_data->sharedBuffer(), true); + m_image->setData(m_data.copyRef(), true); } - - ASSERT(c->resourceClientType() == CachedImageClient::expectedType()); + + ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); if (m_image && !m_image->isNull()) - static_cast<CachedImageClient*>(c)->imageChanged(this); + static_cast<CachedImageClient&>(client).imageChanged(this); - CachedResource::didAddClient(c); + CachedResource::didAddClient(client); } -void CachedImage::didRemoveClient(CachedResourceClient* c) +void CachedImage::didRemoveClient(CachedResourceClient& client) { - ASSERT(c); - ASSERT(c->resourceClientType() == CachedImageClient::expectedType()); + ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); + + m_pendingContainerSizeRequests.remove(&static_cast<CachedImageClient&>(client)); - m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(c)); -#if ENABLE(SVG) if (m_svgImageCache) - m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c)); -#endif + m_svgImageCache->removeClientFromCache(&static_cast<CachedImageClient&>(client)); - CachedResource::didRemoveClient(c); + CachedResource::didRemoveClient(client); } void CachedImage::switchClientsToRevalidatedResource() { - ASSERT(resourceToRevalidate()); - ASSERT(resourceToRevalidate()->isImage()); + ASSERT(is<CachedImage>(resourceToRevalidate())); // Pending container size requests need to be transferred to the revalidated resource. if (!m_pendingContainerSizeRequests.isEmpty()) { // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce(). ContainerSizeRequests switchContainerSizeRequests; - for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) - switchContainerSizeRequests.set(it->key, it->value); + for (auto& request : m_pendingContainerSizeRequests) + switchContainerSizeRequests.set(request.key, request.value); CachedResource::switchClientsToRevalidatedResource(); - CachedImage* revalidatedCachedImage = static_cast<CachedImage*>(resourceToRevalidate()); - for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it) - revalidatedCachedImage->setContainerSizeForRenderer(it->key, it->value.first, it->value.second); + CachedImage& revalidatedCachedImage = downcast<CachedImage>(*resourceToRevalidate()); + for (auto& request : switchContainerSizeRequests) + revalidatedCachedImage.setContainerSizeForRenderer(request.key, request.value.first, request.value.second); return; } @@ -159,12 +160,17 @@ void CachedImage::allClientsRemoved() std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const { + if (deviceScaleFactor >= 3) { + static NeverDestroyed<Image*> brokenImageVeryHiRes(Image::loadPlatformResource("missingImage@3x").leakRef()); + return std::make_pair(brokenImageVeryHiRes, 3); + } + if (deviceScaleFactor >= 2) { - DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef())); + static NeverDestroyed<Image*> brokenImageHiRes(Image::loadPlatformResource("missingImage@2x").leakRef()); return std::make_pair(brokenImageHiRes, 2); } - DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef())); + static NeverDestroyed<Image*> brokenImageLoRes(Image::loadPlatformResource("missingImage").leakRef()); return std::make_pair(brokenImageLoRes, 1); } @@ -175,8 +181,6 @@ bool CachedImage::willPaintBrokenImage() const Image* CachedImage::image() { - ASSERT(!isPurgeable()); - if (errorOccurred() && m_shouldPaintBrokenImage) { // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() @@ -192,8 +196,6 @@ Image* CachedImage::image() Image* CachedImage::imageForRenderer(const RenderObject* renderer) { - ASSERT(!isPurgeable()); - if (errorOccurred() && m_shouldPaintBrokenImage) { // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() @@ -204,19 +206,15 @@ Image* CachedImage::imageForRenderer(const RenderObject* renderer) if (!m_image) return Image::nullImage(); -#if ENABLE(SVG) if (m_image->isSVGImage()) { Image* image = m_svgImageCache->imageForRenderer(renderer); if (image != Image::nullImage()) return image; } -#else - UNUSED_PARAM(renderer); -#endif return m_image.get(); } -void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const IntSize& containerSize, float containerZoom) +void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const LayoutSize& containerSize, float containerZoom) { if (containerSize.isEmpty()) return; @@ -226,17 +224,13 @@ void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom)); return; } -#if ENABLE(SVG) + if (!m_image->isSVGImage()) { m_image->setContainerSize(containerSize); return; } m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom); -#else - UNUSED_PARAM(containerZoom); - m_image->setContainerSize(containerSize); -#endif } bool CachedImage::usesImageContainerSize() const @@ -263,42 +257,19 @@ bool CachedImage::imageHasRelativeHeight() const return false; } -LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType) +LayoutSize CachedImage::imageSizeForRenderer(const RenderElement* renderer, float multiplier, SizeType sizeType) { - ASSERT(!isPurgeable()); - if (!m_image) - return IntSize(); + return LayoutSize(); - LayoutSize imageSize(m_image->size()); + LayoutSize imageSize; -#if ENABLE(CSS_IMAGE_ORIENTATION) - if (renderer && m_image->isBitmapImage()) { - ImageOrientationDescription orientationDescription(renderer->shouldRespectImageOrientation(), renderer->style().imageOrientation()); - if (orientationDescription.respectImageOrientation() == RespectImageOrientation) - imageSize = toBitmapImage(m_image.get())->sizeRespectingOrientation(orientationDescription); - } -#else - if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)) -#if !PLATFORM(IOS) - imageSize = toBitmapImage(m_image.get())->sizeRespectingOrientation(); -#else - { - // On iOS, the image may have been subsampled to accommodate our size restrictions. However - // we should tell the renderer what the original size was. - imageSize = toBitmapImage(m_image.get())->originalSizeRespectingOrientation(); - } else if (m_image->isBitmapImage()) - imageSize = toBitmapImage(m_image.get())->originalSize(); -#endif // !PLATFORM(IOS) -#endif // ENABLE(CSS_IMAGE_ORIENTATION) - -#if ENABLE(SVG) - else if (m_image->isSVGImage() && sizeType == UsedSize) { - imageSize = m_svgImageCache->imageSizeForRenderer(renderer); - } -#else - UNUSED_PARAM(sizeType); -#endif + if (is<BitmapImage>(*m_image) && renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation) + imageSize = LayoutSize(downcast<BitmapImage>(*m_image).sizeRespectingOrientation()); + else if (is<SVGImage>(*m_image) && sizeType == UsedSize) + imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer)); + else + imageSize = LayoutSize(m_image->size()); if (multiplier == 1.0f) return imageSize; @@ -331,7 +302,7 @@ void CachedImage::checkShouldPaintBrokenImage() if (!m_loader || m_loader->reachedTerminalState()) return; - m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(m_resourceRequest.url()); + m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url()); } void CachedImage::clear() @@ -347,77 +318,98 @@ inline void CachedImage::createImage() // Create the image if it doesn't yet exist. if (m_image) return; + + m_imageObserver = CachedImageObserver::create(*this); + + if (m_response.mimeType() == "image/svg+xml") { + auto svgImage = SVGImage::create(*m_imageObserver, url()); + m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.ptr()); + m_image = WTFMove(svgImage); #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS) - else if (m_response.mimeType() == "application/pdf") - m_image = PDFDocumentImage::create(this); + } else if (m_response.mimeType() == "application/pdf") { + m_image = PDFDocumentImage::create(m_imageObserver.get()); #endif -#if ENABLE(SVG) - else if (m_response.mimeType() == "image/svg+xml") { - RefPtr<SVGImage> svgImage = SVGImage::create(this); - m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get()); - m_image = svgImage.release(); - } -#endif - else - m_image = BitmapImage::create(this); + } else + m_image = BitmapImage::create(m_imageObserver.get()); if (m_image) { // Send queued container size requests. if (m_image->usesContainerSize()) { - for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) - setContainerSizeForRenderer(it->key, it->value.first, it->value.second); + for (auto& request : m_pendingContainerSizeRequests) + setContainerSizeForRenderer(request.key, request.value.first, request.value.second); } m_pendingContainerSizeRequests.clear(); } } -inline void CachedImage::clearImage() +CachedImage::CachedImageObserver::CachedImageObserver(CachedImage& image) { - // If our Image has an observer, it's always us so we need to clear the back pointer - // before dropping our reference. - if (m_image) - m_image->setImageObserver(0); - m_image.clear(); + m_cachedImages.reserveInitialCapacity(1); + m_cachedImages.append(&image); + if (auto* loader = image.loader()) { + m_allowSubsampling = loader->frameLoader()->frame().settings().imageSubsamplingEnabled(); + m_allowLargeImageAsyncDecoding = loader->frameLoader()->frame().settings().largeImageAsyncDecodingEnabled(); + m_allowAnimatedImageAsyncDecoding = loader->frameLoader()->frame().settings().animatedImageAsyncDecodingEnabled(); + m_showDebugBackground = loader->frameLoader()->frame().settings().showDebugBorders(); + } } -bool CachedImage::canBeDrawn() const +void CachedImage::CachedImageObserver::decodedSizeChanged(const Image* image, long long delta) { - if (!m_image || m_image->isNull()) - return false; + for (auto cachedImage : m_cachedImages) + cachedImage->decodedSizeChanged(image, delta); +} - if (!m_loader || m_loader->reachedTerminalState()) - return true; +void CachedImage::CachedImageObserver::didDraw(const Image* image) +{ + for (auto cachedImage : m_cachedImages) + cachedImage->didDraw(image); +} - size_t estimatedDecodedImageSize = m_image->width() * m_image->height() * 4; // no overflow check - return estimatedDecodedImageSize <= m_loader->frameLoader()->frame().settings().maximumDecodedImageSize(); +void CachedImage::CachedImageObserver::animationAdvanced(const Image* image) +{ + for (auto cachedImage : m_cachedImages) + cachedImage->animationAdvanced(image); } -void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data) +void CachedImage::CachedImageObserver::changedInRect(const Image* image, const IntRect* rect) { - m_data = data; - if (!data) - return; + for (auto cachedImage : m_cachedImages) + cachedImage->changedInRect(image, rect); +} + +inline void CachedImage::clearImage() +{ + if (m_imageObserver) { + m_imageObserver->remove(*this); + m_imageObserver = nullptr; + } + m_image = nullptr; +} + +void CachedImage::addIncrementalDataBuffer(SharedBuffer& data) +{ + m_data = &data; createImage(); // Have the image update its data from its internal buffer. // It will not do anything now, but will delay decoding until // queried for info (like size or specific image frames). - bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false); + bool sizeAvailable = m_image->setData(&data, false); if (!sizeAvailable) return; - if (!canBeDrawn()) { - // There's no image to draw or its decoded size is bigger than the maximum allowed. + if (m_image->isNull()) { + // Image decoding failed. Either we need more image data or the image data is malformed. error(errorOccurred() ? status() : DecodeError); if (inCache()) - memoryCache()->remove(this); + MemoryCache::singleton().remove(*this); return; } - // Go ahead and tell our observers to try to draw. - // Each chunk from the network causes observers to repaint, which will - // force that chunk to decode. + // Tell our observers to try to draw. + // Each chunk from the network causes observers to repaint, which will force that chunk to decode. // It would be nice to only redraw the decoded band of the image, but with the current design // (decoding delayed until painting) that seems hard. notifyObservers(); @@ -425,32 +417,34 @@ void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data) setEncodedSize(m_image->data() ? m_image->data()->size() : 0); } -void CachedImage::addDataBuffer(ResourceBuffer* data) +void CachedImage::addDataBuffer(SharedBuffer& data) { - ASSERT(m_options.dataBufferingPolicy == BufferData); + ASSERT(dataBufferingPolicy() == BufferData); addIncrementalDataBuffer(data); + CachedResource::addDataBuffer(data); } void CachedImage::addData(const char* data, unsigned length) { - ASSERT(m_options.dataBufferingPolicy == DoNotBufferData); - addIncrementalDataBuffer(ResourceBuffer::create(data, length).get()); + ASSERT(dataBufferingPolicy() == DoNotBufferData); + addIncrementalDataBuffer(SharedBuffer::create(data, length)); + CachedResource::addData(data, length); } -void CachedImage::finishLoading(ResourceBuffer* data) +void CachedImage::finishLoading(SharedBuffer* data) { m_data = data; if (!m_image && data) createImage(); if (m_image) - m_image->setData(m_data->sharedBuffer(), true); + m_image->setData(data, true); - if (!canBeDrawn()) { - // There's no image to draw or its decoded size is bigger than the maximum allowed. + if (!m_image || m_image->isNull()) { + // Image decoding failed; the image data is malformed. error(errorOccurred() ? status() : DecodeError); if (inCache()) - memoryCache()->remove(this); + MemoryCache::singleton().remove(*this); return; } @@ -460,6 +454,16 @@ void CachedImage::finishLoading(ResourceBuffer* data) CachedResource::finishLoading(data); } +void CachedImage::didReplaceSharedBufferContents() +{ + if (m_image) { + // Let the Image know that the SharedBuffer has been rejigged, so it can let go of any references to the heap-allocated resource buffer. + // FIXME(rdar://problem/24275617): It would be better if we could somehow tell the Image's decoder to swap in the new contents without destroying anything. + m_image->destroyDecodedData(true); + } + CachedResource::didReplaceSharedBufferContents(); +} + void CachedImage::error(CachedResource::Status status) { checkShouldPaintBrokenImage(); @@ -478,23 +482,20 @@ void CachedImage::responseReceived(const ResourceResponse& response) void CachedImage::destroyDecodedData() { bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); - if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) { - // Image refs the data buffer so we should not make it purgeable while the image is alive. - // Invoking addClient() will reconstruct the image object. - m_image = 0; + if (canDeleteImage && !isLoading() && !hasClients()) { + m_image = nullptr; setDecodedSize(0); - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) - makePurgeable(true); } else if (m_image && !errorOccurred()) m_image->destroyDecodedData(); } -void CachedImage::decodedSizeChanged(const Image* image, int delta) +void CachedImage::decodedSizeChanged(const Image* image, long long delta) { if (!image || image != m_image) return; - - setDecodedSize(decodedSize() + delta); + + ASSERT(delta >= 0 || decodedSize() + delta >= 0); + setDecodedSize(static_cast<unsigned>(decodedSize() + delta)); } void CachedImage::didDraw(const Image* image) @@ -509,132 +510,47 @@ void CachedImage::didDraw(const Image* image) CachedResource::didAccessDecodedData(timeStamp); } -bool CachedImage::shouldPauseAnimation(const Image* image) -{ - if (!image || image != m_image) - return false; - - CachedResourceClientWalker<CachedImageClient> w(m_clients); - while (CachedImageClient* c = w.next()) { - if (c->willRenderImage(this)) - return false; - } - - return true; -} - void CachedImage::animationAdvanced(const Image* image) { if (!image || image != m_image) return; - notifyObservers(); + CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); + while (CachedImageClient* client = clientWalker.next()) + client->newImageAnimationFrameAvailable(*this); } -void CachedImage::changedInRect(const Image* image, const IntRect& rect) +void CachedImage::changedInRect(const Image* image, const IntRect* rect) { if (!image || image != m_image) return; - notifyObservers(&rect); -} - -void CachedImage::resumeAnimatingImagesForLoader(CachedResourceLoader* loader) -{ - const CachedResourceLoader::DocumentResourceMap& resources = loader->allCachedResources(); - - for (CachedResourceLoader::DocumentResourceMap::const_iterator it = resources.begin(), end = resources.end(); it != end; ++it) { - const CachedResourceHandle<CachedResource>& resource = it->value; - if (!resource || !resource->isImage()) - continue; - CachedImage* cachedImage = static_cast<CachedImage*>(resource.get()); - if (!cachedImage->hasImage()) - continue; - Image* image = cachedImage->image(); - if (!image->isBitmapImage()) - continue; - BitmapImage* bitmapImage = toBitmapImage(image); - if (!bitmapImage->canAnimate()) - continue; - cachedImage->animationAdvanced(bitmapImage); - } + notifyObservers(rect); } bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer) { Image* image = imageForRenderer(renderer); - if (image->isBitmapImage()) - image->nativeImageForCurrentFrame(); // force decode return image->currentFrameKnownToBeOpaque(); } -#if ENABLE(DISK_IMAGE_CACHE) -bool CachedImage::canUseDiskImageCache() const -{ - if (isLoading() || errorOccurred()) - return false; - - if (!m_data) - return false; - - if (isPurgeable()) - return false; - - if (m_data->size() < diskImageCache().minimumImageSize()) - return false; - - // "Cache-Control: no-store" resources may be marked as such because they may - // contain sensitive information. We should not write these resources to disk. - if (m_response.cacheControlContainsNoStore()) - return false; - - // Testing shows that PDF images did not work when memory mapped. - // However, SVG images and Bitmap images were fine. See: - // <rdar://problem/8591834> Disk Image Cache should support PDF Images - if (m_response.mimeType() == "application/pdf") - return false; - - return true; -} - -void CachedImage::useDiskImageCache() +bool CachedImage::isOriginClean(SecurityOrigin* origin) { - ASSERT(canUseDiskImageCache()); - ASSERT(!isUsingDiskImageCache()); - m_data->sharedBuffer()->allowToBeMemoryMapped(); -} -#endif - -bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin) -{ - if (!image()->hasSingleSecurityOrigin()) - return false; - if (passesAccessControlCheck(securityOrigin)) - return true; - return !securityOrigin->taintsCanvas(response().url()); + ASSERT_UNUSED(origin, origin); + ASSERT(this->origin()); + ASSERT(origin->toString() == this->origin()->toString()); + return !loadFailedOrCanceled() && isCORSSameOrigin(); } -#if USE(CF) -// FIXME: We should look to incorporate the functionality of CachedImageManual -// into CachedImage or find a better place for this class. -// FIXME: Remove the USE(CF) once we make MemoryCache::addImageToCache() platform-independent. -CachedImageManual::CachedImageManual(const URL& url, Image* image) - : CachedImage(url, image) - , m_fakeClient(std::make_unique<CachedImageClient>()) +CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const { - // Use the incoming URL in the response field. This ensures that code - // using the response directly, such as origin checks for security, - // actually see something. - m_response.setURL(url); -} - -bool CachedImageManual::mustRevalidateDueToCacheHeaders(CachePolicy) const -{ - // Do not revalidate manually cached images. This mechanism is used as a - // way to efficiently share an image from the client to content and - // the URL for that image may not represent a resource that can be - // retrieved by standard means. If the manual caching SPI is used, it is - // incumbent on the client to only use valid resources. - return false; + if (UNLIKELY(isManuallyCached())) { + // Do not revalidate manually cached images. This mechanism is used as a + // way to efficiently share an image from the client to content and + // the URL for that image may not represent a resource that can be + // retrieved by standard means. If the manual caching SPI is used, it is + // incumbent on the client to only use valid resources. + return RevalidationDecision::No; + } + return CachedResource::makeRevalidationDecision(cachePolicy); } -#endif } // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedImage.h b/Source/WebCore/loader/cache/CachedImage.h index 12b77cc46..985aa9749 100644 --- a/Source/WebCore/loader/cache/CachedImage.h +++ b/Source/WebCore/loader/cache/CachedImage.h @@ -20,17 +20,16 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedImage_h -#define CachedImage_h +#pragma once #include "CachedResource.h" +#include "Image.h" #include "ImageObserver.h" #include "IntRect.h" #include "IntSizeHash.h" #include "LayoutSize.h" #include "SVGImageCache.h" #include <wtf/HashMap.h> -#include <wtf/Vector.h> namespace WebCore { @@ -44,124 +43,132 @@ class SecurityOrigin; struct Length; -class CachedImage : public CachedResource, public ImageObserver { +class CachedImage final : public CachedResource { friend class MemoryCache; public: - CachedImage(const ResourceRequest&); - CachedImage(Image*); - CachedImage(const URL&, Image*); + CachedImage(CachedResourceRequest&&, SessionID); + CachedImage(Image*, SessionID); + // Constructor to use for manually cached images. + CachedImage(const URL&, Image*, SessionID); virtual ~CachedImage(); - Image* image(); // Returns the nullImage() if the image is not available yet. - Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet. + WEBCORE_EXPORT Image* image(); // Returns the nullImage() if the image is not available yet. + WEBCORE_EXPORT Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet. bool hasImage() const { return m_image.get(); } - bool currentFrameKnownToBeOpaque(const RenderElement*); // Side effect: ensures decoded image is in cache, therefore should only be called when about to draw the image. + bool currentFrameKnownToBeOpaque(const RenderElement*); std::pair<Image*, float> brokenImage(float deviceScaleFactor) const; // Returns an image and the image's resolution scale factor. - bool willPaintBrokenImage() const; + bool willPaintBrokenImage() const; - bool canRender(const RenderObject* renderer, float multiplier) { return !errorOccurred() && !imageSizeForRenderer(renderer, multiplier).isEmpty(); } + bool canRender(const RenderElement* renderer, float multiplier) { return !errorOccurred() && !imageSizeForRenderer(renderer, multiplier).isEmpty(); } - void setContainerSizeForRenderer(const CachedImageClient*, const IntSize&, float); + void setContainerSizeForRenderer(const CachedImageClient*, const LayoutSize&, float); bool usesImageContainerSize() const; bool imageHasRelativeWidth() const; bool imageHasRelativeHeight() const; - virtual void addDataBuffer(ResourceBuffer*) override; - virtual void finishLoading(ResourceBuffer*) override; + void addDataBuffer(SharedBuffer&) override; + void finishLoading(SharedBuffer*) override; enum SizeType { UsedSize, IntrinsicSize }; // This method takes a zoom multiplier that can be used to increase the natural size of the image by the zoom. - LayoutSize imageSizeForRenderer(const RenderObject*, float multiplier, SizeType = UsedSize); // returns the size of the complete image. + LayoutSize imageSizeForRenderer(const RenderElement*, float multiplier, SizeType = UsedSize); // returns the size of the complete image. void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio); -#if USE(CF) - // FIXME: Remove the USE(CF) once we make MemoryCache::addImageToCache() platform-independent. - virtual bool isManual() const { return false; } -#endif - - static void resumeAnimatingImagesForLoader(CachedResourceLoader*); - -#if ENABLE(DISK_IMAGE_CACHE) - virtual bool canUseDiskImageCache() const override; - virtual void useDiskImageCache() override; -#endif + bool isManuallyCached() const { return m_isManuallyCached; } + RevalidationDecision makeRevalidationDecision(CachePolicy) const override; + void load(CachedResourceLoader&) override; bool isOriginClean(SecurityOrigin*); private: - virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&) override; - void clear(); + CachedImage(CachedImage&, const ResourceRequest&, SessionID); + + void setBodyDataFrom(const CachedResource&) final; + void createImage(); void clearImage(); - bool canBeDrawn() const; // If not null, changeRect is the changed part of the image. - void notifyObservers(const IntRect* changeRect = 0); - virtual PurgePriority purgePriority() const override { return PurgeFirst; } + void notifyObservers(const IntRect* changeRect = nullptr); void checkShouldPaintBrokenImage(); - virtual void switchClientsToRevalidatedResource() override; - virtual bool mayTryReplaceEncodedData() const override { return true; } + void switchClientsToRevalidatedResource() final; + bool mayTryReplaceEncodedData() const final { return true; } - virtual void didAddClient(CachedResourceClient*) override; - virtual void didRemoveClient(CachedResourceClient*) override; + void didAddClient(CachedResourceClient&) final; + void didRemoveClient(CachedResourceClient&) final; - virtual void allClientsRemoved() override; - virtual void destroyDecodedData() override; + void allClientsRemoved() override; + void destroyDecodedData() override; - virtual void addData(const char* data, unsigned length) override; - virtual void error(CachedResource::Status) override; - virtual void responseReceived(const ResourceResponse&) override; + void addData(const char* data, unsigned length) override; + void error(CachedResource::Status) override; + void responseReceived(const ResourceResponse&) override; // For compatibility, images keep loading even if there are HTTP errors. - virtual bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; } - - virtual bool isImage() const override { return true; } - virtual bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); } + bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; } + + bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); } + + class CachedImageObserver final : public RefCounted<CachedImageObserver>, public ImageObserver { + public: + static Ref<CachedImageObserver> create(CachedImage& image) { return adoptRef(*new CachedImageObserver(image)); } + void add(CachedImage& image) { m_cachedImages.append(&image); } + void remove(CachedImage& image) { m_cachedImages.removeFirst(&image); } + + private: + explicit CachedImageObserver(CachedImage&); + + // ImageObserver API + URL sourceUrl() const override { return m_cachedImages[0]->url(); } + bool allowSubsampling() const final { return m_allowSubsampling; } + bool allowLargeImageAsyncDecoding() const override { return m_allowLargeImageAsyncDecoding; } + bool allowAnimatedImageAsyncDecoding() const override { return m_allowAnimatedImageAsyncDecoding; } + bool showDebugBackground() const final { return m_showDebugBackground; } + void decodedSizeChanged(const Image*, long long delta) final; + void didDraw(const Image*) final; + + void animationAdvanced(const Image*) final; + void changedInRect(const Image*, const IntRect*) final; + + Vector<CachedImage*> m_cachedImages; + // The default value of m_allowSubsampling should be the same as defaultImageSubsamplingEnabled in Settings.cpp +#if PLATFORM(IOS) + bool m_allowSubsampling { true }; +#else + bool m_allowSubsampling { false }; +#endif + bool m_allowLargeImageAsyncDecoding { true }; + bool m_allowAnimatedImageAsyncDecoding { true }; + bool m_showDebugBackground { false }; + }; - // ImageObserver - virtual void decodedSizeChanged(const Image*, int delta) override; - virtual void didDraw(const Image*) override; + void decodedSizeChanged(const Image*, long long delta); + void didDraw(const Image*); + void animationAdvanced(const Image*); + void changedInRect(const Image*, const IntRect*); - virtual bool shouldPauseAnimation(const Image*) override; - virtual void animationAdvanced(const Image*) override; - virtual void changedInRect(const Image*, const IntRect&) override; + void addIncrementalDataBuffer(SharedBuffer&); - void addIncrementalDataBuffer(ResourceBuffer*); + void didReplaceSharedBufferContents() override; - typedef std::pair<IntSize, float> SizeAndZoom; + typedef std::pair<LayoutSize, float> SizeAndZoom; typedef HashMap<const CachedImageClient*, SizeAndZoom> ContainerSizeRequests; ContainerSizeRequests m_pendingContainerSizeRequests; + RefPtr<CachedImageObserver> m_imageObserver; RefPtr<Image> m_image; -#if ENABLE(SVG) std::unique_ptr<SVGImageCache> m_svgImageCache; -#endif - bool m_shouldPaintBrokenImage; -}; - -#if USE(CF) -// FIXME: We should look to incorporate the functionality of CachedImageManual -// into CachedImage or find a better place for this class. -// FIXME: Remove the USE(CF) once we make MemoryCache::addImageToCache() platform-independent. -class CachedImageManual : public CachedImage { -public: - CachedImageManual(const URL&, Image*); - void addFakeClient() { addClient(m_fakeClient.get()); } - void removeFakeClient() { removeClient(m_fakeClient.get()); } - virtual bool isManual() const override { return true; } - virtual bool mustRevalidateDueToCacheHeaders(CachePolicy) const; -private: - std::unique_ptr<CachedResourceClient> m_fakeClient; + bool m_isManuallyCached { false }; + bool m_shouldPaintBrokenImage { true }; }; -#endif -} +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedImage, CachedResource::ImageResource) diff --git a/Source/WebCore/loader/cache/CachedImageClient.h b/Source/WebCore/loader/cache/CachedImageClient.h index 1c3fac206..e09621ed8 100644 --- a/Source/WebCore/loader/cache/CachedImageClient.h +++ b/Source/WebCore/loader/cache/CachedImageClient.h @@ -20,8 +20,7 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedImageClient_h -#define CachedImageClient_h +#pragma once #include "CachedResourceClient.h" @@ -34,19 +33,14 @@ class CachedImageClient : public CachedResourceClient { public: virtual ~CachedImageClient() { } static CachedResourceClientType expectedType() { return ImageType; } - virtual CachedResourceClientType resourceClientType() const override { return expectedType(); } + CachedResourceClientType resourceClientType() const override { return expectedType(); } - // Called whenever a frame of an image changes, either because we got more data from the network or - // because we are animating. If not null, the IntRect is the changed rect of the image. - virtual void imageChanged(CachedImage*, const IntRect* = 0) { } + // Called whenever a frame of an image changes because we got more data from the network. + // If not null, the IntRect is the changed rect of the image. + virtual void imageChanged(CachedImage*, const IntRect* = nullptr) { } - // Called to find out if this client wants to actually display the image. Used to tell when we - // can halt animation. Content nodes that hold image refs for example would not render the image, - // but RenderImages would (assuming they have visibility: visible and their render tree isn't hidden - // e.g., in the b/f cache or in a background tab). - virtual bool willRenderImage(CachedImage*) { return false; } + // Called when GIF animation progresses. + virtual void newImageAnimationFrameAvailable(CachedImage& image) { imageChanged(&image); } }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedRawResource.cpp b/Source/WebCore/loader/cache/CachedRawResource.cpp index a6eff5efb..05b9de128 100644 --- a/Source/WebCore/loader/cache/CachedRawResource.cpp +++ b/Source/WebCore/loader/cache/CachedRawResource.cpp @@ -29,23 +29,26 @@ #include "CachedRawResourceClient.h" #include "CachedResourceClientWalker.h" #include "CachedResourceLoader.h" -#include "ResourceBuffer.h" +#include "HTTPHeaderNames.h" +#include "SharedBuffer.h" #include "SubresourceLoader.h" -#include <wtf/PassRefPtr.h> +#include <wtf/text/StringView.h> namespace WebCore { -CachedRawResource::CachedRawResource(ResourceRequest& resourceRequest, Type type) - : CachedResource(resourceRequest, type) +CachedRawResource::CachedRawResource(CachedResourceRequest&& request, Type type, SessionID sessionID) + : CachedResource(WTFMove(request), type, sessionID) , m_identifier(0) + , m_allowEncodedDataReplacement(true) { + ASSERT(isMainOrMediaOrRawResource()); } -const char* CachedRawResource::calculateIncrementalDataChunk(ResourceBuffer* data, unsigned& incrementalDataLength) +const char* CachedRawResource::calculateIncrementalDataChunk(SharedBuffer* data, unsigned& incrementalDataLength) { incrementalDataLength = 0; if (!data) - return 0; + return nullptr; unsigned previousDataLength = encodedSize(); ASSERT(data->size() >= previousDataLength); @@ -53,34 +56,37 @@ const char* CachedRawResource::calculateIncrementalDataChunk(ResourceBuffer* dat return data->data() + previousDataLength; } -void CachedRawResource::addDataBuffer(ResourceBuffer* data) +void CachedRawResource::addDataBuffer(SharedBuffer& data) { - CachedResourceHandle<CachedRawResource> protect(this); - ASSERT(m_options.dataBufferingPolicy == BufferData); - m_data = data; + CachedResourceHandle<CachedRawResource> protectedThis(this); + ASSERT(dataBufferingPolicy() == BufferData); + m_data = &data; unsigned incrementalDataLength; - const char* incrementalData = calculateIncrementalDataChunk(data, incrementalDataLength); - if (data) - setEncodedSize(data->size()); + const char* incrementalData = calculateIncrementalDataChunk(&data, incrementalDataLength); + setEncodedSize(data.size()); notifyClientsDataWasReceived(incrementalData, incrementalDataLength); - if (m_options.dataBufferingPolicy == DoNotBufferData) { + if (dataBufferingPolicy() == DoNotBufferData) { if (m_loader) m_loader->setDataBufferingPolicy(DoNotBufferData); clear(); + return; } + + CachedResource::addDataBuffer(data); } void CachedRawResource::addData(const char* data, unsigned length) { - ASSERT(m_options.dataBufferingPolicy == DoNotBufferData); + ASSERT(dataBufferingPolicy() == DoNotBufferData); notifyClientsDataWasReceived(data, length); + CachedResource::addData(data, length); } -void CachedRawResource::finishLoading(ResourceBuffer* data) +void CachedRawResource::finishLoading(SharedBuffer* data) { - CachedResourceHandle<CachedRawResource> protect(this); - DataBufferingPolicy dataBufferingPolicy = m_options.dataBufferingPolicy; + CachedResourceHandle<CachedRawResource> protectedThis(this); + DataBufferingPolicy dataBufferingPolicy = this->dataBufferingPolicy(); if (dataBufferingPolicy == BufferData) { m_data = data; @@ -91,8 +97,12 @@ void CachedRawResource::finishLoading(ResourceBuffer* data) notifyClientsDataWasReceived(incrementalData, incrementalDataLength); } +#if USE(QUICK_LOOK) + m_allowEncodedDataReplacement = !m_loader->isQuickLookResource(); +#endif + CachedResource::finishLoading(data); - if (dataBufferingPolicy == BufferData && m_options.dataBufferingPolicy == DoNotBufferData) { + if (dataBufferingPolicy == BufferData && this->dataBufferingPolicy() == DoNotBufferData) { if (m_loader) m_loader->setDataBufferingPolicy(DoNotBufferData); clear(); @@ -104,37 +114,45 @@ void CachedRawResource::notifyClientsDataWasReceived(const char* data, unsigned if (!length) return; - CachedResourceHandle<CachedRawResource> protect(this); + CachedResourceHandle<CachedRawResource> protectedThis(this); CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); while (CachedRawResourceClient* c = w.next()) - c->dataReceived(this, data, length); + c->dataReceived(*this, data, length); } -void CachedRawResource::didAddClient(CachedResourceClient* c) +void CachedRawResource::didAddClient(CachedResourceClient& c) { if (!hasClient(c)) return; // The calls to the client can result in events running, potentially causing // this resource to be evicted from the cache and all clients to be removed, // so a protector is necessary. - CachedResourceHandle<CachedRawResource> protect(this); - CachedRawResourceClient* client = static_cast<CachedRawResourceClient*>(c); + CachedResourceHandle<CachedRawResource> protectedThis(this); + CachedRawResourceClient& client = static_cast<CachedRawResourceClient&>(c); size_t redirectCount = m_redirectChain.size(); for (size_t i = 0; i < redirectCount; i++) { RedirectPair redirect = m_redirectChain[i]; ResourceRequest request(redirect.m_request); - client->redirectReceived(this, request, redirect.m_redirectResponse); + client.redirectReceived(*this, request, redirect.m_redirectResponse); if (!hasClient(c)) return; } ASSERT(redirectCount == m_redirectChain.size()); - if (!m_response.isNull()) - client->responseReceived(this, m_response); + if (!m_response.isNull()) { + ResourceResponse response(m_response); + if (validationCompleting()) + response.setSource(ResourceResponse::Source::MemoryCacheAfterValidation); + else { + ASSERT(!validationInProgress()); + response.setSource(ResourceResponse::Source::MemoryCache); + } + client.responseReceived(*this, response); + } if (!hasClient(c)) return; if (m_data) - client->dataReceived(this, m_data->data(), m_data->size()); + client.dataReceived(*this, m_data->data(), m_data->size()); if (!hasClient(c)) return; CachedResource::didAddClient(client); @@ -146,34 +164,51 @@ void CachedRawResource::allClientsRemoved() m_loader->cancelIfNotFinishing(); } -void CachedRawResource::willSendRequest(ResourceRequest& request, const ResourceResponse& response) +void CachedRawResource::redirectReceived(ResourceRequest& request, const ResourceResponse& response) { - CachedResourceHandle<CachedRawResource> protect(this); + CachedResourceHandle<CachedRawResource> protectedThis(this); if (!response.isNull()) { CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); while (CachedRawResourceClient* c = w.next()) - c->redirectReceived(this, request, response); + c->redirectReceived(*this, request, response); m_redirectChain.append(RedirectPair(request, response)); } - CachedResource::willSendRequest(request, response); + CachedResource::redirectReceived(request, response); } void CachedRawResource::responseReceived(const ResourceResponse& response) { - CachedResourceHandle<CachedRawResource> protect(this); + CachedResourceHandle<CachedRawResource> protectedThis(this); if (!m_identifier) m_identifier = m_loader->identifier(); CachedResource::responseReceived(response); CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); while (CachedRawResourceClient* c = w.next()) - c->responseReceived(this, m_response); + c->responseReceived(*this, m_response); +} + +bool CachedRawResource::shouldCacheResponse(const ResourceResponse& response) +{ + CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); + while (CachedRawResourceClient* c = w.next()) { + if (!c->shouldCacheResponse(*this, response)) + return false; + } + return true; } void CachedRawResource::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); while (CachedRawResourceClient* c = w.next()) - c->dataSent(this, bytesSent, totalBytesToBeSent); + c->dataSent(*this, bytesSent, totalBytesToBeSent); +} + +void CachedRawResource::finishedTimingForWorkerLoad(ResourceTiming&& resourceTiming) +{ + CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); + while (CachedRawResourceClient* c = w.next()) + c->finishedTimingForWorkerLoad(*this, resourceTiming); } void CachedRawResource::switchClientsToRevalidatedResource() @@ -181,7 +216,7 @@ void CachedRawResource::switchClientsToRevalidatedResource() ASSERT(m_loader); // If we're in the middle of a successful revalidation, responseReceived() hasn't been called, so we haven't set m_identifier. ASSERT(!m_identifier); - static_cast<CachedRawResource*>(resourceToRevalidate())->m_identifier = m_loader->identifier(); + downcast<CachedRawResource>(*resourceToRevalidate()).m_identifier = m_loader->identifier(); CachedResource::switchClientsToRevalidatedResource(); } @@ -196,25 +231,26 @@ void CachedRawResource::setDataBufferingPolicy(DataBufferingPolicy dataBuffering m_options.dataBufferingPolicy = dataBufferingPolicy; } -static bool shouldIgnoreHeaderForCacheReuse(AtomicString headerName) +static bool shouldIgnoreHeaderForCacheReuse(HTTPHeaderName name) { + switch (name) { // FIXME: This list of headers that don't affect cache policy almost certainly isn't complete. - DEFINE_STATIC_LOCAL(HashSet<AtomicString>, m_headers, ()); - if (m_headers.isEmpty()) { - m_headers.add("Accept"); - m_headers.add("Cache-Control"); - m_headers.add("Origin"); - m_headers.add("Pragma"); - m_headers.add("Purpose"); - m_headers.add("Referer"); - m_headers.add("User-Agent"); + case HTTPHeaderName::Accept: + case HTTPHeaderName::CacheControl: + case HTTPHeaderName::Pragma: + case HTTPHeaderName::Purpose: + case HTTPHeaderName::Referer: + case HTTPHeaderName::UserAgent: + return true; + + default: + return false; } - return m_headers.contains(headerName); } bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const { - if (m_options.dataBufferingPolicy == DoNotBufferData) + if (dataBufferingPolicy() == DoNotBufferData) return false; if (m_resourceRequest.httpMethod() != newRequest.httpMethod()) @@ -226,6 +262,9 @@ bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const if (m_resourceRequest.allowCookies() != newRequest.allowCookies()) return false; + if (newRequest.isConditional()) + return false; + // Ensure most headers match the existing headers before continuing. // Note that the list of ignored headers includes some headers explicitly related to caching. // A more detailed check of caching policy will be performed later, this is simply a list of @@ -233,22 +272,23 @@ bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const const HTTPHeaderMap& newHeaders = newRequest.httpHeaderFields(); const HTTPHeaderMap& oldHeaders = m_resourceRequest.httpHeaderFields(); - HTTPHeaderMap::const_iterator end = newHeaders.end(); - for (HTTPHeaderMap::const_iterator i = newHeaders.begin(); i != end; ++i) { - AtomicString headerName = i->key; - if (!shouldIgnoreHeaderForCacheReuse(headerName) && i->value != oldHeaders.get(headerName)) - return false; - } - - end = oldHeaders.end(); - for (HTTPHeaderMap::const_iterator i = oldHeaders.begin(); i != end; ++i) { - AtomicString headerName = i->key; - if (!shouldIgnoreHeaderForCacheReuse(headerName) && i->value != newHeaders.get(headerName)) + for (const auto& header : newHeaders) { + if (header.keyAsHTTPHeaderName) { + if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value()) + && header.value != oldHeaders.commonHeaders().get(header.keyAsHTTPHeaderName.value())) + return false; + } else if (header.value != oldHeaders.uncommonHeaders().get(header.key)) return false; } - for (size_t i = 0; i < m_redirectChain.size(); i++) { - if (m_redirectChain[i].m_redirectResponse.cacheControlContainsNoStore()) + // For this second loop, we don't actually need to compare values, checking that the + // key is contained in newHeaders is sufficient due to the previous loop. + for (const auto& header : oldHeaders) { + if (header.keyAsHTTPHeaderName) { + if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value()) + && !newHeaders.commonHeaders().contains(header.keyAsHTTPHeaderName.value())) + return false; + } else if (!newHeaders.uncommonHeaders().contains(header.key)) return false; } @@ -257,7 +297,7 @@ bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const void CachedRawResource::clear() { - m_data.clear(); + m_data = nullptr; setEncodedSize(0); if (m_loader) m_loader->clearResourceData(); diff --git a/Source/WebCore/loader/cache/CachedRawResource.h b/Source/WebCore/loader/cache/CachedRawResource.h index 48e04ffec..7d7468c0e 100644 --- a/Source/WebCore/loader/cache/CachedRawResource.h +++ b/Source/WebCore/loader/cache/CachedRawResource.h @@ -20,19 +20,19 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedRawResource_h -#define CachedRawResource_h +#pragma once #include "CachedResource.h" namespace WebCore { class CachedResourceClient; +class ResourceTiming; class SubresourceLoader; class CachedRawResource final : public CachedResource { public: - CachedRawResource(ResourceRequest&, Type); + CachedRawResource(CachedResourceRequest&&, Type, SessionID); // FIXME: AssociatedURLLoader shouldn't be a DocumentThreadableLoader and therefore shouldn't // use CachedRawResource. However, it is, and it needs to be able to defer loading. @@ -40,38 +40,44 @@ public: virtual void setDefersLoading(bool); virtual void setDataBufferingPolicy(DataBufferingPolicy); - - // FIXME: This is exposed for the InpsectorInstrumentation for preflights in DocumentThreadableLoader. It's also really lame. + + // FIXME: This is exposed for the InspectorInstrumentation for preflights in DocumentThreadableLoader. It's also really lame. unsigned long identifier() const { return m_identifier; } void clear(); -private: - virtual void didAddClient(CachedResourceClient*) override; - virtual void addDataBuffer(ResourceBuffer*) override; - virtual void addData(const char* data, unsigned length) override; - virtual void finishLoading(ResourceBuffer*) override; + bool canReuse(const ResourceRequest&) const; + + bool wasRedirected() const { return !m_redirectChain.isEmpty(); }; - virtual bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; } - virtual void allClientsRemoved() override; + void finishedTimingForWorkerLoad(ResourceTiming&&); + +private: + void didAddClient(CachedResourceClient&) final; + void addDataBuffer(SharedBuffer&) final; + void addData(const char* data, unsigned length) final; + void finishLoading(SharedBuffer*) final; - virtual void willSendRequest(ResourceRequest&, const ResourceResponse&) override; - virtual void responseReceived(const ResourceResponse&) override; - virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; } + void allClientsRemoved() override; - virtual void switchClientsToRevalidatedResource() override; - virtual bool mayTryReplaceEncodedData() const override { return true; } + void redirectReceived(ResourceRequest&, const ResourceResponse&) override; + void responseReceived(const ResourceResponse&) override; + bool shouldCacheResponse(const ResourceResponse&) override; + void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; - virtual bool canReuse(const ResourceRequest&) const override; + void switchClientsToRevalidatedResource() override; + bool mayTryReplaceEncodedData() const override { return m_allowEncodedDataReplacement; } - const char* calculateIncrementalDataChunk(ResourceBuffer*, unsigned& incrementalDataLength); + const char* calculateIncrementalDataChunk(SharedBuffer*, unsigned& incrementalDataLength); void notifyClientsDataWasReceived(const char* data, unsigned length); #if USE(SOUP) - virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize); + char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override; #endif unsigned long m_identifier; + bool m_allowEncodedDataReplacement; struct RedirectPair { public: @@ -88,6 +94,8 @@ private: Vector<RedirectPair> m_redirectChain; }; -} +} // namespace WebCore -#endif // CachedRawResource_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::CachedRawResource) + static bool isType(const WebCore::CachedResource& resource) { return resource.isMainOrMediaOrRawResource(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/loader/cache/CachedRawResourceClient.h b/Source/WebCore/loader/cache/CachedRawResourceClient.h index 3d7fd5e5c..2e9fac591 100644 --- a/Source/WebCore/loader/cache/CachedRawResourceClient.h +++ b/Source/WebCore/loader/cache/CachedRawResourceClient.h @@ -20,8 +20,7 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedRawResourceClient_h -#define CachedRawResourceClient_h +#pragma once #include "CachedResourceClient.h" @@ -30,22 +29,23 @@ namespace WebCore { class CachedResource; class ResourceRequest; class ResourceResponse; +class ResourceTiming; class CachedRawResourceClient : public CachedResourceClient { public: virtual ~CachedRawResourceClient() { } static CachedResourceClientType expectedType() { return RawResourceType; } - virtual CachedResourceClientType resourceClientType() const override { return expectedType(); } - - virtual void dataSent(CachedResource*, unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { } - virtual void responseReceived(CachedResource*, const ResourceResponse&) { } - virtual void dataReceived(CachedResource*, const char* /* data */, int /* length */) { } - virtual void redirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&) { } + CachedResourceClientType resourceClientType() const override { return expectedType(); } + + virtual void dataSent(CachedResource&, unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { } + virtual void responseReceived(CachedResource&, const ResourceResponse&) { } + virtual bool shouldCacheResponse(CachedResource&, const ResourceResponse&) { return true; } + virtual void dataReceived(CachedResource&, const char* /* data */, int /* length */) { } + virtual void redirectReceived(CachedResource&, ResourceRequest&, const ResourceResponse&) { } + virtual void finishedTimingForWorkerLoad(CachedResource&, const ResourceTiming&) { } #if USE(SOUP) - virtual char* getOrCreateReadBuffer(CachedResource*, size_t /* requestedSize */, size_t& /* actualSize */) { return 0; } + virtual char* getOrCreateReadBuffer(CachedResource&, size_t /* requestedSize */, size_t& /* actualSize */) { return nullptr; } #endif }; } - -#endif // CachedRawResourceClient_h diff --git a/Source/WebCore/loader/cache/CachedResource.cpp b/Source/WebCore/loader/cache/CachedResource.cpp index 5f0bc76fa..8b09d3285 100644 --- a/Source/WebCore/loader/cache/CachedResource.cpp +++ b/Source/WebCore/loader/cache/CachedResource.cpp @@ -3,7 +3,7 @@ Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + Copyright (C) 2004-2011, 2014 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -29,23 +29,23 @@ #include "CachedResourceHandle.h" #include "CachedResourceLoader.h" #include "CrossOriginAccessControl.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" #include "Document.h" #include "DocumentLoader.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" +#include "HTTPHeaderNames.h" #include "InspectorInstrumentation.h" #include "URL.h" #include "LoaderStrategy.h" #include "Logging.h" +#include "MainFrame.h" #include "MemoryCache.h" #include "PlatformStrategies.h" -#include "PurgeableBuffer.h" -#include "ResourceBuffer.h" #include "ResourceHandle.h" -#include "ResourceLoadScheduler.h" #include "SchemeRegistry.h" #include "SecurityOrigin.h" -#include "SecurityPolicy.h" #include "SubresourceLoader.h" #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> @@ -60,139 +60,99 @@ using namespace WTF; -namespace WebCore { +#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(cachedResourceLoader.isAlwaysOnLoggingAllowed(), Network, "%p - CachedResource::" fmt, this, ##__VA_ARGS__) -// These response headers are not copied from a revalidated response to the -// cached response headers. For compatibility, this list is based on Chromium's -// net/http/http_response_headers.cc. -const char* const headersToIgnoreAfterRevalidation[] = { - "allow", - "connection", - "etag", - "expires", - "keep-alive", - "last-modified" - "proxy-authenticate", - "proxy-connection", - "trailer", - "transfer-encoding", - "upgrade", - "www-authenticate", - "x-frame-options", - "x-xss-protection", -}; - -// Some header prefixes mean "Don't copy this header from a 304 response.". -// Rather than listing all the relevant headers, we can consolidate them into -// this list, also grabbed from Chromium's net/http/http_response_headers.cc. -const char* const headerPrefixesToIgnoreAfterRevalidation[] = { - "content-", - "x-content-", - "x-webkit-" -}; - -static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header) -{ - for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) { - if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i])) - return false; - } - for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { - if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false)) - return false; - } - return true; -} +namespace WebCore { -static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type) +ResourceLoadPriority CachedResource::defaultPriorityForResourceType(Type type) { switch (type) { case CachedResource::MainResource: - return ResourceLoadPriorityVeryHigh; + return ResourceLoadPriority::VeryHigh; case CachedResource::CSSStyleSheet: - return ResourceLoadPriorityHigh; case CachedResource::Script: + return ResourceLoadPriority::High; +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif + case CachedResource::MediaResource: case CachedResource::FontResource: case CachedResource::RawResource: - return ResourceLoadPriorityMedium; + return ResourceLoadPriority::Medium; case CachedResource::ImageResource: - return ResourceLoadPriorityLow; + return ResourceLoadPriority::Low; #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: - return ResourceLoadPriorityHigh; + return ResourceLoadPriority::High; #endif -#if ENABLE(SVG) case CachedResource::SVGDocumentResource: - return ResourceLoadPriorityLow; -#endif + return ResourceLoadPriority::Low; #if ENABLE(LINK_PREFETCH) case CachedResource::LinkPrefetch: - return ResourceLoadPriorityVeryLow; + return ResourceLoadPriority::VeryLow; case CachedResource::LinkSubresource: - return ResourceLoadPriorityVeryLow; + return ResourceLoadPriority::VeryLow; #endif #if ENABLE(VIDEO_TRACK) case CachedResource::TextTrackResource: - return ResourceLoadPriorityLow; + return ResourceLoadPriority::Low; #endif } ASSERT_NOT_REACHED(); - return ResourceLoadPriorityLow; + return ResourceLoadPriority::Low; } -static double deadDecodedDataDeletionIntervalForResourceType(CachedResource::Type type) +static std::chrono::milliseconds deadDecodedDataDeletionIntervalForResourceType(CachedResource::Type type) { if (type == CachedResource::Script) - return 0; - return memoryCache()->deadDecodedDataDeletionInterval(); + return std::chrono::milliseconds { 0 }; + + return MemoryCache::singleton().deadDecodedDataDeletionInterval(); } DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource")); -CachedResource::CachedResource(const ResourceRequest& request, Type type) - : m_resourceRequest(request) +CachedResource::CachedResource(CachedResourceRequest&& request, Type type, SessionID sessionID) + : m_resourceRequest(request.releaseResourceRequest()) + , m_options(request.options()) + , m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type)) + , m_sessionID(sessionID) , m_loadPriority(defaultPriorityForResourceType(type)) - , m_responseTimestamp(currentTime()) - , m_decodedDataDeletionTimer(this, &CachedResource::decodedDataDeletionTimerFired, deadDecodedDataDeletionIntervalForResourceType(type)) - , m_lastDecodedAccessTime(0) - , m_loadFinishTime(0) - , m_encodedSize(0) - , m_decodedSize(0) - , m_accessCount(0) - , m_handleCount(0) - , m_preloadCount(0) - , m_preloadResult(PreloadNotReferenced) - , m_inLiveDecodedResourcesList(false) - , m_requestedFromNetworkingLayer(false) - , m_inCache(false) - , m_loading(false) - , m_switchingClientsToRevalidatedResource(false) + , m_responseTimestamp(std::chrono::system_clock::now()) + , m_fragmentIdentifierForRequest(request.releaseFragmentIdentifier()) + , m_origin(request.releaseOrigin()) + , m_initiatorName(request.initiatorName()) + , m_isLinkPreload(request.isLinkPreload()) , m_type(type) - , m_status(Pending) +{ + ASSERT(sessionID.isValid()); + + setLoadPriority(request.priority()); #ifndef NDEBUG - , m_deleted(false) - , m_lruIndex(0) + cachedResourceLeakCounter.increment(); #endif - , m_nextInAllResourcesList(0) - , m_prevInAllResourcesList(0) - , m_nextInLiveResourcesList(0) - , m_prevInLiveResourcesList(0) - , m_owningCachedResourceLoader(0) - , m_resourceToRevalidate(0) - , m_proxyResource(0) -{ - ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum. + + // FIXME: We should have a better way of checking for Navigation loads, maybe FetchMode::Options::Navigate. + ASSERT(m_origin || m_type == CachedResource::MainResource); + + if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options)) + setCrossOrigin(); +} + +// FIXME: For this constructor, we should probably mandate that the URL has no fragment identifier. +CachedResource::CachedResource(const URL& url, Type type, SessionID sessionID) + : m_resourceRequest(url) + , m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type)) + , m_sessionID(sessionID) + , m_responseTimestamp(std::chrono::system_clock::now()) + , m_fragmentIdentifierForRequest(CachedResourceRequest::splitFragmentIdentifierFromRequestURL(m_resourceRequest)) + , m_type(type) + , m_status(Cached) +{ + ASSERT(sessionID.isValid()); #ifndef NDEBUG cachedResourceLeakCounter.increment(); #endif - - if (!m_resourceRequest.url().hasFragmentIdentifier()) - return; - URL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url()); - if (urlForCache.hasFragmentIdentifier()) - return; - m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier(); - m_resourceRequest.setURL(urlForCache); } CachedResource::~CachedResource() @@ -201,7 +161,7 @@ CachedResource::~CachedResource() ASSERT(canDelete()); ASSERT(!inCache()); ASSERT(!m_deleted); - ASSERT(url().isNull() || memoryCache()->resourceForRequest(resourceRequest()) != this); + ASSERT(url().isNull() || !allowsCaching() || MemoryCache::singleton().resourceForRequest(resourceRequest(), sessionID()) != this); #ifndef NDEBUG m_deleted = true; @@ -209,99 +169,81 @@ CachedResource::~CachedResource() #endif if (m_owningCachedResourceLoader) - m_owningCachedResourceLoader->removeCachedResource(this); + m_owningCachedResourceLoader->removeCachedResource(*this); } void CachedResource::failBeforeStarting() { // FIXME: What if resources in other frames were waiting for this revalidation? LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data()); - if (m_resourceToRevalidate) - memoryCache()->revalidationFailed(this); + if (allowsCaching() && m_resourceToRevalidate) + MemoryCache::singleton().revalidationFailed(*this); error(CachedResource::LoadError); } -void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader* cachedResourceLoader) +void CachedResource::load(CachedResourceLoader& cachedResourceLoader) { - // Note: We skip the Content-Security-Policy check here because we check - // the Content-Security-Policy at the CachedResourceLoader layer so we can - // handle different resource types differently. - - FrameLoader& frameLoader = cachedResourceLoader->frame()->loader(); - String outgoingReferrer; - String outgoingOrigin; - if (m_resourceRequest.httpReferrer().isNull()) { - outgoingReferrer = frameLoader.outgoingReferrer(); - outgoingOrigin = frameLoader.outgoingOrigin(); - } else { - outgoingReferrer = m_resourceRequest.httpReferrer(); - outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString(); - } - - outgoingReferrer = SecurityPolicy::generateReferrerHeader(cachedResourceLoader->document()->referrerPolicy(), m_resourceRequest.url(), outgoingReferrer); - if (outgoingReferrer.isEmpty()) - m_resourceRequest.clearHTTPReferrer(); - else if (!m_resourceRequest.httpReferrer()) - m_resourceRequest.setHTTPReferrer(outgoingReferrer); - FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin); - - frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest); -} - -void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options) -{ - if (!cachedResourceLoader->frame()) { + if (!cachedResourceLoader.frame()) { + RELEASE_LOG_IF_ALLOWED("load: No associated frame"); failBeforeStarting(); return; } + Frame& frame = *cachedResourceLoader.frame(); + + // Prevent new loads if we are in the PageCache or being added to the PageCache. + // We query the top document because new frames may be created in pagehide event handlers + // and their pageCacheState will not reflect the fact that they are about to enter page + // cache. + if (auto* topDocument = frame.mainFrame().document()) { + if (topDocument->pageCacheState() != Document::NotInPageCache) { + RELEASE_LOG_IF_ALLOWED("load: Already in page cache or being added to it (frame = %p)", &frame); + failBeforeStarting(); + return; + } + } - FrameLoader& frameLoader = cachedResourceLoader->frame()->loader(); - if (options.securityCheck == DoSecurityCheck && (frameLoader.state() == FrameStateProvisional || !frameLoader.activeDocumentLoader() || frameLoader.activeDocumentLoader()->isStopping())) { + FrameLoader& frameLoader = frame.loader(); + if (m_options.securityCheck == DoSecurityCheck && (frameLoader.state() == FrameStateProvisional || !frameLoader.activeDocumentLoader() || frameLoader.activeDocumentLoader()->isStopping())) { + if (frameLoader.state() == FrameStateProvisional) + RELEASE_LOG_IF_ALLOWED("load: Failed security check -- state is provisional (frame = %p)", &frame); + else if (!frameLoader.activeDocumentLoader()) + RELEASE_LOG_IF_ALLOWED("load: Failed security check -- not active document (frame = %p)", &frame); + else if (frameLoader.activeDocumentLoader()->isStopping()) + RELEASE_LOG_IF_ALLOWED("load: Failed security check -- active loader is stopping (frame = %p)", &frame); failBeforeStarting(); return; } - m_options = options; m_loading = true; -#if USE(QUICK_LOOK) - if (!m_resourceRequest.isNull() && m_resourceRequest.url().protocolIs(QLPreviewProtocol())) { - // When QuickLook is invoked to convert a document, it returns a unique URL in the - // NSURLReponse for the main document. To make safeQLURLForDocumentURLAndResourceURL() - // work, we need to use the QL URL not the original URL. - const URL& documentURL = cachedResourceLoader->frame() ? cachedResourceLoader->frame()->loader().documentLoader()->response().url() : cachedResourceLoader->document()->url(); - m_resourceRequest.setURL(safeQLURLForDocumentURLAndResourceURL(documentURL, url())); - } -#endif - - if (!accept().isEmpty()) - m_resourceRequest.setHTTPAccept(accept()); - if (isCacheValidator()) { CachedResource* resourceToRevalidate = m_resourceToRevalidate; ASSERT(resourceToRevalidate->canUseCacheValidator()); ASSERT(resourceToRevalidate->isLoaded()); - const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified"); - const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag"); + const String& lastModified = resourceToRevalidate->response().httpHeaderField(HTTPHeaderName::LastModified); + const String& eTag = resourceToRevalidate->response().httpHeaderField(HTTPHeaderName::ETag); if (!lastModified.isEmpty() || !eTag.isEmpty()) { - ASSERT(cachedResourceLoader->cachePolicy(type()) != CachePolicyReload); - if (cachedResourceLoader->cachePolicy(type()) == CachePolicyRevalidate) - m_resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); + ASSERT(cachedResourceLoader.cachePolicy(type()) != CachePolicyReload); + if (cachedResourceLoader.cachePolicy(type()) == CachePolicyRevalidate) + m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"); if (!lastModified.isEmpty()) - m_resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); + m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified); if (!eTag.isEmpty()) - m_resourceRequest.setHTTPHeaderField("If-None-Match", eTag); + m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag); } } #if ENABLE(LINK_PREFETCH) if (type() == CachedResource::LinkPrefetch || type() == CachedResource::LinkSubresource) - m_resourceRequest.setHTTPHeaderField("Purpose", "prefetch"); + m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Purpose, "prefetch"); #endif m_resourceRequest.setPriority(loadPriority()); - if (type() != MainResource) - addAdditionalRequestHeaders(cachedResourceLoader); + // Navigation algorithm is setting up the request before sending it to CachedResourceLoader?CachedResource. + // So no need for extra fields for MainResource. + if (type() != CachedResource::MainResource) + frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest); + // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers. // We should look into removing the expectation of that knowledge from the platform network stacks. @@ -313,8 +255,9 @@ void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const Reso m_fragmentIdentifierForRequest = String(); } - m_loader = platformStrategies()->loaderStrategy()->resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, request, request.priority(), options); + m_loader = platformStrategies()->loaderStrategy()->loadResource(frame, *this, request, m_options); if (!m_loader) { + RELEASE_LOG_IF_ALLOWED("load: Unable to create SubresourceLoader (frame = %p)", &frame); failBeforeStarting(); return; } @@ -322,27 +265,55 @@ void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const Reso m_status = Pending; } +void CachedResource::loadFrom(const CachedResource& resource) +{ + ASSERT(url() == resource.url()); + ASSERT(type() == resource.type()); + ASSERT(resource.status() == Status::Cached); + + if (isCrossOrigin() && m_options.mode == FetchOptions::Mode::Cors) { + ASSERT(m_origin); + String errorMessage; + if (!WebCore::passesAccessControlCheck(resource.response(), m_options.allowCredentials, *m_origin, errorMessage)) { + setResourceError(ResourceError(String(), 0, url(), errorMessage, ResourceError::Type::AccessControl)); + return; + } + } + + setBodyDataFrom(resource); + setStatus(Status::Cached); + setLoading(false); +} + +void CachedResource::setBodyDataFrom(const CachedResource& resource) +{ + m_data = resource.m_data; + m_response = resource.m_response; + setDecodedSize(resource.decodedSize()); + setEncodedSize(resource.encodedSize()); +} + void CachedResource::checkNotify() { - if (isLoading()) + if (isLoading() || stillNeedsLoad()) return; - CachedResourceClientWalker<CachedResourceClient> w(m_clients); - while (CachedResourceClient* c = w.next()) - c->notifyFinished(this); + CachedResourceClientWalker<CachedResourceClient> walker(m_clients); + while (CachedResourceClient* client = walker.next()) + client->notifyFinished(*this); } -void CachedResource::addDataBuffer(ResourceBuffer*) +void CachedResource::addDataBuffer(SharedBuffer&) { - ASSERT(m_options.dataBufferingPolicy == BufferData); + ASSERT(dataBufferingPolicy() == BufferData); } void CachedResource::addData(const char*, unsigned) { - ASSERT(m_options.dataBufferingPolicy == DoNotBufferData); + ASSERT(dataBufferingPolicy() == DoNotBufferData); } -void CachedResource::finishLoading(ResourceBuffer*) +void CachedResource::finishLoading(SharedBuffer*) { setLoading(false); checkNotify(); @@ -352,7 +323,7 @@ void CachedResource::error(CachedResource::Status status) { setStatus(status); ASSERT(errorOccurred()); - m_data.clear(); + m_data = nullptr; setLoading(false); checkNotify(); @@ -360,7 +331,7 @@ void CachedResource::error(CachedResource::Status status) void CachedResource::cancelLoad() { - if (!isLoading()) + if (!isLoading() && !stillNeedsLoad()) return; setStatus(LoadError); @@ -374,10 +345,30 @@ void CachedResource::finish() m_status = Cached; } -bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin) +void CachedResource::setCrossOrigin() { - String errorDescription; - return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription); + ASSERT(m_options.mode != FetchOptions::Mode::SameOrigin); + m_responseTainting = (m_options.mode == FetchOptions::Mode::Cors) ? ResourceResponse::Tainting::Cors : ResourceResponse::Tainting::Opaque; +} + +bool CachedResource::isCrossOrigin() const +{ + return m_responseTainting != ResourceResponse::Tainting::Basic; +} + +bool CachedResource::isCORSSameOrigin() const +{ + // Following resource types do not use CORS + ASSERT(type() != CachedResource::Type::FontResource); +#if ENABLE(SVG_FONTS) + ASSERT(type() != CachedResource::Type::SVGFontResource); +#endif +#if ENABLE(XSLT) + ASSERT(type() != CachedResource::XSLStyleSheet); +#endif + + // https://html.spec.whatwg.org/multipage/infrastructure.html#cors-same-origin + return !loadFailedOrCanceled() && m_responseTainting != ResourceResponse::Tainting::Opaque; } bool CachedResource::isExpired() const @@ -385,53 +376,62 @@ bool CachedResource::isExpired() const if (m_response.isNull()) return false; - return currentAge() > freshnessLifetime(); + return computeCurrentAge(m_response, m_responseTimestamp) > freshnessLifetime(m_response); } -double CachedResource::currentAge() const +static inline bool shouldCacheSchemeIndefinitely(StringView scheme) { - // RFC2616 13.2.3 - // No compensation for latency as that is not terribly important in practice - double dateValue = m_response.date(); - double apparentAge = std::isfinite(dateValue) ? std::max(0., m_responseTimestamp - dateValue) : 0; - double ageValue = m_response.age(); - double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge; - double residentTime = currentTime() - m_responseTimestamp; - return correctedReceivedAge + residentTime; +#if PLATFORM(COCOA) + if (equalLettersIgnoringASCIICase(scheme, "applewebdata")) + return true; +#endif +#if USE(SOUP) + if (equalLettersIgnoringASCIICase(scheme, "resource")) + return true; +#endif + return equalLettersIgnoringASCIICase(scheme, "data"); } -double CachedResource::freshnessLifetime() const +std::chrono::microseconds CachedResource::freshnessLifetime(const ResourceResponse& response) const { - if (!m_response.url().protocolIsInHTTPFamily()) { - // Don't cache non-HTTP main resources since we can't check for freshness. - // FIXME: We should not cache subresources either, but when we tried this - // it caused performance and flakiness issues in our test infrastructure. - if (m_type == MainResource && !SchemeRegistry::shouldCacheResponsesFromURLSchemeIndefinitely(m_response.url().protocol())) - return 0; + if (!response.url().protocolIsInHTTPFamily()) { + StringView protocol = response.url().protocol(); + if (!shouldCacheSchemeIndefinitely(protocol)) { + // Don't cache non-HTTP main resources since we can't check for freshness. + // FIXME: We should not cache subresources either, but when we tried this + // it caused performance and flakiness issues in our test infrastructure. + if (m_type == MainResource || SchemeRegistry::shouldAlwaysRevalidateURLScheme(protocol.toStringWithoutCopying())) + return 0us; + } - return std::numeric_limits<double>::max(); + return std::chrono::microseconds::max(); } - // RFC2616 13.2.4 - double maxAgeValue = m_response.cacheControlMaxAge(); - if (std::isfinite(maxAgeValue)) - return maxAgeValue; - double expiresValue = m_response.expires(); - double dateValue = m_response.date(); - double creationTime = std::isfinite(dateValue) ? dateValue : m_responseTimestamp; - if (std::isfinite(expiresValue)) - return expiresValue - creationTime; - double lastModifiedValue = m_response.lastModified(); - if (std::isfinite(lastModifiedValue)) - return (creationTime - lastModifiedValue) * 0.1; - // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0. - return 0; + return computeFreshnessLifetimeForHTTPFamily(response, m_responseTimestamp); +} + +void CachedResource::redirectReceived(ResourceRequest&, const ResourceResponse& response) +{ + m_requestedFromNetworkingLayer = true; + if (response.isNull()) + return; + + updateRedirectChainStatus(m_redirectChainCacheStatus, response); +} + +void CachedResource::setResponse(const ResourceResponse& response) +{ + ASSERT(m_response.type() == ResourceResponse::Type::Default); + m_response = response; + m_response.setRedirected(m_redirectChainCacheStatus.status != RedirectChainCacheStatus::NoRedirection); + + m_varyingHeaderValues = collectVaryingRequestHeaders(m_resourceRequest, m_response, m_sessionID); } void CachedResource::responseReceived(const ResourceResponse& response) { setResponse(response); - m_responseTimestamp = currentTime(); + m_responseTimestamp = std::chrono::system_clock::now(); String encoding = response.textEncodingName(); if (!encoding.isNull()) setEncoding(encoding); @@ -440,32 +440,30 @@ void CachedResource::responseReceived(const ResourceResponse& response) void CachedResource::clearLoader() { ASSERT(m_loader); - m_loader = 0; + m_identifierForLoadWithoutResourceLoader = m_loader->identifier(); + m_loader = nullptr; + deleteIfPossible(); } -void CachedResource::addClient(CachedResourceClient* client) +void CachedResource::addClient(CachedResourceClient& client) { if (addClientToSet(client)) didAddClient(client); } -void CachedResource::didAddClient(CachedResourceClient* c) +void CachedResource::didAddClient(CachedResourceClient& client) { if (m_decodedDataDeletionTimer.isActive()) m_decodedDataDeletionTimer.stop(); - if (m_clientsAwaitingCallback.contains(c)) { - m_clients.add(c); - m_clientsAwaitingCallback.remove(c); - } + if (m_clientsAwaitingCallback.remove(&client)) + m_clients.add(&client); if (!isLoading() && !stillNeedsLoad()) - c->notifyFinished(this); + client.notifyFinished(*this); } -bool CachedResource::addClientToSet(CachedResourceClient* client) +bool CachedResource::addClientToSet(CachedResourceClient& client) { - ASSERT(!isPurgeable()); - if (m_preloadResult == PreloadNotReferenced) { if (isLoaded()) m_preloadResult = PreloadReferencedWhileComplete; @@ -474,78 +472,90 @@ bool CachedResource::addClientToSet(CachedResourceClient* client) else m_preloadResult = PreloadReferenced; } - if (!hasClients() && inCache()) - memoryCache()->addToLiveResourcesSize(this); + if (allowsCaching() && !hasClients() && inCache()) + MemoryCache::singleton().addToLiveResourcesSize(*this); if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) { // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns // synchronously (e.g., scripts may not have set all the state they need to handle the load). // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources, // we schedule the callbacks and ensure we never finish synchronously. - ASSERT(!m_clientsAwaitingCallback.contains(client)); - m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client)); + ASSERT(!m_clientsAwaitingCallback.contains(&client)); + m_clientsAwaitingCallback.add(&client, std::make_unique<Callback>(*this, client)); return false; } - m_clients.add(client); + m_clients.add(&client); return true; } -void CachedResource::removeClient(CachedResourceClient* client) +void CachedResource::removeClient(CachedResourceClient& client) { - OwnPtr<CachedResourceCallback> callback = m_clientsAwaitingCallback.take(client); + auto callback = m_clientsAwaitingCallback.take(&client); if (callback) { - ASSERT(!m_clients.contains(client)); + ASSERT(!m_clients.contains(&client)); callback->cancel(); - callback.clear(); + callback = nullptr; } else { - ASSERT(m_clients.contains(client)); - m_clients.remove(client); + ASSERT(m_clients.contains(&client)); + m_clients.remove(&client); didRemoveClient(client); } - bool deleted = deleteIfPossible(); - if (!deleted && !hasClients()) { - if (inCache()) { - memoryCache()->removeFromLiveResourcesSize(this); - memoryCache()->removeFromLiveDecodedResourcesList(this); - } - if (!m_switchingClientsToRevalidatedResource) - allClientsRemoved(); - destroyDecodedDataIfNeeded(); - if (response().cacheControlContainsNoStore()) { - // RFC2616 14.9.2: - // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible" - // "... History buffers MAY store such responses as part of their normal operation." - // We allow non-secure content to be reused in history, but we do not allow secure content to be reused. - if (url().protocolIs("https")) - memoryCache()->remove(this); - } else - memoryCache()->prune(); + if (deleteIfPossible()) { + // `this` object is dead here. + return; } - // This object may be dead here. + + if (hasClients()) + return; + + auto& memoryCache = MemoryCache::singleton(); + if (allowsCaching() && inCache()) { + memoryCache.removeFromLiveResourcesSize(*this); + memoryCache.removeFromLiveDecodedResourcesList(*this); + } + if (!m_switchingClientsToRevalidatedResource) + allClientsRemoved(); + destroyDecodedDataIfNeeded(); + + if (!allowsCaching()) + return; + + if (response().cacheControlContainsNoStore() && url().protocolIs("https")) { + // RFC2616 14.9.2: + // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible" + // "... History buffers MAY store such responses as part of their normal operation." + // We allow non-secure content to be reused in history, but we do not allow secure content to be reused. + memoryCache.remove(*this); + } + memoryCache.pruneSoon(); } void CachedResource::destroyDecodedDataIfNeeded() { if (!m_decodedSize) return; - if (!memoryCache()->deadDecodedDataDeletionInterval()) + if (!MemoryCache::singleton().deadDecodedDataDeletionInterval().count()) return; m_decodedDataDeletionTimer.restart(); } -void CachedResource::decodedDataDeletionTimerFired(DeferrableOneShotTimer<CachedResource>&) +void CachedResource::decodedDataDeletionTimerFired() { destroyDecodedData(); } bool CachedResource::deleteIfPossible() { - if (canDelete() && !inCache()) { - InspectorInstrumentation::willDestroyCachedResource(this); - delete this; - return true; + if (canDelete()) { + if (!inCache()) { + InspectorInstrumentation::willDestroyCachedResource(*this); + delete this; + return true; + } + if (m_data) + m_data->hintMemoryNotNeededSoon(); } return false; } @@ -555,19 +565,19 @@ void CachedResource::setDecodedSize(unsigned size) if (size == m_decodedSize) return; - int delta = size - m_decodedSize; + long long delta = static_cast<long long>(size) - m_decodedSize; + + // The object must be moved to a different queue, since its size has been changed. + // Remove before updating m_decodedSize, so we find the resource in the correct LRU list. + if (allowsCaching() && inCache()) + MemoryCache::singleton().removeFromLRUList(*this); - // The object must now be moved to a different queue, since its size has been changed. - // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous - // queue. - if (inCache()) - memoryCache()->removeFromLRUList(this); - m_decodedSize = size; - if (inCache()) { + if (allowsCaching() && inCache()) { + auto& memoryCache = MemoryCache::singleton(); // Now insert into the new LRU list. - memoryCache()->insertInLRUList(this); + memoryCache.insertInLRUList(*this); // Insert into or remove from the live decoded list if necessary. // When inserting into the LiveDecodedResourcesList it is possible @@ -576,13 +586,14 @@ void CachedResource::setDecodedSize(unsigned size) // violation of the invariant that the list is to be kept sorted // by access time. The weakening of the invariant does not pose // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209 - if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients()) - memoryCache()->insertInLiveDecodedResourcesList(this); - else if (!m_decodedSize && m_inLiveDecodedResourcesList) - memoryCache()->removeFromLiveDecodedResourcesList(this); + bool inLiveDecodedResourcesList = memoryCache.inLiveDecodedResourcesList(*this); + if (m_decodedSize && !inLiveDecodedResourcesList && hasClients()) + memoryCache.insertInLiveDecodedResourcesList(*this); + else if (!m_decodedSize && inLiveDecodedResourcesList) + memoryCache.removeFromLiveDecodedResourcesList(*this); // Update the cache's size totals. - memoryCache()->adjustSize(hasClients(), delta); + memoryCache.adjustSize(hasClients(), delta); } } @@ -591,25 +602,19 @@ void CachedResource::setEncodedSize(unsigned size) if (size == m_encodedSize) return; - // The size cannot ever shrink (unless it is being nulled out because of an error). If it ever does, assert. - ASSERT(size == 0 || size >= m_encodedSize); - - int delta = size - m_encodedSize; + long long delta = static_cast<long long>(size) - m_encodedSize; + + // The object must be moved to a different queue, since its size has been changed. + // Remove before updating m_encodedSize, so we find the resource in the correct LRU list. + if (allowsCaching() && inCache()) + MemoryCache::singleton().removeFromLRUList(*this); - // The object must now be moved to a different queue, since its size has been changed. - // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous - // queue. - if (inCache()) - memoryCache()->removeFromLRUList(this); - m_encodedSize = size; - - if (inCache()) { - // Now insert into the new LRU list. - memoryCache()->insertInLRUList(this); - - // Update the cache's size totals. - memoryCache()->adjustSize(hasClients(), delta); + + if (allowsCaching() && inCache()) { + auto& memoryCache = MemoryCache::singleton(); + memoryCache.insertInLRUList(*this); + memoryCache.adjustSize(hasClients(), delta); } } @@ -617,12 +622,13 @@ void CachedResource::didAccessDecodedData(double timeStamp) { m_lastDecodedAccessTime = timeStamp; - if (inCache()) { - if (m_inLiveDecodedResourcesList) { - memoryCache()->removeFromLiveDecodedResourcesList(this); - memoryCache()->insertInLiveDecodedResourcesList(this); + if (allowsCaching() && inCache()) { + auto& memoryCache = MemoryCache::singleton(); + if (memoryCache.inLiveDecodedResourcesList(*this)) { + memoryCache.removeFromLiveDecodedResourcesList(*this); + memoryCache.insertInLiveDecodedResourcesList(*this); } - memoryCache()->prune(); + memoryCache.pruneSoon(); } } @@ -633,31 +639,27 @@ void CachedResource::setResourceToRevalidate(CachedResource* resource) ASSERT(resource != this); ASSERT(m_handlesToRevalidate.isEmpty()); ASSERT(resource->type() == type()); + ASSERT(!resource->m_proxyResource); LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource); - // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances. - // https://bugs.webkit.org/show_bug.cgi?id=28604. - // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate. - ASSERT(!resource->m_proxyResource); - resource->m_proxyResource = this; m_resourceToRevalidate = resource; } void CachedResource::clearResourceToRevalidate() -{ +{ ASSERT(m_resourceToRevalidate); + ASSERT(m_resourceToRevalidate->m_proxyResource == this); + if (m_switchingClientsToRevalidatedResource) return; - // A resource may start revalidation before this method has been called, so check that this resource is still the proxy resource before clearing it out. - if (m_resourceToRevalidate->m_proxyResource == this) { - m_resourceToRevalidate->m_proxyResource = 0; - m_resourceToRevalidate->deleteIfPossible(); - } + m_resourceToRevalidate->m_proxyResource = nullptr; + m_resourceToRevalidate->deleteIfPossible(); + m_handlesToRevalidate.clear(); - m_resourceToRevalidate = 0; + m_resourceToRevalidate = nullptr; deleteIfPossible(); } @@ -670,9 +672,7 @@ void CachedResource::switchClientsToRevalidatedResource() LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate); m_switchingClientsToRevalidatedResource = true; - HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end(); - for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { - CachedResourceHandleBase* handle = *it; + for (auto& handle : m_handlesToRevalidate) { handle->m_resource = m_resourceToRevalidate; m_resourceToRevalidate->registerHandle(handle); --m_handleCount; @@ -681,52 +681,37 @@ void CachedResource::switchClientsToRevalidatedResource() m_handlesToRevalidate.clear(); Vector<CachedResourceClient*> clientsToMove; - HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end(); - for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) { - CachedResourceClient* client = it->key; - unsigned count = it->value; + for (auto& entry : m_clients) { + CachedResourceClient* client = entry.key; + unsigned count = entry.value; while (count) { clientsToMove.append(client); --count; } } - unsigned moveCount = clientsToMove.size(); - for (unsigned n = 0; n < moveCount; ++n) - removeClient(clientsToMove[n]); + for (auto& client : clientsToMove) + removeClient(*client); ASSERT(m_clients.isEmpty()); - for (unsigned n = 0; n < moveCount; ++n) - m_resourceToRevalidate->addClientToSet(clientsToMove[n]); - for (unsigned n = 0; n < moveCount; ++n) { + for (auto& client : clientsToMove) + m_resourceToRevalidate->addClientToSet(*client); + for (auto& client : clientsToMove) { // Calling didAddClient may do anything, including trying to cancel revalidation. // Assert that it didn't succeed. ASSERT(m_resourceToRevalidate); // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore. - if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n])) - m_resourceToRevalidate->didAddClient(clientsToMove[n]); + if (m_resourceToRevalidate->m_clients.contains(client)) + m_resourceToRevalidate->didAddClient(*client); } m_switchingClientsToRevalidatedResource = false; } void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse) { - m_responseTimestamp = currentTime(); - - // RFC2616 10.3.5 - // Update cached headers from the 304 response - const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields(); - HTTPHeaderMap::const_iterator end = newHeaders.end(); - for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) { - // Entity headers should not be sent by servers when generating a 304 - // response; misconfigured servers send them anyway. We shouldn't allow - // such headers to update the original request. We'll base this on the - // list defined by RFC2616 7.1, with a few additions for extension headers - // we care about. - if (!shouldUpdateHeaderAfterRevalidation(it->key)) - continue; - m_response.setHTTPHeaderField(it->key, it->value); - } + m_responseTimestamp = std::chrono::system_clock::now(); + + updateResponseHeadersAfterRevalidation(m_response, validatingResponse); } void CachedResource::registerHandle(CachedResourceHandleBase* h) @@ -758,95 +743,50 @@ bool CachedResource::canUseCacheValidator() const return m_response.hasCacheValidatorFields(); } -bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const +CachedResource::RevalidationDecision CachedResource::makeRevalidationDecision(CachePolicy cachePolicy) const { - ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify); - - if (cachePolicy == CachePolicyRevalidate) - return true; - - if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) { - LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this); - return true; - } - - if (cachePolicy == CachePolicyCache) { - if (m_response.cacheControlContainsMustRevalidate() && isExpired()) { - LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this); - return true; - } - return false; - } - - // CachePolicyVerify - if (isExpired()) { - LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this); - return true; - } - - return false; -} - -bool CachedResource::isSafeToMakePurgeable() const -{ -#if ENABLE(DISK_IMAGE_CACHE) - // It does not make sense to have a resource in the disk image cache - // (memory mapped on disk) and purgeable (in memory). So do not allow - // disk image cached resources to be purgeable. - if (isUsingDiskImageCache()) - return false; -#endif - - return !hasClients() && !m_proxyResource && !m_resourceToRevalidate; -} - -bool CachedResource::makePurgeable(bool purgeable) -{ - if (purgeable) { - ASSERT(isSafeToMakePurgeable()); - - if (m_purgeableData) { - ASSERT(!m_data); - return true; + switch (cachePolicy) { + case CachePolicyHistoryBuffer: + return RevalidationDecision::No; + + case CachePolicyReload: + return RevalidationDecision::YesDueToCachePolicy; + + case CachePolicyRevalidate: + if (m_response.cacheControlContainsImmutable() && m_response.url().protocolIs("https")) { + if (isExpired()) + return RevalidationDecision::YesDueToExpired; + return RevalidationDecision::No; } - if (!m_data) - return false; - - // Should not make buffer purgeable if it has refs other than this since we don't want two copies. - if (!m_data->hasOneRef()) - return false; + return RevalidationDecision::YesDueToCachePolicy; - m_data->createPurgeableBuffer(); - if (!m_data->hasPurgeableBuffer()) - return false; + case CachePolicyVerify: + if (m_response.cacheControlContainsNoCache()) + return RevalidationDecision::YesDueToNoCache; + // FIXME: Cache-Control:no-store should prevent storing, not reuse. + if (m_response.cacheControlContainsNoStore()) + return RevalidationDecision::YesDueToNoStore; - m_purgeableData = m_data->releasePurgeableBuffer(); - m_purgeableData->setPurgePriority(purgePriority()); - m_purgeableData->makePurgeable(true); - m_data.clear(); - return true; - } - - if (!m_purgeableData) - return true; - ASSERT(!m_data); - ASSERT(!hasClients()); + if (isExpired()) + return RevalidationDecision::YesDueToExpired; - if (!m_purgeableData->makePurgeable(false)) - return false; - - m_data = ResourceBuffer::adoptSharedBuffer(SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release())); - return true; + return RevalidationDecision::No; + }; + ASSERT_NOT_REACHED(); + return RevalidationDecision::No; } -bool CachedResource::isPurgeable() const +bool CachedResource::redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot reuseExpiredRedirection) const { - return m_purgeableData && m_purgeableData->isPurgeable(); + return WebCore::redirectChainAllowsReuse(m_redirectChainCacheStatus, reuseExpiredRedirection); } -bool CachedResource::wasPurged() const +bool CachedResource::varyHeaderValuesMatch(const ResourceRequest& request) { - return m_purgeableData && m_purgeableData->wasPurged(); + if (m_varyingHeaderValues.isEmpty()) + return true; + + return verifyVaryingRequestHeaders(m_varyingHeaderValues, request, m_sessionID); } unsigned CachedResource::overheadSize() const @@ -855,59 +795,65 @@ unsigned CachedResource::overheadSize() const return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2; } -void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority) +bool CachedResource::areAllClientsXMLHttpRequests() const { - if (loadPriority == ResourceLoadPriorityUnresolved) - loadPriority = defaultPriorityForResourceType(type()); - if (loadPriority == m_loadPriority) - return; - m_loadPriority = loadPriority; - if (m_loader) - m_loader->didChangePriority(loadPriority); + if (type() != RawResource) + return false; + + for (auto& client : m_clients) { + if (!client.key->isXMLHttpRequest()) + return false; + } + return true; } -CachedResource::CachedResourceCallback::CachedResourceCallback(CachedResource* resource, CachedResourceClient* client) - : m_resource(resource) - , m_client(client) - , m_callbackTimer(this, &CachedResourceCallback::timerFired) +void CachedResource::setLoadPriority(const std::optional<ResourceLoadPriority>& loadPriority) { - m_callbackTimer.startOneShot(0); + if (loadPriority) + m_loadPriority = loadPriority.value(); + else + m_loadPriority = defaultPriorityForResourceType(type()); } -void CachedResource::CachedResourceCallback::cancel() +inline CachedResource::Callback::Callback(CachedResource& resource, CachedResourceClient& client) + : m_resource(resource) + , m_client(client) + , m_timer(*this, &Callback::timerFired) { - if (m_callbackTimer.isActive()) - m_callbackTimer.stop(); + m_timer.startOneShot(0); } -void CachedResource::CachedResourceCallback::timerFired(Timer<CachedResourceCallback>&) +inline void CachedResource::Callback::cancel() { - m_resource->didAddClient(m_client); + if (m_timer.isActive()) + m_timer.stop(); } -#if ENABLE(DISK_IMAGE_CACHE) -bool CachedResource::isUsingDiskImageCache() const +void CachedResource::Callback::timerFired() { - return m_data && m_data->isUsingDiskImageCache(); + m_resource.didAddClient(m_client); } -#endif -#if PLATFORM(MAC) -void CachedResource::tryReplaceEncodedData(PassRefPtr<SharedBuffer> newBuffer) +#if USE(FOUNDATION) || USE(SOUP) + +void CachedResource::tryReplaceEncodedData(SharedBuffer& newBuffer) { if (!m_data) return; if (!mayTryReplaceEncodedData()) return; - - // Because the disk cache is asynchronous and racey with regards to the data we might be asked to replace, - // we need to verify that the new buffer has the same contents as our old buffer. - if (m_data->size() != newBuffer->size() || memcmp(m_data->data(), newBuffer->data(), m_data->size())) + + // We have to do the memcmp because we can't tell if the replacement file backed data is for the + // same resource or if we made a second request with the same URL which gave us a different + // resource. We have seen this happen for cached POST resources. + if (m_data->size() != newBuffer.size() || memcmp(m_data->data(), newBuffer.data(), m_data->size())) return; - m_data->tryReplaceSharedBufferContents(newBuffer.get()); + if (m_data->tryReplaceContentsWithPlatformBuffer(newBuffer)) + didReplaceSharedBufferContents(); } + #endif } diff --git a/Source/WebCore/loader/cache/CachedResource.h b/Source/WebCore/loader/cache/CachedResource.h index 8de69cdbf..6f4e0e1b2 100644 --- a/Source/WebCore/loader/cache/CachedResource.h +++ b/Source/WebCore/loader/cache/CachedResource.h @@ -20,37 +20,37 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedResource_h -#define CachedResource_h +#pragma once #include "CachePolicy.h" +#include "CacheValidation.h" #include "FrameLoaderTypes.h" -#include "PurgePriority.h" #include "ResourceError.h" #include "ResourceLoadPriority.h" #include "ResourceLoaderOptions.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "SessionID.h" #include "Timer.h" #include <time.h> #include <wtf/HashCountedSet.h> #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> +#include <wtf/TypeCasts.h> #include <wtf/Vector.h> #include <wtf/text/WTFString.h> namespace WebCore { -class MemoryCache; class CachedResourceClient; class CachedResourceHandleBase; class CachedResourceLoader; -class InspectorResource; -class PurgeableBuffer; -class ResourceBuffer; +class CachedResourceRequest; +class LoadTiming; +class MemoryCache; class SecurityOrigin; class SharedBuffer; class SubresourceLoader; +class TextResourceDecoder; // A resource that is held in the cache. Classes who want to use this object should derive // from CachedResourceClient, to get the function calls in case the requested data has arrived. @@ -58,8 +58,7 @@ class SubresourceLoader; class CachedResource { WTF_MAKE_NONCOPYABLE(CachedResource); WTF_MAKE_FAST_ALLOCATED; friend class MemoryCache; - friend class InspectorResource; - + public: enum Type { MainResource, @@ -67,10 +66,12 @@ public: CSSStyleSheet, Script, FontResource, - RawResource -#if ENABLE(SVG) - , SVGDocumentResource +#if ENABLE(SVG_FONTS) + SVGFontResource, #endif + MediaResource, + RawResource, + SVGDocumentResource #if ENABLE(XSLT) , XSLStyleSheet #endif @@ -91,16 +92,17 @@ public: DecodeError }; - CachedResource(const ResourceRequest&, Type); + CachedResource(CachedResourceRequest&&, Type, SessionID); virtual ~CachedResource(); - virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&); + virtual void load(CachedResourceLoader&); virtual void setEncoding(const String&) { } virtual String encoding() const { return String(); } - virtual void addDataBuffer(ResourceBuffer*); + virtual const TextResourceDecoder* textResourceDecoder() const { return nullptr; } + virtual void addDataBuffer(SharedBuffer&); virtual void addData(const char* data, unsigned length); - virtual void finishLoading(ResourceBuffer*); + virtual void finishLoading(SharedBuffer*); virtual void error(CachedResource::Status); void setResourceError(const ResourceError& error) { m_error = error; } @@ -108,20 +110,20 @@ public: virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; } + const ResourceRequest& resourceRequest() const { return m_resourceRequest; } ResourceRequest& resourceRequest() { return m_resourceRequest; } const URL& url() const { return m_resourceRequest.url();} -#if ENABLE(CACHE_PARTITIONING) const String& cachePartition() const { return m_resourceRequest.cachePartition(); } -#endif - Type type() const { return static_cast<Type>(m_type); } - + SessionID sessionID() const { return m_sessionID; } + Type type() const { return m_type; } + ResourceLoadPriority loadPriority() const { return m_loadPriority; } - void setLoadPriority(ResourceLoadPriority); + void setLoadPriority(const std::optional<ResourceLoadPriority>&); - void addClient(CachedResourceClient*); - void removeClient(CachedResourceClient*); + WEBCORE_EXPORT void addClient(CachedResourceClient&); + WEBCORE_EXPORT void removeClient(CachedResourceClient&); bool hasClients() const { return !m_clients.isEmpty() || !m_clientsAwaitingCallback.isEmpty(); } - bool hasClient(CachedResourceClient* client) { return m_clients.contains(client) || m_clientsAwaitingCallback.contains(client); } + bool hasClient(CachedResourceClient& client) { return m_clients.contains(&client) || m_clientsAwaitingCallback.contains(&client); } bool deleteIfPossible(); enum PreloadResult { @@ -132,8 +134,8 @@ public: }; PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); } - virtual void didAddClient(CachedResourceClient*); - virtual void didRemoveClient(CachedResourceClient*) { } + virtual void didAddClient(CachedResourceClient&); + virtual void didRemoveClient(CachedResourceClient&) { } virtual void allClientsRemoved() { } void destroyDecodedDataIfNeeded(); @@ -146,7 +148,7 @@ public: unsigned encodedSize() const { return m_encodedSize; } unsigned decodedSize() const { return m_decodedSize; } unsigned overheadSize() const; - + bool isLoaded() const { return !m_loading; } // FIXME. Method name is inaccurate. Loading might not have started yet. bool isLoading() const { return m_loading; } @@ -155,10 +157,15 @@ public: SubresourceLoader* loader() { return m_loader.get(); } - virtual bool isImage() const { return false; } + bool areAllClientsXMLHttpRequests() const; + + bool isImage() const { return type() == ImageResource; } + // FIXME: CachedRawResource could be a main resource, an audio/video resource, or a raw XHR/icon resource. + bool isMainOrMediaOrRawResource() const { return type() == MainResource || type() == MediaResource || type() == RawResource; } bool ignoreForRequestCount() const { - return type() == MainResource + return m_resourceRequest.ignoreForRequestCount() + || type() == MainResource #if ENABLE(LINK_PREFETCH) || type() == LinkPrefetch || type() == LinkSubresource @@ -169,205 +176,220 @@ public: unsigned accessCount() const { return m_accessCount; } void increaseAccessCount() { m_accessCount++; } - // Computes the status of an object after loading. + // Computes the status of an object after loading. // Updates the expire date on the cache entry file void finish(); - bool passesAccessControlCheck(SecurityOrigin*); - // Called by the cache if the object has been removed from the cache // while still being referenced. This means the object should delete itself // if the number of clients observing it ever drops to 0. // The resource can be brought back to cache after successful revalidation. void setInCache(bool inCache) { m_inCache = inCache; } bool inCache() const { return m_inCache; } - - bool inLiveDecodedResourcesList() { return m_inLiveDecodedResourcesList; } - + void clearLoader(); - ResourceBuffer* resourceBuffer() const { ASSERT(!m_purgeableData); return m_data.get(); } + SharedBuffer* resourceBuffer() const { return m_data.get(); } - virtual void willSendRequest(ResourceRequest&, const ResourceResponse&) { m_requestedFromNetworkingLayer = true; } + virtual void redirectReceived(ResourceRequest&, const ResourceResponse&); virtual void responseReceived(const ResourceResponse&); - void setResponse(const ResourceResponse& response) { m_response = response; } + virtual bool shouldCacheResponse(const ResourceResponse&) { return true; } + void setResponse(const ResourceResponse&); const ResourceResponse& response() const { return m_response; } + void setCrossOrigin(); + bool isCrossOrigin() const; + bool isCORSSameOrigin() const; + ResourceResponse::Tainting responseTainting() const { return m_responseTainting; } + + void loadFrom(const CachedResource&); + + SecurityOrigin* origin() const { return m_origin.get(); } + AtomicString initiatorName() const { return m_initiatorName; } + bool canDelete() const { return !hasClients() && !m_loader && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; } bool hasOneHandle() const { return m_handleCount == 1; } bool isExpired() const; - // List of acceptable MIME types separated by ",". - // A MIME type may contain a wildcard, e.g. "text/*". - String accept() const { return m_accept; } - void setAccept(const String& accept) { m_accept = accept; } - void cancelLoad(); bool wasCanceled() const { return m_error.isCancellation(); } bool errorOccurred() const { return m_status == LoadError || m_status == DecodeError; } - bool loadFailedOrCanceled() { return !m_error.isNull(); } + bool loadFailedOrCanceled() const { return !m_error.isNull(); } bool shouldSendResourceLoadCallbacks() const { return m_options.sendLoadCallbacks == SendCallbacks; } DataBufferingPolicy dataBufferingPolicy() const { return m_options.dataBufferingPolicy; } - + + bool allowsCaching() const { return m_options.cachingPolicy == CachingPolicy::AllowCaching; } + const FetchOptions& options() const { return m_options; } + virtual void destroyDecodedData() { } void setOwningCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { m_owningCachedResourceLoader = cachedResourceLoader; } - + bool isPreloaded() const { return m_preloadCount; } void increasePreloadCount() { ++m_preloadCount; } void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } - - void registerHandle(CachedResourceHandleBase* h); - void unregisterHandle(CachedResourceHandleBase* h); - + bool isLinkPreload() { return m_isLinkPreload; } + void setLinkPreload() { m_isLinkPreload = true; } + + void registerHandle(CachedResourceHandleBase*); + WEBCORE_EXPORT void unregisterHandle(CachedResourceHandleBase*); + bool canUseCacheValidator() const; - virtual bool mustRevalidateDueToCacheHeaders(CachePolicy) const; + enum class RevalidationDecision { No, YesDueToCachePolicy, YesDueToNoStore, YesDueToNoCache, YesDueToExpired }; + virtual RevalidationDecision makeRevalidationDecision(CachePolicy) const; + bool redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot) const; + bool hasRedirections() const { return m_redirectChainCacheStatus.status != RedirectChainCacheStatus::Status::NoRedirection; } + + bool varyHeaderValuesMatch(const ResourceRequest&); bool isCacheValidator() const { return m_resourceToRevalidate; } CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; } - - bool isPurgeable() const; - bool wasPurged() const; - - // This is used by the archive machinery to get at a purged resource without - // triggering a load. We should make it protected again if we can find a - // better way to handle the archive case. - bool makePurgeable(bool purgeable); - + // HTTP revalidation support methods for CachedResourceLoader. void setResourceToRevalidate(CachedResource*); virtual void switchClientsToRevalidatedResource(); void clearResourceToRevalidate(); void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse); - + bool validationInProgress() const { return m_proxyResource; } + bool validationCompleting() const { return m_proxyResource && m_proxyResource->m_switchingClientsToRevalidatedResource; } + virtual void didSendData(unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { } + virtual void didRetrieveDerivedDataFromCache(const String& /* type */, SharedBuffer&) { } + void setLoadFinishTime(double finishTime) { m_loadFinishTime = finishTime; } double loadFinishTime() const { return m_loadFinishTime; } -#if ENABLE(DISK_IMAGE_CACHE) - bool isUsingDiskImageCache() const; - virtual bool canUseDiskImageCache() const { return false; } - virtual void useDiskImageCache() { ASSERT(canUseDiskImageCache()); } -#endif - - virtual bool canReuse(const ResourceRequest&) const { return true; } - -#if PLATFORM(MAC) - void tryReplaceEncodedData(PassRefPtr<SharedBuffer>); +#if USE(FOUNDATION) || USE(SOUP) + WEBCORE_EXPORT void tryReplaceEncodedData(SharedBuffer&); #endif #if USE(SOUP) - virtual char* getOrCreateReadBuffer(size_t /* requestedSize */, size_t& /* actualSize */) { return 0; } + virtual char* getOrCreateReadBuffer(size_t /* requestedSize */, size_t& /* actualSize */) { return nullptr; } #endif + unsigned long identifierForLoadWithoutResourceLoader() const { return m_identifierForLoadWithoutResourceLoader; } + static ResourceLoadPriority defaultPriorityForResourceType(Type); + protected: - virtual void checkNotify(); + // CachedResource constructor that may be used when the CachedResource can already be filled with response data. + CachedResource(const URL&, Type, SessionID); void setEncodedSize(unsigned); void setDecodedSize(unsigned); void didAccessDecodedData(double timeStamp); - bool isSafeToMakePurgeable() const; - - HashCountedSet<CachedResourceClient*> m_clients; + virtual void didReplaceSharedBufferContents() { } - class CachedResourceCallback { - public: - static PassOwnPtr<CachedResourceCallback> schedule(CachedResource* resource, CachedResourceClient* client) { return adoptPtr(new CachedResourceCallback(resource, client)); } - void cancel(); - private: - CachedResourceCallback(CachedResource*, CachedResourceClient*); - void timerFired(Timer<CachedResourceCallback>&); - - CachedResource* m_resource; - CachedResourceClient* m_client; - Timer<CachedResourceCallback> m_callbackTimer; - }; - HashMap<CachedResourceClient*, OwnPtr<CachedResourceCallback>> m_clientsAwaitingCallback; + virtual void setBodyDataFrom(const CachedResource&); + // FIXME: Make the rest of these data members private and use functions in derived classes instead. + HashCountedSet<CachedResourceClient*> m_clients; ResourceRequest m_resourceRequest; - String m_accept; RefPtr<SubresourceLoader> m_loader; ResourceLoaderOptions m_options; - ResourceLoadPriority m_loadPriority; - ResourceResponse m_response; - double m_responseTimestamp; - - RefPtr<ResourceBuffer> m_data; - OwnPtr<PurgeableBuffer> m_purgeableData; - DeferrableOneShotTimer<CachedResource> m_decodedDataDeletionTimer; + ResourceResponse::Tainting m_responseTainting { ResourceResponse::Tainting::Basic }; + RefPtr<SharedBuffer> m_data; + DeferrableOneShotTimer m_decodedDataDeletionTimer; private: - bool addClientToSet(CachedResourceClient*); + class Callback; + + bool addClientToSet(CachedResourceClient&); - void decodedDataDeletionTimerFired(DeferrableOneShotTimer<CachedResource>&); + void decodedDataDeletionTimerFired(); - virtual PurgePriority purgePriority() const { return PurgeDefault; } + virtual void checkNotify(); virtual bool mayTryReplaceEncodedData() const { return false; } - double currentAge() const; - double freshnessLifetime() const; + std::chrono::microseconds freshnessLifetime(const ResourceResponse&) const; - void addAdditionalRequestHeaders(CachedResourceLoader*); + void addAdditionalRequestHeaders(CachedResourceLoader&); void failBeforeStarting(); + HashMap<CachedResourceClient*, std::unique_ptr<Callback>> m_clientsAwaitingCallback; + SessionID m_sessionID; + ResourceLoadPriority m_loadPriority; + std::chrono::system_clock::time_point m_responseTimestamp; + String m_fragmentIdentifierForRequest; ResourceError m_error; + RefPtr<SecurityOrigin> m_origin; + AtomicString m_initiatorName; - double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache - double m_loadFinishTime; + double m_lastDecodedAccessTime { 0 }; // Used as a "thrash guard" in the cache + double m_loadFinishTime { 0 }; - unsigned m_encodedSize; - unsigned m_decodedSize; - unsigned m_accessCount; - unsigned m_handleCount; - unsigned m_preloadCount; + unsigned m_encodedSize { 0 }; + unsigned m_decodedSize { 0 }; + unsigned m_accessCount { 0 }; + unsigned m_handleCount { 0 }; + unsigned m_preloadCount { 0 }; - unsigned m_preloadResult : 2; // PreloadResult + PreloadResult m_preloadResult { PreloadNotReferenced }; - bool m_inLiveDecodedResourcesList : 1; - bool m_requestedFromNetworkingLayer : 1; + bool m_requestedFromNetworkingLayer { false }; - bool m_inCache : 1; - bool m_loading : 1; + bool m_inCache { false }; + bool m_loading { false }; + bool m_isLinkPreload { false }; - bool m_switchingClientsToRevalidatedResource : 1; + bool m_switchingClientsToRevalidatedResource { false }; - unsigned m_type : 4; // Type - unsigned m_status : 3; // Status + Type m_type; // Type + unsigned m_status { Pending }; // Status #ifndef NDEBUG - bool m_deleted; - unsigned m_lruIndex; + bool m_deleted { false }; + unsigned m_lruIndex { 0 }; #endif - CachedResource* m_nextInAllResourcesList; - CachedResource* m_prevInAllResourcesList; - - CachedResource* m_nextInLiveResourcesList; - CachedResource* m_prevInLiveResourcesList; + CachedResourceLoader* m_owningCachedResourceLoader { nullptr }; // only non-null for resources that are not in the cache - CachedResourceLoader* m_owningCachedResourceLoader; // only non-0 for resources that are not in the cache - // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this // resources becomes normal resource load. - CachedResource* m_resourceToRevalidate; + CachedResource* m_resourceToRevalidate { nullptr }; // If this field is non-null, the resource has a proxy for checking whether it is still up to date (see m_resourceToRevalidate). - CachedResource* m_proxyResource; + CachedResource* m_proxyResource { nullptr }; // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response. HashSet<CachedResourceHandleBase*> m_handlesToRevalidate; -}; -} + RedirectChainCacheStatus m_redirectChainCacheStatus; + + Vector<std::pair<String, String>> m_varyingHeaderValues; + + unsigned long m_identifierForLoadWithoutResourceLoader { 0 }; +}; +class CachedResource::Callback { +#if !COMPILER(MSVC) + WTF_MAKE_FAST_ALLOCATED; #endif +public: + Callback(CachedResource&, CachedResourceClient&); + + void cancel(); + +private: + void timerFired(); + + CachedResource& m_resource; + CachedResourceClient& m_client; + Timer m_timer; +}; + +} // namespace WebCore + +#define SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(ToClassName, CachedResourceType) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \ + static bool isType(const WebCore::CachedResource& resource) { return resource.type() == WebCore::CachedResourceType; } \ +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/loader/cache/CachedResourceClient.h b/Source/WebCore/loader/cache/CachedResourceClient.h index 402ff91a1..17c361d71 100644 --- a/Source/WebCore/loader/cache/CachedResourceClient.h +++ b/Source/WebCore/loader/cache/CachedResourceClient.h @@ -21,39 +21,33 @@ This class provides all functionality needed for loading images, style sheets and html pages from the web. It has a memory cache for these objects. */ - -#ifndef CachedResourceClient_h -#define CachedResourceClient_h - -#include <wtf/FastMalloc.h> +#pragma once namespace WebCore { + class CachedResource; class CachedResourceClient { - WTF_MAKE_FAST_ALLOCATED; public: enum CachedResourceClientType { BaseResourceType, ImageType, FontType, StyleSheetType, -#if ENABLE(SVG) SVGDocumentType, -#endif RawResourceType }; virtual ~CachedResourceClient() { } - virtual void notifyFinished(CachedResource*) { } - virtual void deprecatedDidReceiveCachedResource(CachedResource*) { } - + virtual void notifyFinished(CachedResource&) { } + virtual void deprecatedDidReceiveCachedResource(CachedResource&) { } + virtual bool isXMLHttpRequest() const { return false; } + static CachedResourceClientType expectedType() { return BaseResourceType; } virtual CachedResourceClientType resourceClientType() const { return expectedType(); } protected: CachedResourceClient() { } }; -} -#endif +} diff --git a/Source/WebCore/loader/cache/CachedResourceClientWalker.h b/Source/WebCore/loader/cache/CachedResourceClientWalker.h index de352f037..39ee0234f 100644 --- a/Source/WebCore/loader/cache/CachedResourceClientWalker.h +++ b/Source/WebCore/loader/cache/CachedResourceClientWalker.h @@ -22,8 +22,7 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef CachedResourceClientWalker_h -#define CachedResourceClientWalker_h +#pragma once #include "CachedResourceClient.h" #include <wtf/HashCountedSet.h> @@ -56,7 +55,7 @@ public: } } - return 0; + return nullptr; } private: const HashCountedSet<CachedResourceClient*>& m_clientSet; @@ -64,6 +63,4 @@ private: size_t m_index; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedResourceHandle.cpp b/Source/WebCore/loader/cache/CachedResourceHandle.cpp index 07c84ed70..097276e93 100644 --- a/Source/WebCore/loader/cache/CachedResourceHandle.cpp +++ b/Source/WebCore/loader/cache/CachedResourceHandle.cpp @@ -31,7 +31,7 @@ namespace WebCore { CachedResourceHandleBase::CachedResourceHandleBase() - : m_resource(0) + : m_resource(nullptr) { } diff --git a/Source/WebCore/loader/cache/CachedResourceHandle.h b/Source/WebCore/loader/cache/CachedResourceHandle.h index 5b6b42540..3dbbda6c1 100644 --- a/Source/WebCore/loader/cache/CachedResourceHandle.h +++ b/Source/WebCore/loader/cache/CachedResourceHandle.h @@ -23,9 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedResourceHandle_h -#define CachedResourceHandle_h +#pragma once +#include "PlatformExportMacros.h" #include <wtf/Forward.h> namespace WebCore { @@ -49,7 +49,7 @@ protected: CachedResourceHandleBase(CachedResource*); CachedResourceHandleBase(const CachedResourceHandleBase&); - void setResource(CachedResource*); + WEBCORE_EXPORT void setResource(CachedResource*); private: CachedResourceHandleBase& operator=(const CachedResourceHandleBase&) { return *this; } @@ -68,6 +68,7 @@ public: R* get() const { return reinterpret_cast<R*>(CachedResourceHandleBase::get()); } R* operator->() const { return get(); } + R& operator*() const { ASSERT(get()); return *get(); } CachedResourceHandle& operator=(R* res) { setResource(res); return *this; } CachedResourceHandle& operator=(const CachedResourceHandle& o) { setResource(o.get()); return *this; } @@ -95,5 +96,3 @@ template <class R, class RR> bool operator!=(const RR* res, const CachedResource } } // namespace WebCore - -#endif // CachedResourceHandle diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp index bd38fe823..baf827eb7 100644 --- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp +++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp @@ -2,7 +2,7 @@ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) - Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + Copyright (C) 2004-2016 Apple Inc. All rights reserved. Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ This library is free software; you can redistribute it and/or @@ -28,18 +28,22 @@ #include "CachedResourceLoader.h" #include "CachedCSSStyleSheet.h" -#include "CachedSVGDocument.h" #include "CachedFont.h" #include "CachedImage.h" #include "CachedRawResource.h" #include "CachedResourceRequest.h" +#include "CachedSVGDocument.h" +#include "CachedSVGFont.h" #include "CachedScript.h" #include "CachedXSLStyleSheet.h" #include "Chrome.h" #include "ChromeClient.h" -#include "Console.h" +#include "ContentExtensionError.h" +#include "ContentExtensionRule.h" #include "ContentSecurityPolicy.h" #include "DOMWindow.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" #include "Document.h" #include "DocumentLoader.h" #include "Frame.h" @@ -48,16 +52,26 @@ #include "HTMLElement.h" #include "HTMLFrameOwnerElement.h" #include "LoaderStrategy.h" +#include "LocalizedStrings.h" #include "Logging.h" +#include "MainFrame.h" #include "MemoryCache.h" #include "Page.h" #include "PingLoader.h" #include "PlatformStrategies.h" #include "RenderElement.h" -#include "ResourceLoadScheduler.h" +#include "ResourceLoadInfo.h" +#include "ResourceTiming.h" +#include "RuntimeEnabledFeatures.h" #include "ScriptController.h" #include "SecurityOrigin.h" +#include "SecurityPolicy.h" +#include "SessionID.h" #include "Settings.h" +#include "StyleSheetContents.h" +#include "SubresourceLoader.h" +#include "UserContentController.h" +#include "UserStyleSheet.h" #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> @@ -65,56 +79,57 @@ #include "CachedTextTrack.h" #endif -#if ENABLE(RESOURCE_TIMING) -#include "Performance.h" -#endif - #define PRELOAD_DEBUG 0 +#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - CachedResourceLoader::" fmt, this, ##__VA_ARGS__) + namespace WebCore { -static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset) +static CachedResource* createResource(CachedResource::Type type, CachedResourceRequest&& request, SessionID sessionID) { switch (type) { case CachedResource::ImageResource: - return new CachedImage(request); + return new CachedImage(WTFMove(request), sessionID); case CachedResource::CSSStyleSheet: - return new CachedCSSStyleSheet(request, charset); + return new CachedCSSStyleSheet(WTFMove(request), sessionID); case CachedResource::Script: - return new CachedScript(request, charset); -#if ENABLE(SVG) + return new CachedScript(WTFMove(request), sessionID); case CachedResource::SVGDocumentResource: - return new CachedSVGDocument(request); + return new CachedSVGDocument(WTFMove(request), sessionID); +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: + return new CachedSVGFont(WTFMove(request), sessionID); #endif case CachedResource::FontResource: - return new CachedFont(request); + return new CachedFont(WTFMove(request), sessionID); + case CachedResource::MediaResource: case CachedResource::RawResource: case CachedResource::MainResource: - return new CachedRawResource(request, type); + return new CachedRawResource(WTFMove(request), type, sessionID); #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: - return new CachedXSLStyleSheet(request); + return new CachedXSLStyleSheet(WTFMove(request), sessionID); #endif #if ENABLE(LINK_PREFETCH) case CachedResource::LinkPrefetch: - return new CachedResource(request, CachedResource::LinkPrefetch); + return new CachedResource(WTFMove(request), CachedResource::LinkPrefetch, sessionID); case CachedResource::LinkSubresource: - return new CachedResource(request, CachedResource::LinkSubresource); + return new CachedResource(WTFMove(request), CachedResource::LinkSubresource, sessionID); #endif #if ENABLE(VIDEO_TRACK) case CachedResource::TextTrackResource: - return new CachedTextTrack(request); + return new CachedTextTrack(WTFMove(request), sessionID); #endif } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader) - : m_document(0) + : m_document(nullptr) , m_documentLoader(documentLoader) , m_requestCount(0) - , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired) + , m_garbageCollectDocumentResourcesTimer(*this, &CachedResourceLoader::garbageCollectDocumentResources) , m_autoLoadImages(true) , m_imagesEnabled(true) , m_allowStaleResources(false) @@ -123,157 +138,233 @@ CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader) CachedResourceLoader::~CachedResourceLoader() { - m_documentLoader = 0; - m_document = 0; + m_documentLoader = nullptr; + m_document = nullptr; - clearPreloads(); - DocumentResourceMap::iterator end = m_documentResources.end(); - for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) - it->value->setOwningCachedResourceLoader(0); + clearPreloads(ClearPreloadsMode::ClearAllPreloads); + for (auto& resource : m_documentResources.values()) + resource->setOwningCachedResourceLoader(nullptr); // Make sure no requests still point to this CachedResourceLoader ASSERT(m_requestCount == 0); } -CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const +CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const { - URL url = m_document->completeURL(resourceURL); - return cachedResource(url); + ASSERT(!resourceURL.isNull()); + return cachedResource(MemoryCache::removeFragmentIdentifierIfNeeded(m_document->completeURL(resourceURL))); } -CachedResource* CachedResourceLoader::cachedResource(const URL& resourceURL) const +CachedResource* CachedResourceLoader::cachedResource(const URL& url) const { - URL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); - return m_documentResources.get(url).get(); + ASSERT(!MemoryCache::shouldRemoveFragmentIdentifier(url)); + return m_documentResources.get(url).get(); } Frame* CachedResourceLoader::frame() const { - return m_documentLoader ? m_documentLoader->frame() : 0; + return m_documentLoader ? m_documentLoader->frame() : nullptr; +} + +SessionID CachedResourceLoader::sessionID() const +{ + SessionID sessionID = SessionID::defaultSessionID(); + + if (Frame* f = frame()) + sessionID = f->page()->sessionID(); + + return sessionID; } -CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request) +CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest&& request) { - if (Frame* f = frame()) { - if (f->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) { + if (Frame* frame = this->frame()) { + if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) { + if (Document* document = frame->document()) + request.upgradeInsecureRequestIfNeeded(*document); URL requestURL = request.resourceRequest().url(); - if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request.options(), request.forPreload())) - PingLoader::loadImage(f, requestURL); - return 0; + if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request, ForPreload::No)) + PingLoader::loadImage(*frame, requestURL); + return nullptr; } } - request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer); - return static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, request).get()); + + auto defer = clientDefersImage(request.resourceRequest().url()) ? DeferOption::DeferredByClient : DeferOption::NoDefer; + return downcast<CachedImage>(requestResource(CachedResource::ImageResource, WTFMove(request), ForPreload::No, defer).get()); } -CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request) +CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG) { - return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, request).get()); +#if ENABLE(SVG_FONTS) + if (isSVG) + return downcast<CachedSVGFont>(requestResource(CachedResource::SVGFontResource, WTFMove(request)).get()); +#else + UNUSED_PARAM(isSVG); +#endif + return downcast<CachedFont>(requestResource(CachedResource::FontResource, WTFMove(request)).get()); } #if ENABLE(VIDEO_TRACK) -CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request) +CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest&& request) { - return static_cast<CachedTextTrack*>(requestResource(CachedResource::TextTrackResource, request).get()); + return downcast<CachedTextTrack>(requestResource(CachedResource::TextTrackResource, WTFMove(request)).get()); } #endif -CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request) +CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest&& request) { - return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request).get()); + return downcast<CachedCSSStyleSheet>(requestResource(CachedResource::CSSStyleSheet, WTFMove(request)).get()); } -CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request) +CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest&& request) { - URL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url()); - -#if ENABLE(CACHE_PARTITIONING) - request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); -#endif - - if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest())) { - if (existing->type() == CachedResource::CSSStyleSheet) - return static_cast<CachedCSSStyleSheet*>(existing); - memoryCache()->remove(existing); + ASSERT(document()); + request.setDomainForCachePartition(*document()); + + auto& memoryCache = MemoryCache::singleton(); + if (request.allowsCaching()) { + if (CachedResource* existing = memoryCache.resourceForRequest(request.resourceRequest(), sessionID())) { + if (is<CachedCSSStyleSheet>(*existing)) + return downcast<CachedCSSStyleSheet>(existing); + memoryCache.remove(*existing); + } } - if (url.string() != request.resourceRequest().url()) - request.mutableResourceRequest().setURL(url); - CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset()); + request.removeFragmentIdentifierIfNeeded(); + + CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(WTFMove(request), sessionID()); - memoryCache()->add(userSheet.get()); + if (userSheet->allowsCaching()) + memoryCache.add(*userSheet); // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too? - userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType)); - + userSheet->load(*this); return userSheet; } -CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request) +CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest&& request) { - return static_cast<CachedScript*>(requestResource(CachedResource::Script, request).get()); + return downcast<CachedScript>(requestResource(CachedResource::Script, WTFMove(request)).get()); } #if ENABLE(XSLT) -CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request) +CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest&& request) { - return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, request).get()); + return downcast<CachedXSLStyleSheet>(requestResource(CachedResource::XSLStyleSheet, WTFMove(request)).get()); } #endif -#if ENABLE(SVG) -CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request) +CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest&& request) { - return static_cast<CachedSVGDocument*>(requestResource(CachedResource::SVGDocumentResource, request).get()); + return downcast<CachedSVGDocument>(requestResource(CachedResource::SVGDocumentResource, WTFMove(request)).get()); } -#endif #if ENABLE(LINK_PREFETCH) -CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request) +CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest&& request) { ASSERT(frame()); ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource); - return requestResource(type, request); + return requestResource(type, WTFMove(request)); } #endif -CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request) +CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMedia(CachedResourceRequest&& request) { - return static_cast<CachedRawResource*>(requestResource(CachedResource::RawResource, request).get()); + return downcast<CachedRawResource>(requestResource(CachedResource::MediaResource, WTFMove(request)).get()); } -CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request) +CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest&& request) { - return static_cast<CachedRawResource*>(requestResource(CachedResource::MainResource, request).get()); + return downcast<CachedRawResource>(requestResource(CachedResource::RawResource, WTFMove(request)).get()); +} + +CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest&& request) +{ + return downcast<CachedRawResource>(requestResource(CachedResource::MainResource, WTFMove(request)).get()); +} + +static MixedContentChecker::ContentType contentTypeFromResourceType(CachedResource::Type type) +{ + switch (type) { + // https://w3c.github.io/webappsec-mixed-content/#category-optionally-blockable + // Editor's Draft, 11 February 2016 + // 3.1. Optionally-blockable Content + case CachedResource::ImageResource: + case CachedResource::MediaResource: + return MixedContentChecker::ContentType::ActiveCanWarn; + + case CachedResource::CSSStyleSheet: + case CachedResource::Script: + case CachedResource::FontResource: + return MixedContentChecker::ContentType::Active; + +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: + return MixedContentChecker::ContentType::Active; +#endif + + case CachedResource::RawResource: + case CachedResource::SVGDocumentResource: + return MixedContentChecker::ContentType::Active; +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: + return MixedContentChecker::ContentType::Active; +#endif + +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + case CachedResource::LinkSubresource: + return MixedContentChecker::ContentType::Active; +#endif + +#if ENABLE(VIDEO_TRACK) + case CachedResource::TextTrackResource: + return MixedContentChecker::ContentType::Active; +#endif + default: + ASSERT_NOT_REACHED(); + return MixedContentChecker::ContentType::Active; + } } bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const { + if (!canRequestInContentDispositionAttachmentSandbox(type, url)) + return false; + switch (type) { case CachedResource::Script: #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: #endif -#if ENABLE(SVG) case CachedResource::SVGDocumentResource: -#endif case CachedResource::CSSStyleSheet: // These resource can inject script into the current document (Script, // XSL) or exfiltrate the content of the current document (CSS). - if (Frame* f = frame()) - if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url)) + if (Frame* frame = this->frame()) { + if (!frame->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url)) return false; + Frame& top = frame->tree().top(); + if (&top != frame && !top.loader().mixedContentChecker().canRunInsecureContent(top.document()->securityOrigin(), url)) + return false; + } break; #if ENABLE(VIDEO_TRACK) case CachedResource::TextTrackResource: #endif + case CachedResource::MediaResource: case CachedResource::RawResource: case CachedResource::ImageResource: +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif case CachedResource::FontResource: { // These resources can corrupt only the frame's pixels. - if (Frame* f = frame()) { - Frame& topFrame = f->tree().top(); - if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), url)) + if (Frame* frame = this->frame()) { + if (!frame->loader().mixedContentChecker().canDisplayInsecureContent(m_document->securityOrigin(), contentTypeFromResourceType(type), url, MixedContentChecker::AlwaysDisplayInNonStrictMode::Yes)) + return false; + Frame& topFrame = frame->tree().top(); + if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), contentTypeFromResourceType(type), url)) return false; } break; @@ -289,306 +380,602 @@ bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const return true; } -bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, bool forPreload) +bool CachedResourceLoader::allowedByContentSecurityPolicy(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived) const { - if (document() && !document()->securityOrigin()->canDisplay(url)) { - if (!forPreload) - FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength()); - LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay"); - return 0; - } - - // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. - bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()); + if (options.contentSecurityPolicyImposition == ContentSecurityPolicyImposition::SkipPolicyCheck) + return true; - // Some types of resources can be loaded only from the same origin. Other - // types of resources, like Images, Scripts, and CSS, can be loaded from - // any URL. - switch (type) { - case CachedResource::MainResource: - case CachedResource::ImageResource: - case CachedResource::CSSStyleSheet: - case CachedResource::Script: - case CachedResource::FontResource: - case CachedResource::RawResource: -#if ENABLE(LINK_PREFETCH) - case CachedResource::LinkPrefetch: - case CachedResource::LinkSubresource: -#endif -#if ENABLE(VIDEO_TRACK) - case CachedResource::TextTrackResource: -#endif - if (options.requestOriginPolicy == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) { - printAccessDeniedMessage(url); - return false; - } - break; -#if ENABLE(SVG) - case CachedResource::SVGDocumentResource: -#endif -#if ENABLE(XSLT) - case CachedResource::XSLStyleSheet: - if (!m_document->securityOrigin()->canRequest(url)) { - printAccessDeniedMessage(url); - return false; - } -#endif - break; - } + ASSERT(m_document); + ASSERT(m_document->contentSecurityPolicy()); switch (type) { #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: - if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) - return false; - break; #endif case CachedResource::Script: - if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) + if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url, redirectResponseReceived)) return false; - - if (frame()) { - if (!frame()->loader().client().allowScriptFromSource(frame()->settings().isScriptEnabled(), url)) - return false; - } break; case CachedResource::CSSStyleSheet: - if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url)) + if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url, redirectResponseReceived)) return false; break; -#if ENABLE(SVG) case CachedResource::SVGDocumentResource: -#endif case CachedResource::ImageResource: - if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url)) - return false; - break; - case CachedResource::FontResource: { - if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url)) + if (!m_document->contentSecurityPolicy()->allowImageFromSource(url, redirectResponseReceived)) return false; break; - } - case CachedResource::MainResource: - case CachedResource::RawResource: -#if ENABLE(LINK_PREFETCH) - case CachedResource::LinkPrefetch: - case CachedResource::LinkSubresource: +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: #endif + case CachedResource::FontResource: + if (!m_document->contentSecurityPolicy()->allowFontFromSource(url, redirectResponseReceived)) + return false; break; + case CachedResource::MediaResource: #if ENABLE(VIDEO_TRACK) case CachedResource::TextTrackResource: - if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url)) +#endif + if (!m_document->contentSecurityPolicy()->allowMediaFromSource(url, redirectResponseReceived)) return false; break; -#endif + case CachedResource::RawResource: + return true; + default: + ASSERT_NOT_REACHED(); } + return true; +} + +static inline bool isSameOriginDataURL(const URL& url, const ResourceLoaderOptions& options) +{ + // FIXME: Remove same-origin data URL flag since it was removed from fetch spec (https://github.com/whatwg/fetch/issues/381). + return url.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set; +} + +bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const CachedResourceRequest& request, ForPreload forPreload) +{ + auto& options = request.options(); + + if (document() && !document()->securityOrigin().canDisplay(url)) { + if (forPreload == ForPreload::No) + FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength()); + LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay"); + return false; + } + + if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin().canRequest(url) && !isSameOriginDataURL(url, options)) { + printAccessDeniedMessage(url); + return false; + } + + if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::No)) + return false; + // SVG Images have unique security rules that prevent all subresource requests except for data urls. if (type != CachedResource::MainResource && frame() && frame()->page()) { if (frame()->page()->chrome().client().isSVGImageChromeClient() && !url.protocolIsData()) return false; } - // Last of all, check for insecure content. We do this last so that when - // folks block insecure content with a CSP policy, they don't get a warning. + // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning. // They'll still get a warning in the console about CSP blocking the load. - // FIXME: Should we consider forPreload here? + // FIXME: Should we consider whether the request is for preload here? + if (!checkInsecureContent(type, url)) + return false; + + return true; +} + +// FIXME: Should we find a way to know whether the redirection is for a preload request like we do for CachedResourceLoader::canRequest? +bool CachedResourceLoader::canRequestAfterRedirection(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options) const +{ + if (document() && !document()->securityOrigin().canDisplay(url)) { + FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength()); + LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay"); + return false; + } + + // FIXME: According to https://fetch.spec.whatwg.org/#http-redirect-fetch, we should check that the URL is HTTP(s) except if in navigation mode. + // But we currently allow at least data URLs to be loaded. + + if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin().canRequest(url)) { + printAccessDeniedMessage(url); + return false; + } + + if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::Yes)) + return false; + + // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning. + // They'll still get a warning in the console about CSP blocking the load. if (!checkInsecureContent(type, url)) return false; return true; } -bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(CachedResource* resource) +bool CachedResourceLoader::updateRequestAfterRedirection(CachedResource::Type type, ResourceRequest& request, const ResourceLoaderOptions& options) +{ + ASSERT(m_documentLoader); + if (auto* document = m_documentLoader->cachedResourceLoader().document()) + upgradeInsecureResourceRequestIfNeeded(request, *document); + + // FIXME: We might want to align the checks done here with the ones done in CachedResourceLoader::requestResource, content extensions blocking in particular. + + return canRequestAfterRedirection(type, request.url(), options); +} + +bool CachedResourceLoader::canRequestInContentDispositionAttachmentSandbox(CachedResource::Type type, const URL& url) const +{ + Document* document; + + // FIXME: Do we want to expand this to all resource types that the mixed content checker would consider active content? + switch (type) { + case CachedResource::MainResource: + if (auto ownerElement = frame() ? frame()->ownerElement() : nullptr) { + document = &ownerElement->document(); + break; + } + return true; + case CachedResource::CSSStyleSheet: + document = m_document; + break; + default: + return true; + } + + if (!document->shouldEnforceContentDispositionAttachmentSandbox() || document->securityOrigin().canRequest(url)) + return true; + + String message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from document with Content-Disposition: attachment at URL " + document->url().stringCenterEllipsizedToLength() + "."; + document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); + return false; +} + +bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource* resource) { if (!resource || !frame() || resource->status() != CachedResource::Cached) return true; - ResourceRequest newRequest; + ResourceRequest newRequest = ResourceRequest(resource->url()); + newRequest.setInitiatorIdentifier(request.resourceRequest().initiatorIdentifier()); + if (request.resourceRequest().hiddenFromInspector()) + newRequest.setHiddenFromInspector(true); frame()->loader().loadedResourceFromMemoryCache(resource, newRequest); - + // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's // URL, it is no longer appropriate to use this CachedResource. return !newRequest.isNull(); } -CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request) +bool CachedResourceLoader::shouldUpdateCachedResourceWithCurrentRequest(const CachedResource& resource, const CachedResourceRequest& request) +{ + // WebKit is not supporting CORS for fonts (https://bugs.webkit.org/show_bug.cgi?id=86817), no need to update the resource before reusing it. + if (resource.type() == CachedResource::Type::FontResource) + return false; + +#if ENABLE(SVG_FONTS) + if (resource.type() == CachedResource::Type::SVGFontResource) + return false; +#endif + +#if ENABLE(XSLT) + // Load is same-origin, we do not check for CORS. + if (resource.type() == CachedResource::XSLStyleSheet) + return false; +#endif + + // FIXME: We should enable resource reuse for these resource types + switch (resource.type()) { + case CachedResource::SVGDocumentResource: + return false; + case CachedResource::MainResource: + return false; +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return false; + case CachedResource::LinkSubresource: + return false; +#endif + default: + break; + } + + if (resource.options().mode != request.options().mode || !originsMatch(request.origin(), resource.origin())) + return true; + + if (resource.options().redirect != request.options().redirect && resource.hasRedirections()) + return true; + + return false; +} + +static inline bool isResourceSuitableForDirectReuse(const CachedResource& resource, const CachedResourceRequest& request) +{ + // FIXME: For being loaded requests, the response tainting may not be correctly computed if the fetch mode is not the same. + // Even if the fetch mode is the same, we are not sure that the resource can be reused (Vary: Origin header for instance). + // We should find a way to improve this. + if (resource.status() != CachedResource::Cached) + return false; + + // If the cached resource has not followed redirections, it is incomplete and we should not use it. + // Let's make sure the memory cache has no such resource. + ASSERT(resource.response().type() != ResourceResponse::Type::Opaqueredirect); + + // We could support redirect modes other than Follow in case of a redirected resource. + // This case is rare and is not worth optimizing currently. + if (request.options().redirect != FetchOptions::Redirect::Follow && resource.hasRedirections()) + return false; + + // FIXME: Implement reuse of cached raw resources. + if (resource.type() == CachedResource::Type::RawResource || resource.type() == CachedResource::Type::MediaResource) + return false; + + return true; +} + +CachedResourceHandle<CachedResource> CachedResourceLoader::updateCachedResourceWithCurrentRequest(const CachedResource& resource, CachedResourceRequest&& request) +{ + if (!isResourceSuitableForDirectReuse(resource, request)) { + request.setCachingPolicy(CachingPolicy::DisallowCaching); + return loadResource(resource.type(), WTFMove(request)); + } + + auto resourceHandle = createResource(resource.type(), WTFMove(request), sessionID()); + resourceHandle->loadFrom(resource); + return resourceHandle; +} + +static inline void logMemoryCacheResourceRequest(Frame* frame, const String& key, const String& description) { + if (!frame || !frame->page()) + return; + frame->page()->diagnosticLoggingClient().logDiagnosticMessage(key, description, ShouldSample::Yes); +} + +void CachedResourceLoader::prepareFetch(CachedResource::Type type, CachedResourceRequest& request) +{ + // Implementing step 1 to 7 of https://fetch.spec.whatwg.org/#fetching + + if (!request.origin() && document()) + request.setOrigin(document()->securityOrigin()); + + request.setAcceptHeaderIfNone(type); + + // Accept-Language value is handled in underlying port-specific code. + // FIXME: Decide whether to support client hints +} + +void CachedResourceLoader::updateHTTPRequestHeaders(CachedResource::Type type, CachedResourceRequest& request) +{ + // Implementing steps 7 to 12 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch + + // FIXME: We should reconcile handling of MainResource with other resources. + if (type != CachedResource::Type::MainResource) { + // In some cases we may try to load resources in frameless documents. Such loads always fail. + // FIXME: We shouldn't need to do the check on frame. + if (auto* frame = this->frame()) + request.updateReferrerOriginAndUserAgentHeaders(frame->loader(), document() ? document()->referrerPolicy() : ReferrerPolicy::Default); + } + + request.updateAccordingCacheMode(); +} + +CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer) +{ + if (Document* document = this->document()) + request.upgradeInsecureRequestIfNeeded(*document); + URL url = request.resourceRequest().url(); - - LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload()); - - // If only the fragment identifiers differ, it is the same resource. - url = MemoryCache::removeFragmentIdentifierIfNeeded(url); - if (!url.isValid()) - return 0; + LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority() ? static_cast<int>(request.priority().value()) : -1, forPreload == ForPreload::Yes); - if (!canRequest(type, url, request.options(), request.forPreload())) - return 0; + if (!url.isValid()) { + RELEASE_LOG_IF_ALLOWED("requestResource: URL is invalid (frame = %p)", frame()); + return nullptr; + } - if (Frame* f = frame()) - f->loader().client().dispatchWillRequestResource(&request); + prepareFetch(type, request); + + // We are passing url as well as request, as request url may contain a fragment identifier. + if (!canRequest(type, url, request, forPreload)) { + RELEASE_LOG_IF_ALLOWED("requestResource: Not allowed to request resource (frame = %p)", frame()); + return nullptr; + } - if (memoryCache()->disabled()) { +#if ENABLE(CONTENT_EXTENSIONS) + if (frame() && frame()->mainFrame().page() && m_documentLoader) { + const auto& resourceRequest = request.resourceRequest(); + auto blockedStatus = frame()->mainFrame().page()->userContentProvider().processContentExtensionRulesForLoad(resourceRequest.url(), toResourceType(type), *m_documentLoader); + request.applyBlockedStatus(blockedStatus); + if (blockedStatus.blockedLoad) { + RELEASE_LOG_IF_ALLOWED("requestResource: Resource blocked by content blocker (frame = %p)", frame()); + if (type == CachedResource::Type::MainResource) { + auto resource = createResource(type, WTFMove(request), sessionID()); + ASSERT(resource); + resource->error(CachedResource::Status::LoadError); + resource->setResourceError(ResourceError(ContentExtensions::WebKitContentBlockerDomain, 0, resourceRequest.url(), WEB_UI_STRING("The URL was blocked by a content blocker", "WebKitErrorBlockedByContentBlocker description"))); + return resource; + } + return nullptr; + } + if (blockedStatus.madeHTTPS + && type == CachedResource::Type::MainResource + && m_documentLoader->isLoadingMainResource()) { + // This is to make sure the correct 'new' URL shows in the location bar. + m_documentLoader->frameLoader()->client().dispatchDidChangeProvisionalURL(); + } + url = request.resourceRequest().url(); // The content extension could have changed it from http to https. + url = MemoryCache::removeFragmentIdentifierIfNeeded(url); // Might need to remove fragment identifier again. + } +#endif + +#if ENABLE(WEB_TIMING) + LoadTiming loadTiming; + loadTiming.markStartTimeAndFetchStart(); + InitiatorContext initiatorContext = request.options().initiatorContext; +#endif + + if (request.resourceRequest().url().protocolIsInHTTPFamily()) + updateHTTPRequestHeaders(type, request); + + auto& memoryCache = MemoryCache::singleton(); + if (request.allowsCaching() && memoryCache.disabled()) { DocumentResourceMap::iterator it = m_documentResources.find(url.string()); if (it != m_documentResources.end()) { - it->value->setOwningCachedResourceLoader(0); + it->value->setOwningCachedResourceLoader(nullptr); m_documentResources.remove(it); } } // See if we can use an existing resource from the cache. CachedResourceHandle<CachedResource> resource; -#if ENABLE(CACHE_PARTITIONING) if (document()) - request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); -#endif + request.setDomainForCachePartition(*document()); + + if (request.allowsCaching()) + resource = memoryCache.resourceForRequest(request.resourceRequest(), sessionID()); + + if (resource && request.isLinkPreload() && !resource->isLinkPreload()) + resource->setLinkPreload(); - resource = memoryCache()->resourceForRequest(request.resourceRequest()); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheUsageKey(), resource ? DiagnosticLoggingKeys::inMemoryCacheKey() : DiagnosticLoggingKeys::notInMemoryCacheKey()); - const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); + RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, defer); switch (policy) { case Reload: - memoryCache()->remove(resource.get()); + memoryCache.remove(*resource); FALLTHROUGH; case Load: - resource = loadResource(type, request, request.charset()); + if (resource) + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::unusedKey()); + resource = loadResource(type, WTFMove(request)); break; case Revalidate: - resource = revalidateResource(request, resource.get()); + if (resource) + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::revalidatingKey()); + resource = revalidateResource(WTFMove(request), *resource); break; case Use: - if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(resource.get())) - return 0; - memoryCache()->resourceAccessed(resource.get()); + ASSERT(resource); + if (shouldUpdateCachedResourceWithCurrentRequest(*resource, request)) { + resource = updateCachedResourceWithCurrentRequest(*resource, WTFMove(request)); + if (resource->status() != CachedResource::Status::Cached) + policy = Load; + } else { + if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, resource.get())) + return nullptr; + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::usedKey()); + memoryCache.resourceAccessed(*resource); +#if ENABLE(WEB_TIMING) + loadTiming.setResponseEnd(MonotonicTime::now()); + + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) { + ResourceTiming resourceTiming = ResourceTiming::fromCache(url, request.initiatorName(), loadTiming); + if (initiatorContext == InitiatorContext::Worker) { + ASSERT(is<CachedRawResource>(resource.get())); + downcast<CachedRawResource>(resource.get())->finishedTimingForWorkerLoad(WTFMove(resourceTiming)); + } else if (document()) { + ASSERT(initiatorContext == InitiatorContext::Document); + m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, request.initiatorName(), frame()); + m_resourceTimingInfo.addResourceTiming(*resource.get(), *document(), WTFMove(resourceTiming)); + } + } +#endif + if (forPreload == ForPreload::No) + resource->setLoadPriority(request.priority()); + } break; } if (!resource) - return 0; + return nullptr; - if (!request.forPreload() || policy != Use) - resource->setLoadPriority(request.priority()); + if (forPreload == ForPreload::No && resource->loader() && resource->resourceRequest().ignoreForRequestCount()) { + resource->resourceRequest().setIgnoreForRequestCount(false); + incrementRequestCount(*resource); + } - if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) { - resource->load(this, request.options()); + if ((policy != Use || resource->stillNeedsLoad()) && defer == DeferOption::NoDefer) { + resource->load(*this); // We don't support immediate loads, but we do support immediate failure. if (resource->errorOccurred()) { - if (resource->inCache()) - memoryCache()->remove(resource.get()); - return 0; + if (resource->allowsCaching() && resource->inCache()) + memoryCache.remove(*resource); + return nullptr; } } - if (!request.resourceRequest().url().protocolIsData()) - m_validatedURLs.add(request.resourceRequest().url()); + if (document() && !document()->loadEventFinished() && !resource->resourceRequest().url().protocolIsData()) + m_validatedURLs.add(resource->resourceRequest().url()); ASSERT(resource->url() == url.string()); m_documentResources.set(resource->url(), resource); return resource; } -CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource) +void CachedResourceLoader::documentDidFinishLoadEvent() { - ASSERT(resource); - ASSERT(resource->inCache()); - ASSERT(!memoryCache()->disabled()); - ASSERT(resource->canUseCacheValidator()); - ASSERT(!resource->resourceToRevalidate()); - - // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below. - String url = resource->url(); - CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding()); - - LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); - newResource->setResourceToRevalidate(resource); - - memoryCache()->remove(resource); - memoryCache()->add(newResource.get()); -#if ENABLE(RESOURCE_TIMING) - storeResourceTimingInitiatorInformation(resource, request); -#else - UNUSED_PARAM(request); + m_validatedURLs.clear(); +} + +CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(CachedResourceRequest&& request, CachedResource& resource) +{ + ASSERT(resource.inCache()); + auto& memoryCache = MemoryCache::singleton(); + ASSERT(!memoryCache.disabled()); + ASSERT(resource.canUseCacheValidator()); + ASSERT(!resource.resourceToRevalidate()); + ASSERT(resource.sessionID() == sessionID()); + ASSERT(resource.allowsCaching()); + + CachedResourceHandle<CachedResource> newResource = createResource(resource.type(), WTFMove(request), resource.sessionID()); + + LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), &resource); + newResource->setResourceToRevalidate(&resource); + + memoryCache.remove(resource); + memoryCache.add(*newResource); +#if ENABLE(WEB_TIMING) + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) + m_resourceTimingInfo.storeResourceTimingInitiatorInformation(newResource, newResource->initiatorName(), frame()); #endif return newResource; } -CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request, const String& charset) +CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest&& request) { - ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest())); + auto& memoryCache = MemoryCache::singleton(); + ASSERT(!request.allowsCaching() || !memoryCache.resourceForRequest(request.resourceRequest(), sessionID()) + || request.resourceRequest().cachePolicy() == DoNotUseAnyCache || request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData || request.resourceRequest().cachePolicy() == RefreshAnyCacheData); LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data()); - CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), charset); + CachedResourceHandle<CachedResource> resource = createResource(type, WTFMove(request), sessionID()); - if (!memoryCache()->add(resource.get())) + if (resource->allowsCaching() && !memoryCache.add(*resource)) resource->setOwningCachedResourceLoader(this); -#if ENABLE(RESOURCE_TIMING) - storeResourceTimingInitiatorInformation(resource, request); +#if ENABLE(WEB_TIMING) + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) + m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, resource->initiatorName(), frame()); #endif return resource; } -#if ENABLE(RESOURCE_TIMING) -void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request) +static void logRevalidation(const String& reason, DiagnosticLoggingClient& logClient) { - if (resource->type() == CachedResource::MainResource) { - // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. - if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine()->committingFirstRealLoad()) { - InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() }; - m_initiatorMap.add(resource.get(), info); - } - } else { - InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() }; - m_initiatorMap.add(resource.get(), info); + logClient.logDiagnosticMessage(DiagnosticLoggingKeys::cachedResourceRevalidationReasonKey(), reason, ShouldSample::Yes); +} + +static void logResourceRevalidationDecision(CachedResource::RevalidationDecision reason, const Frame* frame) +{ + if (!frame || !frame->page()) + return; + auto& logClient = frame->page()->diagnosticLoggingClient(); + switch (reason) { + case CachedResource::RevalidationDecision::No: + break; + case CachedResource::RevalidationDecision::YesDueToExpired: + logRevalidation(DiagnosticLoggingKeys::isExpiredKey(), logClient); + break; + case CachedResource::RevalidationDecision::YesDueToNoStore: + logRevalidation(DiagnosticLoggingKeys::noStoreKey(), logClient); + break; + case CachedResource::RevalidationDecision::YesDueToNoCache: + logRevalidation(DiagnosticLoggingKeys::noCacheKey(), logClient); + break; + case CachedResource::RevalidationDecision::YesDueToCachePolicy: + logRevalidation(DiagnosticLoggingKeys::reloadKey(), logClient); + break; } } -#endif // ENABLE(RESOURCE_TIMING) -CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const +CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, DeferOption defer) const { + auto& request = cachedResourceRequest.resourceRequest(); + if (!existingResource) return Load; + if (request.cachePolicy() == DoNotUseAnyCache || request.cachePolicy() == ReloadIgnoringCacheData) + return Load; + + if (request.cachePolicy() == RefreshAnyCacheData) + return Reload; + // We already have a preload going for this URL. - if (forPreload && existingResource->isPreloaded()) + if (forPreload == ForPreload::Yes && existingResource->isPreloaded()) return Use; // If the same URL has been loaded as a different type, we need to reload. if (existingResource->type() != type) { LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch."); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonTypeMismatchKey()); return Reload; } - if (!existingResource->canReuse(request)) + if (!existingResource->varyHeaderValuesMatch(request)) return Reload; + auto* textDecoder = existingResource->textResourceDecoder(); + if (textDecoder && !textDecoder->hasEqualEncodingForCharset(cachedResourceRequest.charset())) + return Reload; + + // FIXME: We should use the same cache policy for all resource types. The raw resource policy is overly strict + // while the normal subresource policy is too loose. + if (existingResource->isMainOrMediaOrRawResource() && frame()) { + bool strictPolicyDisabled = frame()->loader().isStrictRawResourceValidationPolicyDisabledForTesting(); + bool canReuseRawResource = strictPolicyDisabled || downcast<CachedRawResource>(*existingResource).canReuse(request); + if (!canReuseRawResource) + return Reload; + } + // Conditional requests should have failed canReuse check. ASSERT(!request.isConditional()); - // Do not load from cache if images are not enabled. The load for this image will be blocked - // in CachedImage::load. - if (CachedResourceRequest::DeferredByClient == defer) + // Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load. + if (defer == DeferOption::DeferredByClient) return Reload; - - // Don't reload resources while pasting. - if (m_allowStaleResources) + + // Don't reload resources while pasting or if cache mode allows stale resources. + if (m_allowStaleResources || cachedResourceRequest.options().cache == FetchOptions::Cache::ForceCache || cachedResourceRequest.options().cache == FetchOptions::Cache::OnlyIfCached) return Use; - - // Alwaus use preloads. + + ASSERT(cachedResourceRequest.options().cache == FetchOptions::Cache::Default || cachedResourceRequest.options().cache == FetchOptions::Cache::NoCache); + + // Always use preloads. if (existingResource->isPreloaded()) return Use; - - // CachePolicyHistoryBuffer uses the cache no matter what. - if (cachePolicy(type) == CachePolicyHistoryBuffer) + + // We can find resources that are being validated from cache only when validation is just successfully completing. + if (existingResource->validationCompleting()) return Use; + ASSERT(!existingResource->validationInProgress()); + + // Validate the redirect chain. + bool cachePolicyIsHistoryBuffer = cachePolicy(type) == CachePolicyHistoryBuffer; + if (!existingResource->redirectChainAllowsReuse(cachePolicyIsHistoryBuffer ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection)) { + LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections."); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonRedirectChainKey()); + return Reload; + } + + // CachePolicyHistoryBuffer uses the cache except if this is a main resource with "cache-control: no-store". + if (cachePolicyIsHistoryBuffer) { + // FIXME: Ignoring "cache-control: no-cache" for sub-resources on history navigation but not the main + // resource is inconsistent. We should probably harmonize this. + if (!existingResource->response().cacheControlContainsNoStore() || type != CachedResource::MainResource) + return Use; + } // Don't reuse resources with Cache-control: no-store. if (existingResource->response().cacheControlContainsNoStore()) { LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store."); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonNoStoreKey()); return Reload; } @@ -600,6 +987,7 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida // client's requests are made without CORS and some with. if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) { LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings."); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonCredentialSettingsKey()); return Reload; } @@ -610,27 +998,40 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida // CachePolicyReload always reloads if (cachePolicy(type) == CachePolicyReload) { LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload."); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonReloadKey()); return Reload; } // We'll try to reload the resource if it failed last time. if (existingResource->errorOccurred()) { LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state"); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonErrorKey()); return Reload; } - - // For resources that are not yet loaded we ignore the cache policy. - if (existingResource->isLoading()) + + if (existingResource->isLoading()) { + // Do not use cached main resources that are still loading because sharing + // loading CachedResources in this case causes issues with regards to cancellation. + // If one of the DocumentLoader clients decides to cancel the load, then the load + // would be cancelled for all other DocumentLoaders as well. + if (type == CachedResource::Type::MainResource) + return Reload; + // For cached subresources that are still loading we ignore the cache policy. return Use; + } + + auto revalidationDecision = existingResource->makeRevalidationDecision(cachePolicy(type)); + logResourceRevalidationDecision(revalidationDecision, frame()); // Check if the cache headers requires us to revalidate (cache expiration for example). - if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy(type))) { + if (revalidationDecision != CachedResource::RevalidationDecision::No) { // See if the resource has usable ETag or Last-modified headers. if (existingResource->canUseCacheValidator()) return Revalidate; // No, must reload. - LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators."); + LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators."); + logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonMustRevalidateNoValidatorKey()); return Reload; } @@ -651,7 +1052,7 @@ void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const else message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n"; - frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); + frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); } void CachedResourceLoader::setAutoLoadImages(bool enable) @@ -680,72 +1081,72 @@ void CachedResourceLoader::setImagesEnabled(bool enable) reloadImagesIfNotDeferred(); } -bool CachedResourceLoader::clientDefersImage(const URL& url) const +bool CachedResourceLoader::clientDefersImage(const URL&) const +{ + return !m_imagesEnabled; +} + +bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const { - return frame() && !frame()->loader().client().allowImage(m_imagesEnabled, url); + return m_autoLoadImages || url.protocolIsData(); } bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const { - return clientDefersImage(url) || !m_autoLoadImages; + return clientDefersImage(url) || !shouldPerformImageLoad(url); } void CachedResourceLoader::reloadImagesIfNotDeferred() { - DocumentResourceMap::iterator end = m_documentResources.end(); - for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { - CachedResource* resource = it->value.get(); - if (resource->type() == CachedResource::ImageResource && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) - const_cast<CachedResource*>(resource)->load(this, defaultCachedResourceOptions()); + for (auto& resource : m_documentResources.values()) { + if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) + downcast<CachedImage>(*resource).load(*this); } } CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const { - if (!frame()) + Frame* frame = this->frame(); + if (!frame) return CachePolicyVerify; if (type != CachedResource::MainResource) - return frame()->loader().subresourceCachePolicy(); - - if (frame()->loader().loadType() == FrameLoadTypeReloadFromOrigin || frame()->loader().loadType() == FrameLoadTypeReload) + return frame->loader().subresourceCachePolicy(); + + if (Page* page = frame->page()) { + if (page->isResourceCachingDisabled()) + return CachePolicyReload; + } + + switch (frame->loader().loadType()) { + case FrameLoadType::ReloadFromOrigin: + case FrameLoadType::Reload: return CachePolicyReload; - return CachePolicyVerify; + case FrameLoadType::Back: + case FrameLoadType::Forward: + case FrameLoadType::IndexedBackForward: + // Do not revalidate cached main resource on back/forward navigation. + return CachePolicyHistoryBuffer; + default: + return CachePolicyVerify; + } } -void CachedResourceLoader::removeCachedResource(CachedResource* resource) const +void CachedResourceLoader::removeCachedResource(CachedResource& resource) { #ifndef NDEBUG - DocumentResourceMap::iterator it = m_documentResources.find(resource->url()); + DocumentResourceMap::iterator it = m_documentResources.find(resource.url()); if (it != m_documentResources.end()) - ASSERT(it->value.get() == resource); + ASSERT(it->value.get() == &resource); #endif - m_documentResources.remove(resource->url()); + m_documentResources.remove(resource.url()); } -void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerformPostLoadActions) +void CachedResourceLoader::loadDone(bool shouldPerformPostLoadActions) { RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader); RefPtr<Document> protectDocument(m_document); -#if ENABLE(RESOURCE_TIMING) - if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304)) { - HashMap<CachedResource*, InitiatorInfo>::iterator initiatorIt = m_initiatorMap.find(resource); - if (initiatorIt != m_initiatorMap.end()) { - ASSERT(document()); - Document* initiatorDocument = document(); - if (resource->type() == CachedResource::MainResource) - initiatorDocument = document()->parentDocument(); - ASSERT(initiatorDocument); - const InitiatorInfo& info = initiatorIt->value; - initiatorDocument->domWindow()->performance()->addResourceTiming(info.name, initiatorDocument, resource->resourceRequest(), resource->response(), info.startTime, resource->loadFinishTime()); - m_initiatorMap.remove(initiatorIt); - } - } -#else - UNUSED_PARAM(resource); -#endif // ENABLE(RESOURCE_TIMING) - if (frame()) frame()->loader().loadDone(); @@ -762,111 +1163,65 @@ void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerform // bookkeeping on CachedResources, so instead pseudo-GC them -- when the // reference count reaches 1, m_documentResources is the only reference, so // remove it from the map. -void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>& timer) -{ - ASSERT_UNUSED(timer, &timer == &m_garbageCollectDocumentResourcesTimer); - garbageCollectDocumentResources(); -} - void CachedResourceLoader::garbageCollectDocumentResources() { typedef Vector<String, 10> StringVector; StringVector resourcesToDelete; - for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) { - if (it->value->hasOneHandle()) { - resourcesToDelete.append(it->key); - it->value->setOwningCachedResourceLoader(0); + for (auto& resource : m_documentResources) { + if (resource.value->hasOneHandle()) { + resourcesToDelete.append(resource.key); + resource.value->setOwningCachedResourceLoader(nullptr); } } - for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it) - m_documentResources.remove(*it); + for (auto& resource : resourcesToDelete) + m_documentResources.remove(resource); } void CachedResourceLoader::performPostLoadActions() { - checkForPendingPreloads(); - - platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests(); + platformStrategies()->loaderStrategy()->servePendingRequests(); } -void CachedResourceLoader::incrementRequestCount(const CachedResource* res) +void CachedResourceLoader::incrementRequestCount(const CachedResource& resource) { - if (res->ignoreForRequestCount()) + if (resource.ignoreForRequestCount()) return; ++m_requestCount; } -void CachedResourceLoader::decrementRequestCount(const CachedResource* res) +void CachedResourceLoader::decrementRequestCount(const CachedResource& resource) { - if (res->ignoreForRequestCount()) + if (resource.ignoreForRequestCount()) return; --m_requestCount; ASSERT(m_requestCount > -1); } -void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset) +CachedResourceHandle<CachedResource> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request) { - // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>. - // FIXME: We should consider adding a setting to toggle aggressive preloading behavior as opposed - // to making this behavior specific to iOS. -#if !PLATFORM(IOS) - bool hasRendering = m_document->body() && m_document->renderView(); - bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet; - if (!hasRendering && !canBlockParser) { - // Don't preload subresources that can't block the parser before we have something to draw. - // This helps prevent preloads from delaying first display when bandwidth is limited. - PendingPreload pendingPreload = { type, request, charset }; - m_pendingPreloads.append(pendingPreload); - return; - } -#endif - requestPreload(type, request, charset); -} - -void CachedResourceLoader::checkForPendingPreloads() -{ - if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer()) - return; -#if PLATFORM(IOS) - // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>. - // So, we should never have any pending preloads. - // FIXME: We should look to avoid compiling this code entirely when building for iOS. - ASSERT_NOT_REACHED(); -#endif - while (!m_pendingPreloads.isEmpty()) { - PendingPreload preload = m_pendingPreloads.takeFirst(); - // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored). - if (!cachedResource(preload.m_request.resourceRequest().url())) - requestPreload(preload.m_type, preload.m_request, preload.m_charset); - } - m_pendingPreloads.clear(); -} + if (request.charset().isEmpty() && (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)) + request.setCharset(m_document->charset()); -void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset) -{ - String encoding; - if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet) - encoding = charset.isEmpty() ? m_document->charset() : charset; - - request.setCharset(encoding); - request.setForPreload(true); - - CachedResourceHandle<CachedResource> resource = requestResource(type, request); + CachedResourceHandle<CachedResource> resource = requestResource(type, WTFMove(request), ForPreload::Yes); if (!resource || (m_preloads && m_preloads->contains(resource.get()))) - return; + return nullptr; + // Fonts need special treatment since just creating the resource doesn't trigger a load. + if (type == CachedResource::FontResource) + downcast<CachedFont>(resource.get())->beginLoadIfNeeded(*this); resource->increasePreloadCount(); if (!m_preloads) - m_preloads = adoptPtr(new ListHashSet<CachedResource*>); + m_preloads = std::make_unique<ListHashSet<CachedResource*>>(); m_preloads->add(resource.get()); #if PRELOAD_DEBUG printf("PRELOADING %s\n", resource->url().latin1().data()); #endif + return resource; } bool CachedResourceLoader::isPreloaded(const String& urlString) const @@ -874,24 +1229,15 @@ bool CachedResourceLoader::isPreloaded(const String& urlString) const const URL& url = m_document->completeURL(urlString); if (m_preloads) { - ListHashSet<CachedResource*>::iterator end = m_preloads->end(); - for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) { - CachedResource* resource = *it; + for (auto& resource : *m_preloads) { if (resource->url() == url) return true; } } - - Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end(); - for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) { - PendingPreload pendingPreload = *it; - if (pendingPreload.m_request.resourceRequest().url() == url) - return true; - } return false; } -void CachedResourceLoader::clearPreloads() +void CachedResourceLoader::clearPreloads(ClearPreloadsMode mode) { #if PRELOAD_DEBUG printPreloadStats(); @@ -899,20 +1245,21 @@ void CachedResourceLoader::clearPreloads() if (!m_preloads) return; - ListHashSet<CachedResource*>::iterator end = m_preloads->end(); - for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) { - CachedResource* res = *it; - res->decreasePreloadCount(); - bool deleted = res->deleteIfPossible(); - if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced) - memoryCache()->remove(res); + std::unique_ptr<ListHashSet<CachedResource*>> remainingLinkPreloads; + for (auto* resource : *m_preloads) { + ASSERT(resource); + if (mode == ClearPreloadsMode::ClearSpeculativePreloads && resource->isLinkPreload()) { + if (!remainingLinkPreloads) + remainingLinkPreloads = std::make_unique<ListHashSet<CachedResource*>>(); + remainingLinkPreloads->add(resource); + continue; + } + resource->decreasePreloadCount(); + bool deleted = resource->deleteIfPossible(); + if (!deleted && resource->preloadResult() == CachedResource::PreloadNotReferenced) + MemoryCache::singleton().remove(*resource); } - m_preloads.clear(); -} - -void CachedResourceLoader::clearPendingPreloads() -{ - m_pendingPreloads.clear(); + m_preloads = WTFMove(remainingLinkPreloads); } #if PRELOAD_DEBUG @@ -924,37 +1271,35 @@ void CachedResourceLoader::printPreloadStats() unsigned stylesheetMisses = 0; unsigned images = 0; unsigned imageMisses = 0; - ListHashSet<CachedResource*>::iterator end = m_preloads.end(); - for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) { - CachedResource* res = *it; - if (res->preloadResult() == CachedResource::PreloadNotReferenced) - printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data()); - else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete) - printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data()); - else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading) - printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data()); - - if (res->type() == CachedResource::Script) { + for (auto& resource : m_preloads) { + if (resource->preloadResult() == CachedResource::PreloadNotReferenced) + printf("!! UNREFERENCED PRELOAD %s\n", resource->url().latin1().data()); + else if (resource->preloadResult() == CachedResource::PreloadReferencedWhileComplete) + printf("HIT COMPLETE PRELOAD %s\n", resource->url().latin1().data()); + else if (resource->preloadResult() == CachedResource::PreloadReferencedWhileLoading) + printf("HIT LOADING PRELOAD %s\n", resource->url().latin1().data()); + + if (resource->type() == CachedResource::Script) { scripts++; - if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) + if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading) scriptMisses++; - } else if (res->type() == CachedResource::CSSStyleSheet) { + } else if (resource->type() == CachedResource::CSSStyleSheet) { stylesheets++; - if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) + if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading) stylesheetMisses++; } else { images++; - if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) + if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading) imageMisses++; } - - if (res->errorOccurred()) - memoryCache()->remove(res); - - res->decreasePreloadCount(); + + if (resource->errorOccurred() && resource->preloadResult() == CachedResource::PreloadNotReferenced) + MemoryCache::singleton().remove(resource); + + resource->decreasePreloadCount(); } - m_preloads.clear(); - + m_preloads = nullptr; + if (scripts) printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts); if (stylesheets) @@ -966,8 +1311,13 @@ void CachedResourceLoader::printPreloadStats() const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions() { - static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType); + static NeverDestroyed<ResourceLoaderOptions> options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientCredentialPolicy::MayAskClientForCredentials, FetchOptions::Credentials::Include, DoSecurityCheck, FetchOptions::Mode::NoCors, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, CachingPolicy::AllowCaching); return options; } +bool CachedResourceLoader::isAlwaysOnLoggingAllowed() const +{ + return m_documentLoader ? m_documentLoader->isAlwaysOnLoggingAllowed() : true; +} + } diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.h b/Source/WebCore/loader/cache/CachedResourceLoader.h index bcc793e0c..23eb1dd6f 100644 --- a/Source/WebCore/loader/cache/CachedResourceLoader.h +++ b/Source/WebCore/loader/cache/CachedResourceLoader.h @@ -23,14 +23,15 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef CachedResourceLoader_h -#define CachedResourceLoader_h +#pragma once #include "CachePolicy.h" #include "CachedResource.h" #include "CachedResourceHandle.h" #include "CachedResourceRequest.h" +#include "ContentSecurityPolicy.h" #include "ResourceLoadPriority.h" +#include "ResourceTimingInformation.h" #include "Timer.h" #include <wtf/Deque.h> #include <wtf/HashMap.h> @@ -68,28 +69,26 @@ friend class ImageLoader; friend class ResourceCacheValidationSuppressor; public: - static PassRef<CachedResourceLoader> create(DocumentLoader* documentLoader) { return adoptRef(*new CachedResourceLoader(documentLoader)); } + static Ref<CachedResourceLoader> create(DocumentLoader* documentLoader) { return adoptRef(*new CachedResourceLoader(documentLoader)); } ~CachedResourceLoader(); - CachedResourceHandle<CachedImage> requestImage(CachedResourceRequest&); - CachedResourceHandle<CachedCSSStyleSheet> requestCSSStyleSheet(CachedResourceRequest&); - CachedResourceHandle<CachedCSSStyleSheet> requestUserCSSStyleSheet(CachedResourceRequest&); - CachedResourceHandle<CachedScript> requestScript(CachedResourceRequest&); - CachedResourceHandle<CachedFont> requestFont(CachedResourceRequest&); - CachedResourceHandle<CachedRawResource> requestRawResource(CachedResourceRequest&); - CachedResourceHandle<CachedRawResource> requestMainResource(CachedResourceRequest&); - -#if ENABLE(SVG) - CachedResourceHandle<CachedSVGDocument> requestSVGDocument(CachedResourceRequest&); -#endif + CachedResourceHandle<CachedImage> requestImage(CachedResourceRequest&&); + CachedResourceHandle<CachedCSSStyleSheet> requestCSSStyleSheet(CachedResourceRequest&&); + CachedResourceHandle<CachedCSSStyleSheet> requestUserCSSStyleSheet(CachedResourceRequest&&); + CachedResourceHandle<CachedScript> requestScript(CachedResourceRequest&&); + CachedResourceHandle<CachedFont> requestFont(CachedResourceRequest&&, bool isSVG); + CachedResourceHandle<CachedRawResource> requestMedia(CachedResourceRequest&&); + CachedResourceHandle<CachedRawResource> requestRawResource(CachedResourceRequest&&); + CachedResourceHandle<CachedRawResource> requestMainResource(CachedResourceRequest&&); + CachedResourceHandle<CachedSVGDocument> requestSVGDocument(CachedResourceRequest&&); #if ENABLE(XSLT) - CachedResourceHandle<CachedXSLStyleSheet> requestXSLStyleSheet(CachedResourceRequest&); + CachedResourceHandle<CachedXSLStyleSheet> requestXSLStyleSheet(CachedResourceRequest&&); #endif #if ENABLE(LINK_PREFETCH) - CachedResourceHandle<CachedResource> requestLinkResource(CachedResource::Type, CachedResourceRequest&); + CachedResourceHandle<CachedResource> requestLinkResource(CachedResource::Type, CachedResourceRequest&&); #endif #if ENABLE(VIDEO_TRACK) - CachedResourceHandle<CachedTextTrack> requestTextTrack(CachedResourceRequest&); + CachedResourceHandle<CachedTextTrack> requestTextTrack(CachedResourceRequest&&); #endif // Logs an access denied message to the console for the specified URL. @@ -97,67 +96,89 @@ public: CachedResource* cachedResource(const String& url) const; CachedResource* cachedResource(const URL& url) const; - + typedef HashMap<String, CachedResourceHandle<CachedResource>> DocumentResourceMap; const DocumentResourceMap& allCachedResources() const { return m_documentResources; } bool autoLoadImages() const { return m_autoLoadImages; } void setAutoLoadImages(bool); + bool imagesEnabled() const { return m_imagesEnabled; } void setImagesEnabled(bool); bool shouldDeferImageLoad(const URL&) const; + bool shouldPerformImageLoad(const URL&) const; CachePolicy cachePolicy(CachedResource::Type) const; Frame* frame() const; // Can be null Document* document() const { return m_document; } // Can be null void setDocument(Document* document) { m_document = document; } - void clearDocumentLoader() { m_documentLoader = 0; } + void clearDocumentLoader() { m_documentLoader = nullptr; } + SessionID sessionID() const; - void removeCachedResource(CachedResource*) const; + void removeCachedResource(CachedResource&); - void loadDone(CachedResource*, bool shouldPerformPostLoadActions = true); + void loadDone(bool shouldPerformPostLoadActions = true); - void garbageCollectDocumentResources(); - - void incrementRequestCount(const CachedResource*); - void decrementRequestCount(const CachedResource*); + WEBCORE_EXPORT void garbageCollectDocumentResources(); + + void incrementRequestCount(const CachedResource&); + void decrementRequestCount(const CachedResource&); int requestCount() const { return m_requestCount; } - bool isPreloaded(const String& urlString) const; - void clearPreloads(); - void clearPendingPreloads(); - void preload(CachedResource::Type, CachedResourceRequest&, const String& charset); - void checkForPendingPreloads(); + WEBCORE_EXPORT bool isPreloaded(const String& urlString) const; + enum class ClearPreloadsMode { ClearSpeculativePreloads, ClearAllPreloads }; + void clearPreloads(ClearPreloadsMode); + CachedResourceHandle<CachedResource> preload(CachedResource::Type, CachedResourceRequest&&); void printPreloadStats(); - bool canRequest(CachedResource::Type, const URL&, const ResourceLoaderOptions&, bool forPreload = false); + + bool updateRequestAfterRedirection(CachedResource::Type, ResourceRequest&, const ResourceLoaderOptions&); static const ResourceLoaderOptions& defaultCachedResourceOptions(); + void documentDidFinishLoadEvent(); + +#if ENABLE(WEB_TIMING) + ResourceTimingInformation& resourceTimingInformation() { return m_resourceTimingInfo; } +#endif + + bool isAlwaysOnLoggingAllowed() const; + private: explicit CachedResourceLoader(DocumentLoader*); - CachedResourceHandle<CachedResource> requestResource(CachedResource::Type, CachedResourceRequest&); - CachedResourceHandle<CachedResource> revalidateResource(const CachedResourceRequest&, CachedResource*); - CachedResourceHandle<CachedResource> loadResource(CachedResource::Type, CachedResourceRequest&, const String& charset); -#if ENABLE(RESOURCE_TIMING) - void storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>&, const CachedResourceRequest&); -#endif - void requestPreload(CachedResource::Type, CachedResourceRequest&, const String& charset); + enum class ForPreload { Yes, No }; + enum class DeferOption { NoDefer, DeferredByClient }; + + CachedResourceHandle<CachedResource> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, DeferOption = DeferOption::NoDefer); + CachedResourceHandle<CachedResource> revalidateResource(CachedResourceRequest&&, CachedResource&); + CachedResourceHandle<CachedResource> loadResource(CachedResource::Type, CachedResourceRequest&&); + + void prepareFetch(CachedResource::Type, CachedResourceRequest&); + void updateHTTPRequestHeaders(CachedResource::Type, CachedResourceRequest&); + void updateReferrerOriginAndUserAgentHeaders(CachedResourceRequest&); + + bool canRequest(CachedResource::Type, const URL&, const CachedResourceRequest&, ForPreload); enum RevalidationPolicy { Use, Revalidate, Reload, Load }; - RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, ResourceRequest&, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption) const; - - bool shouldContinueAfterNotifyingLoadedFromMemoryCache(CachedResource*); + RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, CachedResourceRequest&, CachedResource* existingResource, ForPreload, DeferOption) const; + + bool shouldUpdateCachedResourceWithCurrentRequest(const CachedResource&, const CachedResourceRequest&); + CachedResourceHandle<CachedResource> updateCachedResourceWithCurrentRequest(const CachedResource&, CachedResourceRequest&&); + + bool shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest&, CachedResource*); bool checkInsecureContent(CachedResource::Type, const URL&) const; + bool allowedByContentSecurityPolicy(CachedResource::Type, const URL&, const ResourceLoaderOptions&, ContentSecurityPolicy::RedirectResponseReceived) const; - void garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>&); void performPostLoadActions(); bool clientDefersImage(const URL&) const; void reloadImagesIfNotDeferred(); - + + bool canRequestAfterRedirection(CachedResource::Type, const URL&, const ResourceLoaderOptions&) const; + bool canRequestInContentDispositionAttachmentSandbox(CachedResource::Type, const URL&) const; + HashSet<String> m_validatedURLs; mutable DocumentResourceMap m_documentResources; Document* m_document; @@ -165,22 +186,12 @@ private: int m_requestCount; - OwnPtr<ListHashSet<CachedResource*>> m_preloads; - struct PendingPreload { - CachedResource::Type m_type; - CachedResourceRequest m_request; - String m_charset; - }; - Deque<PendingPreload> m_pendingPreloads; - - Timer<CachedResourceLoader> m_garbageCollectDocumentResourcesTimer; - -#if ENABLE(RESOURCE_TIMING) - struct InitiatorInfo { - AtomicString name; - double startTime; - }; - HashMap<CachedResource*, InitiatorInfo> m_initiatorMap; + std::unique_ptr<ListHashSet<CachedResource*>> m_preloads; + + Timer m_garbageCollectDocumentResourcesTimer; + +#if ENABLE(WEB_TIMING) + ResourceTimingInformation m_resourceTimingInfo; #endif // 29 bits left @@ -193,25 +204,19 @@ class ResourceCacheValidationSuppressor { WTF_MAKE_NONCOPYABLE(ResourceCacheValidationSuppressor); WTF_MAKE_FAST_ALLOCATED; public: - ResourceCacheValidationSuppressor(CachedResourceLoader* loader) + ResourceCacheValidationSuppressor(CachedResourceLoader& loader) : m_loader(loader) - , m_previousState(false) + , m_previousState(m_loader.m_allowStaleResources) { - if (m_loader) { - m_previousState = m_loader->m_allowStaleResources; - m_loader->m_allowStaleResources = true; - } + m_loader.m_allowStaleResources = true; } ~ResourceCacheValidationSuppressor() { - if (m_loader) - m_loader->m_allowStaleResources = m_previousState; + m_loader.m_allowStaleResources = m_previousState; } private: - CachedResourceLoader* m_loader; + CachedResourceLoader& m_loader; bool m_previousState; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/loader/cache/CachedResourceRequest.cpp b/Source/WebCore/loader/cache/CachedResourceRequest.cpp index 55a493859..a5ac5637a 100644 --- a/Source/WebCore/loader/cache/CachedResourceRequest.cpp +++ b/Source/WebCore/loader/cache/CachedResourceRequest.cpp @@ -13,7 +13,7 @@ * 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 COMPUTER, INC. OR + * 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 @@ -27,52 +27,49 @@ #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 "MemoryCache.h" +#include "SecurityPolicy.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { -CachedResourceRequest::CachedResourceRequest(const ResourceRequest& resourceRequest, const String& charset, ResourceLoadPriority priority) - : m_resourceRequest(resourceRequest) - , m_charset(charset) - , m_options(CachedResourceLoader::defaultCachedResourceOptions()) - , m_priority(priority) - , m_forPreload(false) - , m_defer(NoDefer) -{ -} - -CachedResourceRequest::CachedResourceRequest(const ResourceRequest& resourceRequest, const ResourceLoaderOptions& options) - : m_resourceRequest(resourceRequest) +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(ResourceLoadPriorityUnresolved) - , m_forPreload(false) - , m_defer(NoDefer) -{ -} - -CachedResourceRequest::CachedResourceRequest(const ResourceRequest& resourceRequest, ResourceLoadPriority priority) - : m_resourceRequest(resourceRequest) - , m_options(CachedResourceLoader::defaultCachedResourceOptions()) , m_priority(priority) - , m_forPreload(false) - , m_defer(NoDefer) + , m_fragmentIdentifier(splitFragmentIdentifierFromRequestURL(m_resourceRequest)) { } -CachedResourceRequest::~CachedResourceRequest() +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(PassRefPtr<Element> element) +void CachedResourceRequest::setInitiator(Element& element) { - ASSERT(!m_initiatorElement && m_initiatorName.isEmpty()); - m_initiatorElement = element; + ASSERT(!m_initiatorElement); + ASSERT(m_initiatorName.isEmpty()); + m_initiatorElement = &element; } void CachedResourceRequest::setInitiator(const AtomicString& name) { - ASSERT(!m_initiatorElement && m_initiatorName.isEmpty()); + ASSERT(!m_initiatorElement); + ASSERT(m_initiatorName.isEmpty()); m_initiatorName = name; } @@ -83,8 +80,198 @@ const AtomicString& CachedResourceRequest::initiatorName() const if (!m_initiatorName.isEmpty()) return m_initiatorName; - DEFINE_STATIC_LOCAL(AtomicString, defaultName, ("resource", AtomicString::ConstructFromLiteral)); + 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.allowCredentials = credentials == FetchOptions::Credentials::Include ? AllowStoredCredentials : DoNotAllowStoredCredentials; + WebCore::updateRequestForAccessControl(m_resourceRequest, document.securityOrigin(), m_options.allowCredentials); +} + +void CachedResourceRequest::updateForAccessControl(Document& document) +{ + ASSERT(m_options.mode == FetchOptions::Mode::Cors); + + m_origin = &document.securityOrigin(); + WebCore::updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.allowCredentials); +} + +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.topOrigin().domainForCachePartition()); +} + +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: + 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) +{ + ContentExtensions::applyBlockedStatusToRequest(blockedStatus, m_resourceRequest); +} + +#endif + +void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(FrameLoader& frameLoader, ReferrerPolicy defaultPolicy) +{ + // Implementing step 7 to 9 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch + + String outgoingOrigin; + String outgoingReferrer = m_resourceRequest.httpReferrer(); + if (!outgoingReferrer.isNull()) + outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString(); + else { + outgoingReferrer = frameLoader.outgoingReferrer(); + outgoingOrigin = frameLoader.outgoingOrigin(); + } + + // FIXME: Refactor SecurityPolicy::generateReferrerHeader to align with new terminology used in https://w3c.github.io/webappsec-referrer-policy. + switch (m_options.referrerPolicy) { + case FetchOptions::ReferrerPolicy::EmptyString: { + outgoingReferrer = SecurityPolicy::generateReferrerHeader(defaultPolicy, m_resourceRequest.url(), outgoingReferrer); + break; } + case FetchOptions::ReferrerPolicy::NoReferrerWhenDowngrade: + outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Default, m_resourceRequest.url(), outgoingReferrer); + break; + case FetchOptions::ReferrerPolicy::NoReferrer: + outgoingReferrer = String(); + break; + case FetchOptions::ReferrerPolicy::Origin: + outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer); + break; + case FetchOptions::ReferrerPolicy::OriginWhenCrossOrigin: + if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options)) + outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer); + break; + case FetchOptions::ReferrerPolicy::UnsafeUrl: + break; + }; + + if (outgoingReferrer.isEmpty()) + m_resourceRequest.clearHTTPReferrer(); + else + m_resourceRequest.setHTTPReferrer(outgoingReferrer); + FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin); + + frameLoader.applyUserAgent(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); +} + } // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedResourceRequest.h b/Source/WebCore/loader/cache/CachedResourceRequest.h index 0122c9958..1030b3660 100644 --- a/Source/WebCore/loader/cache/CachedResourceRequest.h +++ b/Source/WebCore/loader/cache/CachedResourceRequest.h @@ -13,7 +13,7 @@ * 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 COMPUTER, INC. OR + * 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 @@ -23,55 +23,83 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedResourceRequest_h -#define CachedResourceRequest_h +#pragma once +#include "CachedResource.h" +#include "DocumentLoader.h" #include "Element.h" #include "ResourceLoadPriority.h" #include "ResourceLoaderOptions.h" #include "ResourceRequest.h" +#include "SecurityOrigin.h" #include <wtf/RefPtr.h> #include <wtf/text/AtomicString.h> namespace WebCore { + +namespace ContentExtensions { +struct BlockedStatus; +} + class Document; +class FrameLoader; +enum class ReferrerPolicy; + +bool isRequestCrossOrigin(SecurityOrigin*, const URL& requestURL, const ResourceLoaderOptions&); class CachedResourceRequest { public: - enum DeferOption { NoDefer, DeferredByClient }; - - explicit CachedResourceRequest(const ResourceRequest&, const String& charset = String(), ResourceLoadPriority = ResourceLoadPriorityUnresolved); - CachedResourceRequest(const ResourceRequest&, const ResourceLoaderOptions&); - CachedResourceRequest(const ResourceRequest&, ResourceLoadPriority); - ~CachedResourceRequest(); + CachedResourceRequest(ResourceRequest&&, const ResourceLoaderOptions&, std::optional<ResourceLoadPriority> = std::nullopt, String&& charset = String()); - ResourceRequest& mutableResourceRequest() { return m_resourceRequest; } + ResourceRequest&& releaseResourceRequest() { return WTFMove(m_resourceRequest); } const ResourceRequest& resourceRequest() const { return m_resourceRequest; } const String& charset() const { return m_charset; } void setCharset(const String& charset) { m_charset = charset; } const ResourceLoaderOptions& options() const { return m_options; } void setOptions(const ResourceLoaderOptions& options) { m_options = options; } - void setPriority(ResourceLoadPriority priority) { m_priority = priority; } - ResourceLoadPriority priority() const { return m_priority; } - bool forPreload() const { return m_forPreload; } - void setForPreload(bool forPreload) { m_forPreload = forPreload; } - DeferOption defer() const { return m_defer; } - void setDefer(DeferOption defer) { m_defer = defer; } - void setInitiator(PassRefPtr<Element>); + const std::optional<ResourceLoadPriority>& priority() const { return m_priority; } + void setInitiator(Element&); void setInitiator(const AtomicString& name); const AtomicString& initiatorName() const; + bool allowsCaching() const { return m_options.cachingPolicy == CachingPolicy::AllowCaching; } + void setCachingPolicy(CachingPolicy policy) { m_options.cachingPolicy = policy; } + + void setAsPotentiallyCrossOrigin(const String&, Document&); + void updateForAccessControl(Document&); + + void updateReferrerOriginAndUserAgentHeaders(FrameLoader&, ReferrerPolicy); + void upgradeInsecureRequestIfNeeded(Document&); + void setAcceptHeaderIfNone(CachedResource::Type); + void updateAccordingCacheMode(); + void removeFragmentIdentifierIfNeeded(); +#if ENABLE(CONTENT_EXTENSIONS) + void applyBlockedStatus(const ContentExtensions::BlockedStatus&); +#endif + void setDomainForCachePartition(Document&); + bool isLinkPreload() const { return m_isLinkPreload; } + void setIsLinkPreload() { m_isLinkPreload = true; } + + void setOrigin(Ref<SecurityOrigin>&& origin) { m_origin = WTFMove(origin); } + RefPtr<SecurityOrigin> releaseOrigin() { return WTFMove(m_origin); } + SecurityOrigin* origin() const { return m_origin.get(); } + + String&& releaseFragmentIdentifier() { return WTFMove(m_fragmentIdentifier); } + void clearFragmentIdentifier() { m_fragmentIdentifier = { }; } + + static String splitFragmentIdentifierFromRequestURL(ResourceRequest&); private: ResourceRequest m_resourceRequest; String m_charset; ResourceLoaderOptions m_options; - ResourceLoadPriority m_priority; - bool m_forPreload; - DeferOption m_defer; + std::optional<ResourceLoadPriority> m_priority; RefPtr<Element> m_initiatorElement; AtomicString m_initiatorName; + RefPtr<SecurityOrigin> m_origin; + String m_fragmentIdentifier; + bool m_isLinkPreload { false }; }; -} // namespace WebCore +void upgradeInsecureResourceRequestIfNeeded(ResourceRequest&, Document&); -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp index 91a17b66b..b339a8e58 100644 --- a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp +++ b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp @@ -13,7 +13,7 @@ * 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 COMPUTER, INC. OR + * 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 @@ -30,6 +30,7 @@ namespace WebCore { CachedResourceRequestInitiators::CachedResourceRequestInitiators() : css("css", AtomicString::ConstructFromLiteral) + , fetch("fetch", AtomicString::ConstructFromLiteral) , icon("icon", AtomicString::ConstructFromLiteral) , xmlhttprequest("xmlhttprequest", AtomicString::ConstructFromLiteral) { diff --git a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h index 24ac2387d..50538cafd 100644 --- a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h +++ b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h @@ -13,7 +13,7 @@ * 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 COMPUTER, INC. OR + * 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedResourceRequestInitiators_h -#define CachedResourceRequestInitiators_h +#pragma once #include "ThreadGlobalData.h" #include <wtf/text/AtomicString.h> @@ -32,12 +31,14 @@ namespace WebCore { struct CachedResourceRequestInitiators { + CachedResourceRequestInitiators(); + const AtomicString css; + const AtomicString fetch; const AtomicString icon; const AtomicString xmlhttprequest; WTF_MAKE_NONCOPYABLE(CachedResourceRequestInitiators); WTF_MAKE_FAST_ALLOCATED; private: - CachedResourceRequestInitiators(); friend class ThreadGlobalData; }; @@ -47,5 +48,3 @@ inline const CachedResourceRequestInitiators& cachedResourceRequestInitiators() } } // namespace WebCore - -#endif diff --git a/Source/WebCore/loader/cache/CachedSVGDocument.cpp b/Source/WebCore/loader/cache/CachedSVGDocument.cpp index 15946263f..08b40169d 100644 --- a/Source/WebCore/loader/cache/CachedSVGDocument.cpp +++ b/Source/WebCore/loader/cache/CachedSVGDocument.cpp @@ -21,22 +21,16 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "CachedSVGDocument.h" -#include "CachedResourceClient.h" -#include "CachedResourceHandle.h" -#include "ResourceBuffer.h" -#include <wtf/text/StringBuilder.h> +#include "SharedBuffer.h" namespace WebCore { -CachedSVGDocument::CachedSVGDocument(const ResourceRequest& request) - : CachedResource(request, SVGDocumentResource) +CachedSVGDocument::CachedSVGDocument(CachedResourceRequest&& request, SessionID sessionID) + : CachedResource(WTFMove(request), SVGDocumentResource, sessionID) , m_decoder(TextResourceDecoder::create("application/xml")) { - setAccept("image/svg+xml"); } CachedSVGDocument::~CachedSVGDocument() @@ -53,20 +47,14 @@ String CachedSVGDocument::encoding() const return m_decoder->encoding().name(); } -void CachedSVGDocument::finishLoading(ResourceBuffer* data) +void CachedSVGDocument::finishLoading(SharedBuffer* data) { if (data) { - StringBuilder decodedText; - decodedText.append(m_decoder->decode(data->data(), data->size())); - decodedText.append(m_decoder->flush()); // We don't need to create a new frame because the new document belongs to the parent UseElement. - m_document = SVGDocument::create(0, response().url()); - m_document->setContent(decodedText.toString()); + m_document = SVGDocument::create(nullptr, response().url()); + m_document->setContent(m_decoder->decodeAndFlush(data->data(), data->size())); } CachedResource::finishLoading(data); } } - -#endif - diff --git a/Source/WebCore/loader/cache/CachedSVGDocument.h b/Source/WebCore/loader/cache/CachedSVGDocument.h index d6a6f5d4e..7981f1de0 100644 --- a/Source/WebCore/loader/cache/CachedSVGDocument.h +++ b/Source/WebCore/loader/cache/CachedSVGDocument.h @@ -20,10 +20,8 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedSVGDocument_h -#define CachedSVGDocument_h +#pragma once -#if ENABLE(SVG) #include "CachedResource.h" #include "CachedResourceHandle.h" #include "SVGDocument.h" @@ -33,24 +31,22 @@ namespace WebCore { class CachedSVGDocument final : public CachedResource { public: - explicit CachedSVGDocument(const ResourceRequest&); + explicit CachedSVGDocument(CachedResourceRequest&&, SessionID); virtual ~CachedSVGDocument(); SVGDocument* document() const { return m_document.get(); } -protected: +private: + bool mayTryReplaceEncodedData() const override { return true; } + void setEncoding(const String&) override; + String encoding() const override; + const TextResourceDecoder* textResourceDecoder() const override { return m_decoder.get(); } + void finishLoading(SharedBuffer*) override; + RefPtr<SVGDocument> m_document; RefPtr<TextResourceDecoder> m_decoder; - -private: - virtual bool mayTryReplaceEncodedData() const override { return true; } - virtual void setEncoding(const String&) override; - virtual String encoding() const override; - virtual void finishLoading(ResourceBuffer*) override; }; } // namespace WebCore -#endif // USE(SVG) - -#endif // CachedSVGDocument_h +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedSVGDocument, CachedResource::SVGDocumentResource) diff --git a/Source/WebCore/loader/cache/CachedSVGDocumentClient.h b/Source/WebCore/loader/cache/CachedSVGDocumentClient.h index a01e0aee9..499dcd10e 100644 --- a/Source/WebCore/loader/cache/CachedSVGDocumentClient.h +++ b/Source/WebCore/loader/cache/CachedSVGDocumentClient.h @@ -20,26 +20,17 @@ Boston, MA 02110-1301, USA. */ -#ifndef CachedSVGDocumentClient_h -#define CachedSVGDocumentClient_h - -#if ENABLE(SVG) +#pragma once #include "CachedResourceClient.h" namespace WebCore { -class CachedSVGDocument; - class CachedSVGDocumentClient : public CachedResourceClient { public: virtual ~CachedSVGDocumentClient() { } static CachedResourceClientType expectedType() { return SVGDocumentType; } - virtual CachedResourceClientType resourceClientType() const override { return expectedType(); } + CachedResourceClientType resourceClientType() const override { return expectedType(); } }; } // namespace WebCore - -#endif // ENABLE(SVG) - -#endif // CachedSVGDocumentClient_h diff --git a/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp b/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp index 116f20e88..a9bf7c66e 100644 --- a/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp +++ b/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "CachedSVGDocumentReference.h" -#if ENABLE(SVG) && ENABLE(CSS_FILTERS) #include "CachedResourceHandle.h" #include "CachedResourceLoader.h" #include "CachedResourceRequest.h" @@ -37,7 +36,7 @@ namespace WebCore { CachedSVGDocumentReference::CachedSVGDocumentReference(const String& url) : m_url(url) - , m_document(0) + , m_document(nullptr) , m_loadRequested(false) { } @@ -45,24 +44,23 @@ CachedSVGDocumentReference::CachedSVGDocumentReference(const String& url) CachedSVGDocumentReference::~CachedSVGDocumentReference() { if (m_document) - m_document->removeClient(this); + m_document->removeClient(*this); } -void CachedSVGDocumentReference::load(CachedResourceLoader* loader) +void CachedSVGDocumentReference::load(CachedResourceLoader& loader, const ResourceLoaderOptions& options) { - ASSERT(loader); if (m_loadRequested) return; - CachedResourceRequest request(ResourceRequest(loader->document()->completeURL(m_url))); + auto fetchOptions = options; + fetchOptions.mode = FetchOptions::Mode::SameOrigin; + CachedResourceRequest request(ResourceRequest(loader.document()->completeURL(m_url)), fetchOptions); request.setInitiator(cachedResourceRequestInitiators().css); - m_document = loader->requestSVGDocument(request); + m_document = loader.requestSVGDocument(WTFMove(request)); if (m_document) - m_document->addClient(this); + m_document->addClient(*this); m_loadRequested = true; } } - -#endif diff --git a/Source/WebCore/loader/cache/CachedSVGDocumentReference.h b/Source/WebCore/loader/cache/CachedSVGDocumentReference.h index a297abcf8..70cb1afa4 100644 --- a/Source/WebCore/loader/cache/CachedSVGDocumentReference.h +++ b/Source/WebCore/loader/cache/CachedSVGDocumentReference.h @@ -23,10 +23,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedSVGDocumentReference_h -#define CachedSVGDocumentReference_h +#pragma once -#if ENABLE(SVG) && ENABLE(CSS_FILTERS) #include "CachedResourceHandle.h" #include "CachedSVGDocumentClient.h" #include <wtf/text/WTFString.h> @@ -35,6 +33,7 @@ namespace WebCore { class CachedSVGDocument; class CachedResourceLoader; +struct ResourceLoaderOptions; class CachedSVGDocumentReference : public CachedSVGDocumentClient { public: @@ -42,7 +41,7 @@ public: virtual ~CachedSVGDocumentReference(); - void load(CachedResourceLoader*); + void load(CachedResourceLoader&, const ResourceLoaderOptions&); bool loadRequested() const { return m_loadRequested; } CachedSVGDocument* document() { return m_document.get(); } @@ -53,8 +52,4 @@ private: bool m_loadRequested; }; -}; - -#endif // ENABLE(SVG) && ENABLE(CSS_FILTERS) - -#endif // CachedSVGDocumentReference_h +} // namespace WebCore diff --git a/Source/WebCore/loader/cache/CachedSVGFont.cpp b/Source/WebCore/loader/cache/CachedSVGFont.cpp new file mode 100644 index 000000000..a06641256 --- /dev/null +++ b/Source/WebCore/loader/cache/CachedSVGFont.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * 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 APPLE 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 "CachedSVGFont.h" + +#if ENABLE(SVG_FONTS) + +#include "FontDescription.h" +#include "FontPlatformData.h" +#include "NoEventDispatchAssertion.h" +#include "SVGDocument.h" +#include "SVGFontElement.h" +#include "SVGFontFaceElement.h" +#include "SharedBuffer.h" +#include "TextResourceDecoder.h" +#include "TypedElementDescendantIterator.h" +#include "SVGToOTFFontConversion.h" + +#if USE(DIRECT2D) +#include <dwrite.h> +#endif + +namespace WebCore { + +CachedSVGFont::CachedSVGFont(CachedResourceRequest&& request, SessionID sessionID) + : CachedFont(WTFMove(request), sessionID, SVGFontResource) + , m_externalSVGFontElement(nullptr) +{ +} + +RefPtr<Font> CachedSVGFont::createFont(const FontDescription& fontDescription, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings) +{ + if (firstFontFace(remoteURI)) + return CachedFont::createFont(fontDescription, remoteURI, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings); + return nullptr; +} + +FontPlatformData CachedSVGFont::platformDataFromCustomData(const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings) +{ + if (m_externalSVGDocument) + return FontPlatformData(fontDescription.computedPixelSize(), bold, italic); + return CachedFont::platformDataFromCustomData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings); +} + +bool CachedSVGFont::ensureCustomFontData(const AtomicString& remoteURI) +{ + if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) { + bool sawError = false; + { + // We may get here during render tree updates when events are forbidden. + // Frameless document can't run scripts or call back to the client so this is safe. + m_externalSVGDocument = SVGDocument::create(nullptr, URL()); + auto decoder = TextResourceDecoder::create("application/xml"); + + NoEventDispatchAssertion::EventAllowedScope allowedScope(*m_externalSVGDocument); + + m_externalSVGDocument->setContent(decoder->decodeAndFlush(m_data->data(), m_data->size())); + sawError = decoder->sawError(); + } + + if (sawError) + m_externalSVGDocument = nullptr; + if (m_externalSVGDocument) + maybeInitializeExternalSVGFontElement(remoteURI); + if (!m_externalSVGFontElement) + return false; + if (auto convertedFont = convertSVGToOTFFont(*m_externalSVGFontElement)) + m_convertedFont = SharedBuffer::adoptVector(convertedFont.value()); + else { + m_externalSVGDocument = nullptr; + m_externalSVGFontElement = nullptr; + return false; + } + } + + return m_externalSVGDocument && CachedFont::ensureCustomFontData(m_convertedFont.get()); +} + +SVGFontElement* CachedSVGFont::getSVGFontById(const String& fontName) const +{ + ASSERT(m_externalSVGDocument); + auto elements = descendantsOfType<SVGFontElement>(*m_externalSVGDocument); + + if (fontName.isEmpty()) + return elements.first(); + + for (auto& element : elements) { + if (element.getIdAttribute() == fontName) + return &element; + } + return nullptr; +} + +SVGFontElement* CachedSVGFont::maybeInitializeExternalSVGFontElement(const AtomicString& remoteURI) +{ + if (m_externalSVGFontElement) + return m_externalSVGFontElement; + String fragmentIdentifier; + size_t start = remoteURI.find('#'); + if (start != notFound) + fragmentIdentifier = remoteURI.string().substring(start + 1); + m_externalSVGFontElement = getSVGFontById(fragmentIdentifier); + return m_externalSVGFontElement; +} + +SVGFontFaceElement* CachedSVGFont::firstFontFace(const AtomicString& remoteURI) +{ + if (!maybeInitializeExternalSVGFontElement(remoteURI)) + return nullptr; + + if (auto* firstFontFace = childrenOfType<SVGFontFaceElement>(*m_externalSVGFontElement).first()) + return firstFontFace; + return nullptr; +} + +} + +#endif diff --git a/Source/WebCore/loader/cache/CachedSVGFont.h b/Source/WebCore/loader/cache/CachedSVGFont.h new file mode 100644 index 000000000..93ed7a0c4 --- /dev/null +++ b/Source/WebCore/loader/cache/CachedSVGFont.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007, 2008 Apple 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 APPLE 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. + */ + +#pragma once + +#if ENABLE(SVG_FONTS) + +#include "CachedFont.h" + +namespace WebCore { + +class SVGFontFaceElement; + +class CachedSVGFont final : public CachedFont { +public: + CachedSVGFont(CachedResourceRequest&&, SessionID); + + bool ensureCustomFontData(const AtomicString& remoteURI) override; + + RefPtr<Font> createFont(const FontDescription&, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings&, const FontVariantSettings&) override; + +private: + FontPlatformData platformDataFromCustomData(const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&); + + SVGFontElement* getSVGFontById(const String&) const; + + SVGFontElement* maybeInitializeExternalSVGFontElement(const AtomicString& remoteURI); + SVGFontFaceElement* firstFontFace(const AtomicString& remoteURI); + + RefPtr<SharedBuffer> m_convertedFont; + RefPtr<SVGDocument> m_externalSVGDocument; + SVGFontElement* m_externalSVGFontElement; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedSVGFont, CachedResource::SVGFontResource) + +#endif // ENABLE(SVG_FONTS) diff --git a/Source/WebCore/loader/cache/CachedScript.cpp b/Source/WebCore/loader/cache/CachedScript.cpp index 48facd7bc..84de38ebe 100644 --- a/Source/WebCore/loader/cache/CachedScript.cpp +++ b/Source/WebCore/loader/cache/CachedScript.cpp @@ -29,24 +29,20 @@ #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" +#include "CachedResourceRequest.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" -#include "MemoryCache.h" -#include "ResourceBuffer.h" #include "RuntimeApplicationChecks.h" +#include "SharedBuffer.h" #include "TextResourceDecoder.h" -#include <wtf/Vector.h> namespace WebCore { -CachedScript::CachedScript(const ResourceRequest& resourceRequest, const String& charset) - : CachedResource(resourceRequest, Script) - , m_decoder(TextResourceDecoder::create(ASCIILiteral("application/javascript"), charset)) +CachedScript::CachedScript(CachedResourceRequest&& request, SessionID sessionID) + : CachedResource(WTFMove(request), Script, sessionID) + , m_decoder(TextResourceDecoder::create(ASCIILiteral("application/javascript"), request.charset())) { - // It's javascript we want. - // But some websites think their scripts are <some wrong mimetype here> - // and refuse to serve them if we only accept application/x-javascript. - setAccept("*/*"); } CachedScript::~CachedScript() @@ -65,27 +61,55 @@ String CachedScript::encoding() const String CachedScript::mimeType() const { - return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")).lower(); + return extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase(); } -const String& CachedScript::script() +StringView CachedScript::script() { - ASSERT(!isPurgeable()); + if (!m_data) + return { }; - if (!m_script && m_data) { - m_script = m_decoder->decode(m_data->data(), encodedSize()); - m_script.append(m_decoder->flush()); + if (m_decodingState == NeverDecoded + && TextEncoding(encoding()).isByteBasedEncoding() + && m_data->size() + && charactersAreAllASCII(reinterpret_cast<const LChar*>(m_data->data()), m_data->size())) { + + m_decodingState = DataAndDecodedStringHaveSameBytes; + + // If the encoded and decoded data are the same, there is no decoded data cost! + setDecodedSize(0); + m_decodedDataDeletionTimer.stop(); + + m_scriptHash = StringHasher::computeHashAndMaskTop8Bits(reinterpret_cast<const LChar*>(m_data->data()), m_data->size()); + } + + if (m_decodingState == DataAndDecodedStringHaveSameBytes) + return { reinterpret_cast<const LChar*>(m_data->data()), m_data->size() }; + + if (!m_script) { + m_script = m_decoder->decodeAndFlush(m_data->data(), encodedSize()); + ASSERT(!m_scriptHash || m_scriptHash == m_script.impl()->hash()); + if (m_decodingState == NeverDecoded) + m_scriptHash = m_script.impl()->hash(); + m_decodingState = DataAndDecodedStringHaveDifferentBytes; setDecodedSize(m_script.sizeInBytes()); } + m_decodedDataDeletionTimer.restart(); - return m_script; } -void CachedScript::finishLoading(ResourceBuffer* data) +unsigned CachedScript::scriptHash() +{ + if (m_decodingState == NeverDecoded) + script(); + return m_scriptHash; +} + +void CachedScript::finishLoading(SharedBuffer* data) { m_data = data; - setEncodedSize(m_data.get() ? m_data->size() : 0); + setEncodedSize(data ? data->size() : 0); CachedResource::finishLoading(data); } @@ -93,27 +117,40 @@ void CachedScript::destroyDecodedData() { m_script = String(); setDecodedSize(0); - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable()) - makePurgeable(true); +} + +void CachedScript::setBodyDataFrom(const CachedResource& resource) +{ + ASSERT(resource.type() == type()); + auto& script = static_cast<const CachedScript&>(resource); + + CachedResource::setBodyDataFrom(resource); + + m_script = script.m_script; + m_scriptHash = script.m_scriptHash; + m_decodingState = script.m_decodingState; + m_decoder = script.m_decoder; } #if ENABLE(NOSNIFF) bool CachedScript::mimeTypeAllowedByNosniff() const { - return !parseContentTypeOptionsHeader(m_response.httpHeaderField("X-Content-Type-Options")) == ContentTypeOptionsNosniff || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType()); + return parseContentTypeOptionsHeader(m_response.httpHeaderField(HTTPHeaderName::XContentTypeOptions)) != ContentTypeOptionsNosniff || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType()); } #endif bool CachedScript::shouldIgnoreHTTPStatusCodeErrors() const { +#if PLATFORM(MAC) // This is a workaround for <rdar://problem/13916291> // REGRESSION (r119759): Adobe Flash Player "smaller" installer relies on the incorrect firing // of a load event and needs an app-specific hack for compatibility. // The installer in question tries to load .js file that doesn't exist, causing the server to // return a 404 response. Normally, this would trigger an error event to be dispatched, but the // installer expects a load event instead so we work around it here. - if (applicationIsSolidStateNetworksDownloader()) + if (MacApplication::isSolidStateNetworksDownloader()) return true; +#endif return CachedResource::shouldIgnoreHTTPStatusCodeErrors(); } diff --git a/Source/WebCore/loader/cache/CachedScript.h b/Source/WebCore/loader/cache/CachedScript.h index 80b073c2a..f8898d378 100644 --- a/Source/WebCore/loader/cache/CachedScript.h +++ b/Source/WebCore/loader/cache/CachedScript.h @@ -23,44 +23,51 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef CachedScript_h -#define CachedScript_h +#pragma once #include "CachedResource.h" namespace WebCore { - class CachedResourceLoader; - class TextResourceDecoder; +class TextResourceDecoder; - class CachedScript final : public CachedResource { - public: - CachedScript(const ResourceRequest&, const String& charset); - virtual ~CachedScript(); +class CachedScript final : public CachedResource { +public: + CachedScript(CachedResourceRequest&&, SessionID); + virtual ~CachedScript(); - const String& script(); + StringView script(); + unsigned scriptHash(); - String mimeType() const; + String mimeType() const; #if ENABLE(NOSNIFF) - bool mimeTypeAllowedByNosniff() const; + bool mimeTypeAllowedByNosniff() const; #endif - private: - virtual PurgePriority purgePriority() const override { return PurgeLast; } - virtual bool mayTryReplaceEncodedData() const override { return true; } +private: + bool mayTryReplaceEncodedData() const final { return true; } - virtual bool shouldIgnoreHTTPStatusCodeErrors() const override; + bool shouldIgnoreHTTPStatusCodeErrors() const final; - virtual void setEncoding(const String&) override; - virtual String encoding() const override; - virtual void finishLoading(ResourceBuffer*) override; + void setEncoding(const String&) final; + String encoding() const final; + const TextResourceDecoder* textResourceDecoder() const final { return m_decoder.get(); } + void finishLoading(SharedBuffer*) final; - virtual void destroyDecodedData() override; + void destroyDecodedData() final; - String m_script; - RefPtr<TextResourceDecoder> m_decoder; - }; -} + void setBodyDataFrom(const CachedResource&) final; -#endif + String m_script; + unsigned m_scriptHash { 0 }; + + enum DecodingState { NeverDecoded, DataAndDecodedStringHaveSameBytes, DataAndDecodedStringHaveDifferentBytes }; + DecodingState m_decodingState { NeverDecoded }; + + RefPtr<TextResourceDecoder> m_decoder; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedScript, CachedResource::Script) diff --git a/Source/WebCore/loader/cache/CachedStyleSheetClient.h b/Source/WebCore/loader/cache/CachedStyleSheetClient.h index eed2d3a3e..c2f187389 100644 --- a/Source/WebCore/loader/cache/CachedStyleSheetClient.h +++ b/Source/WebCore/loader/cache/CachedStyleSheetClient.h @@ -23,8 +23,7 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef CachedStyleSheetClient_h -#define CachedStyleSheetClient_h +#pragma once #include "CachedResourceClient.h" #include <wtf/Forward.h> @@ -38,11 +37,9 @@ class CachedStyleSheetClient : public CachedResourceClient { public: virtual ~CachedStyleSheetClient() { } static CachedResourceClientType expectedType() { return StyleSheetType; } - virtual CachedResourceClientType resourceClientType() const override { return expectedType(); } + CachedResourceClientType resourceClientType() const override { return expectedType(); } virtual void setCSSStyleSheet(const String& /* href */, const URL& /* baseURL */, const String& /* charset */, const CachedCSSStyleSheet*) { } virtual void setXSLStyleSheet(const String& /* href */, const URL& /* baseURL */, const String& /* sheet */) { } }; } // namespace WebCore - -#endif // CachedStyleSheetClient_h diff --git a/Source/WebCore/loader/cache/CachedTextTrack.cpp b/Source/WebCore/loader/cache/CachedTextTrack.cpp index 94974a591..1d82bd71b 100644 --- a/Source/WebCore/loader/cache/CachedTextTrack.cpp +++ b/Source/WebCore/loader/cache/CachedTextTrack.cpp @@ -32,36 +32,36 @@ #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" #include "CachedResourceLoader.h" -#include "ResourceBuffer.h" #include "SharedBuffer.h" #include "TextResourceDecoder.h" -#include <wtf/Vector.h> namespace WebCore { -CachedTextTrack::CachedTextTrack(const ResourceRequest& resourceRequest) - : CachedResource(resourceRequest, TextTrackResource) +CachedTextTrack::CachedTextTrack(CachedResourceRequest&& request, SessionID sessionID) + : CachedResource(WTFMove(request), TextTrackResource, sessionID) { } -CachedTextTrack::~CachedTextTrack() +void CachedTextTrack::updateData(SharedBuffer* data) { -} - -void CachedTextTrack::addDataBuffer(ResourceBuffer* data) -{ - ASSERT(m_options.dataBufferingPolicy == BufferData); + ASSERT(dataBufferingPolicy() == BufferData); m_data = data; - setEncodedSize(m_data.get() ? m_data->size() : 0); + setEncodedSize(data ? data->size() : 0); CachedResourceClientWalker<CachedResourceClient> walker(m_clients); - while (CachedResourceClient *client = walker.next()) - client->deprecatedDidReceiveCachedResource(this); + while (CachedResourceClient* client = walker.next()) + client->deprecatedDidReceiveCachedResource(*this); +} + +void CachedTextTrack::addDataBuffer(SharedBuffer& data) +{ + updateData(&data); + CachedResource::addDataBuffer(data); } -void CachedTextTrack::finishLoading(ResourceBuffer* data) +void CachedTextTrack::finishLoading(SharedBuffer* data) { - addDataBuffer(data); + updateData(data); CachedResource::finishLoading(data); } diff --git a/Source/WebCore/loader/cache/CachedTextTrack.h b/Source/WebCore/loader/cache/CachedTextTrack.h index 0df56e2d3..adf544a1b 100644 --- a/Source/WebCore/loader/cache/CachedTextTrack.h +++ b/Source/WebCore/loader/cache/CachedTextTrack.h @@ -23,28 +23,29 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedTextTrack_h -#define CachedTextTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "CachedResource.h" -#include "FontOrientation.h" +#include "TextFlags.h" namespace WebCore { class CachedTextTrack final : public CachedResource { public: - CachedTextTrack(const ResourceRequest&); - virtual ~CachedTextTrack(); + CachedTextTrack(CachedResourceRequest&&, SessionID); private: - virtual bool mayTryReplaceEncodedData() const override { return true; } - virtual void addDataBuffer(ResourceBuffer*) override; - virtual void finishLoading(ResourceBuffer*) override; + bool mayTryReplaceEncodedData() const override { return true; } + void addDataBuffer(SharedBuffer&) override; + void finishLoading(SharedBuffer*) override; + + void updateData(SharedBuffer*); }; -} +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedTextTrack, CachedResource::TextTrackResource) -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp b/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp index eb6c8e1fe..c9f0dc87a 100644 --- a/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp +++ b/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp @@ -29,28 +29,28 @@ #include "CachedResourceClientWalker.h" #include "CachedStyleSheetClient.h" -#include "ResourceBuffer.h" +#include "SharedBuffer.h" #include "TextResourceDecoder.h" -#include <wtf/Vector.h> namespace WebCore { #if ENABLE(XSLT) -CachedXSLStyleSheet::CachedXSLStyleSheet(const ResourceRequest& resourceRequest) - : CachedResource(resourceRequest, XSLStyleSheet) +CachedXSLStyleSheet::CachedXSLStyleSheet(CachedResourceRequest&& request, SessionID sessionID) + : CachedResource(WTFMove(request), XSLStyleSheet, sessionID) , m_decoder(TextResourceDecoder::create("text/xsl")) { - // It's XML we want. - // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example. - setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml"); } -void CachedXSLStyleSheet::didAddClient(CachedResourceClient* c) -{ - ASSERT(c->resourceClientType() == CachedStyleSheetClient::expectedType()); +CachedXSLStyleSheet::~CachedXSLStyleSheet() +{ +} + +void CachedXSLStyleSheet::didAddClient(CachedResourceClient& client) +{ + ASSERT(client.resourceClientType() == CachedStyleSheetClient::expectedType()); if (!isLoading()) - static_cast<CachedStyleSheetClient*>(c)->setXSLStyleSheet(m_resourceRequest.url(), m_response.url(), m_sheet); + static_cast<CachedStyleSheetClient&>(client).setXSLStyleSheet(m_resourceRequest.url(), m_response.url(), m_sheet); } void CachedXSLStyleSheet::setEncoding(const String& chs) @@ -63,14 +63,12 @@ String CachedXSLStyleSheet::encoding() const return m_decoder->encoding().name(); } -void CachedXSLStyleSheet::finishLoading(ResourceBuffer* data) +void CachedXSLStyleSheet::finishLoading(SharedBuffer* data) { m_data = data; - setEncodedSize(m_data.get() ? m_data->size() : 0); - if (m_data.get()) { - m_sheet = m_decoder->decode(m_data->data(), encodedSize()); - m_sheet.append(m_decoder->flush()); - } + setEncodedSize(data ? data->size() : 0); + if (data) + m_sheet = m_decoder->decodeAndFlush(data->data(), encodedSize()); setLoading(false); checkNotify(); } diff --git a/Source/WebCore/loader/cache/CachedXSLStyleSheet.h b/Source/WebCore/loader/cache/CachedXSLStyleSheet.h index 307bda691..3ef0f600e 100644 --- a/Source/WebCore/loader/cache/CachedXSLStyleSheet.h +++ b/Source/WebCore/loader/cache/CachedXSLStyleSheet.h @@ -23,42 +23,38 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef CachedXSLStyleSheet_h -#define CachedXSLStyleSheet_h +#pragma once + +#if ENABLE(XSLT) #include "CachedResource.h" -#include <wtf/Vector.h> namespace WebCore { - class CachedResourceLoader; - class TextResourceDecoder; - -#if ENABLE(XSLT) - class CachedXSLStyleSheet final : public CachedResource { - public: - CachedXSLStyleSheet(const ResourceRequest&); - - const String& sheet() const { return m_sheet; } - - protected: - virtual void checkNotify() override; +class TextResourceDecoder; - String m_sheet; - RefPtr<TextResourceDecoder> m_decoder; +class CachedXSLStyleSheet final : public CachedResource { +public: + CachedXSLStyleSheet(CachedResourceRequest&&, SessionID); + virtual ~CachedXSLStyleSheet(); - private: - virtual bool mayTryReplaceEncodedData() const override { return true; } + const String& sheet() const { return m_sheet; } - virtual void didAddClient(CachedResourceClient*) override; +private: + void checkNotify() final; + bool mayTryReplaceEncodedData() const final { return true; } + void didAddClient(CachedResourceClient&) final; + void setEncoding(const String&) final; + String encoding() const final; + const TextResourceDecoder* textResourceDecoder() const final { return m_decoder.get(); } + void finishLoading(SharedBuffer*) final; - virtual void setEncoding(const String&) override; - virtual String encoding() const override; - virtual void finishLoading(ResourceBuffer*) override; - }; + String m_sheet; + RefPtr<TextResourceDecoder> m_decoder; +}; -#endif +} // namespace WebCore -} +SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedXSLStyleSheet, CachedResource::XSLStyleSheet) -#endif +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/loader/cache/MemoryCache.cpp b/Source/WebCore/loader/cache/MemoryCache.cpp index 652422cf6..75ee84bb7 100644 --- a/Source/WebCore/loader/cache/MemoryCache.cpp +++ b/Source/WebCore/loader/cache/MemoryCache.cpp @@ -25,9 +25,9 @@ #include "BitmapImage.h" #include "CachedImage.h" +#include "CachedImageClient.h" #include "CachedResource.h" #include "CachedResourceHandle.h" -#include "CrossThreadTask.h" #include "Document.h" #include "FrameLoader.h" #include "FrameLoaderTypes.h" @@ -35,166 +35,156 @@ #include "Image.h" #include "Logging.h" #include "PublicSuffix.h" -#include "SecurityOrigin.h" -#include "SecurityOriginHash.h" +#include "SharedBuffer.h" #include "WorkerGlobalScope.h" #include "WorkerLoaderProxy.h" #include "WorkerThread.h" #include <stdio.h> #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> -#include <wtf/TemporaryChange.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/SetForScope.h> #include <wtf/text/CString.h> -#if ENABLE(DISK_IMAGE_CACHE) -#include "DiskImageCacheIOS.h" -#include "ResourceBuffer.h" -#endif - namespace WebCore { static const int cDefaultCacheCapacity = 8192 * 1024; static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again. -static const double cDefaultDecodedDataDeletionInterval = 0; +static const auto defaultDecodedDataDeletionInterval = std::chrono::seconds { 0 }; -MemoryCache* memoryCache() +MemoryCache& MemoryCache::singleton() { - static MemoryCache* staticCache = new MemoryCache; ASSERT(WTF::isMainThread()); - - return staticCache; + static NeverDestroyed<MemoryCache> memoryCache; + return memoryCache; } MemoryCache::MemoryCache() : m_disabled(false) - , m_pruneEnabled(true) , m_inPruneResources(false) , m_capacity(cDefaultCacheCapacity) , m_minDeadCapacity(0) , m_maxDeadCapacity(cDefaultCacheCapacity) - , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval) + , m_deadDecodedDataDeletionInterval(defaultDecodedDataDeletionInterval) , m_liveSize(0) , m_deadSize(0) + , m_pruneTimer(*this, &MemoryCache::prune) { + static_assert(sizeof(long long) > sizeof(unsigned), "Numerical overflow can happen when adjusting the size of the cached memory."); } -URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL) +auto MemoryCache::sessionResourceMap(SessionID sessionID) const -> CachedResourceMap* +{ + ASSERT(sessionID.isValid()); + return m_sessionResources.get(sessionID); +} + +auto MemoryCache::ensureSessionResourceMap(SessionID sessionID) -> CachedResourceMap& +{ + ASSERT(sessionID.isValid()); + auto& map = m_sessionResources.add(sessionID, nullptr).iterator->value; + if (!map) + map = std::make_unique<CachedResourceMap>(); + return *map; +} + +bool MemoryCache::shouldRemoveFragmentIdentifier(const URL& originalURL) { if (!originalURL.hasFragmentIdentifier()) - return originalURL; + return false; // Strip away fragment identifier from HTTP URLs. - // Data URLs must be unmodified. For file and custom URLs clients may expect resources + // Data URLs must be unmodified. For file and custom URLs clients may expect resources // to be unique even when they differ by the fragment identifier only. - if (!originalURL.protocolIsInHTTPFamily()) + return originalURL.protocolIsInHTTPFamily(); +} + +URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL) +{ + if (!shouldRemoveFragmentIdentifier(originalURL)) return originalURL; URL url = originalURL; url.removeFragmentIdentifier(); return url; } -bool MemoryCache::add(CachedResource* resource) +bool MemoryCache::add(CachedResource& resource) { if (disabled()) return false; ASSERT(WTF::isMainThread()); -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem* originMap = m_resources.get(resource->url()); - if (!originMap) { - originMap = new CachedResourceItem; - m_resources.set(resource->url(), adoptPtr(originMap)); - } - originMap->set(resource->cachePartition(), resource); -#else - m_resources.set(resource->url(), resource); -#endif - resource->setInCache(true); + auto key = std::make_pair(resource.url(), resource.cachePartition()); + + ensureSessionResourceMap(resource.sessionID()).set(key, &resource); + resource.setInCache(true); resourceAccessed(resource); - LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().string().latin1().data(), resource); + LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource.url().string().latin1().data(), &resource); return true; } -void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response) +void MemoryCache::revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse& response) { - CachedResource* resource = revalidatingResource->resourceToRevalidate(); - ASSERT(resource); - ASSERT(!resource->inCache()); - ASSERT(resource->isLoaded()); - ASSERT(revalidatingResource->inCache()); + ASSERT(revalidatingResource.resourceToRevalidate()); + CachedResource& resource = *revalidatingResource.resourceToRevalidate(); + ASSERT(!resource.inCache()); + ASSERT(resource.isLoaded()); - // Calling evict() can potentially delete revalidatingResource, which we use + // Calling remove() can potentially delete revalidatingResource, which we use // below. This mustn't be the case since revalidation means it is loaded // and so canDelete() is false. - ASSERT(!revalidatingResource->canDelete()); + ASSERT(!revalidatingResource.canDelete()); - evict(revalidatingResource); + remove(revalidatingResource); -#if ENABLE(CACHE_PARTITIONING) - ASSERT(!m_resources.get(resource->url()) || !m_resources.get(resource->url())->get(resource->cachePartition())); - CachedResourceItem* originMap = m_resources.get(resource->url()); - if (!originMap) { - originMap = new CachedResourceItem; - m_resources.set(resource->url(), adoptPtr(originMap)); - } - originMap->set(resource->cachePartition(), resource); -#else - ASSERT(!m_resources.get(resource->url())); - m_resources.set(resource->url(), resource); -#endif - resource->setInCache(true); - resource->updateResponseAfterRevalidation(response); + auto& resources = ensureSessionResourceMap(resource.sessionID()); + auto key = std::make_pair(resource.url(), resource.cachePartition()); + + ASSERT(!resources.get(key)); + resources.set(key, &resource); + resource.setInCache(true); + resource.updateResponseAfterRevalidation(response); insertInLRUList(resource); - int delta = resource->size(); - if (resource->decodedSize() && resource->hasClients()) + long long delta = resource.size(); + if (resource.decodedSize() && resource.hasClients()) insertInLiveDecodedResourcesList(resource); if (delta) - adjustSize(resource->hasClients(), delta); - - revalidatingResource->switchClientsToRevalidatedResource(); - ASSERT(!revalidatingResource->m_deleted); + adjustSize(resource.hasClients(), delta); + + revalidatingResource.switchClientsToRevalidatedResource(); + ASSERT(!revalidatingResource.m_deleted); // this deletes the revalidating resource - revalidatingResource->clearResourceToRevalidate(); + revalidatingResource.clearResourceToRevalidate(); } -void MemoryCache::revalidationFailed(CachedResource* revalidatingResource) +void MemoryCache::revalidationFailed(CachedResource& revalidatingResource) { ASSERT(WTF::isMainThread()); - LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource); - ASSERT(revalidatingResource->resourceToRevalidate()); - revalidatingResource->clearResourceToRevalidate(); + LOG(ResourceLoading, "Revalidation failed for %p", &revalidatingResource); + ASSERT(revalidatingResource.resourceToRevalidate()); + revalidatingResource.clearResourceToRevalidate(); } -CachedResource* MemoryCache::resourceForURL(const URL& resourceURL) +CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request, SessionID sessionID) { - return resourceForRequest(ResourceRequest(resourceURL)); + // FIXME: Change all clients to make sure HTTP(s) URLs have no fragment identifiers before calling here. + // CachedResourceLoader is now doing this. Add an assertion once all other clients are doing it too. + auto* resources = sessionResourceMap(sessionID); + if (!resources) + return nullptr; + return resourceForRequestImpl(request, *resources); } -CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request) +CachedResource* MemoryCache::resourceForRequestImpl(const ResourceRequest& request, CachedResourceMap& resources) { ASSERT(WTF::isMainThread()); URL url = removeFragmentIdentifierIfNeeded(request.url()); -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem* item = m_resources.get(url); - CachedResource* resource = 0; - if (item) - resource = item->get(request.cachePartition()); -#else - CachedResource* resource = m_resources.get(url); -#endif - bool wasPurgeable = MemoryCache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable(); - if (resource && !resource->makePurgeable(false)) { - ASSERT(!resource->hasClients()); - evict(resource); - return 0; - } - // Add the size back since we had subtracted it when we marked the memory as purgeable. - if (wasPurgeable) - adjustSize(resource->hasClients(), resource->size()); - return resource; + + auto key = std::make_pair(url, request.cachePartition()); + return resources.get(key); } unsigned MemoryCache::deadCapacity() const @@ -212,51 +202,46 @@ unsigned MemoryCache::liveCapacity() const return m_capacity - deadCapacity(); } -#if USE(CG) -// FIXME: Remove the USE(CG) once we either make NativeImagePtr a smart pointer on all platforms or -// remove the usage of CFRetain() in MemoryCache::addImageToCache() so as to make the code platform-independent. -bool MemoryCache::addImageToCache(NativeImagePtr image, const URL& url, const String& cachePartition) +static CachedImageClient& dummyCachedImageClient() +{ + static NeverDestroyed<CachedImageClient> client; + return client; +} + +bool MemoryCache::addImageToCache(NativeImagePtr&& image, const URL& url, const String& domainForCachePartition) { ASSERT(image); - removeImageFromCache(url, cachePartition); // Remove cache entry if it already exists. + SessionID sessionID = SessionID::defaultSessionID(); + removeImageFromCache(url, domainForCachePartition); // Remove cache entry if it already exists. - RefPtr<BitmapImage> bitmapImage = BitmapImage::create(image, nullptr); + RefPtr<BitmapImage> bitmapImage = BitmapImage::create(WTFMove(image), nullptr); if (!bitmapImage) return false; - CachedImageManual* cachedImage = new CachedImageManual(url, bitmapImage.get()); - if (!cachedImage) - return false; + auto cachedImage = std::make_unique<CachedImage>(url, bitmapImage.get(), sessionID); - // Actual release of the CGImageRef is done in BitmapImage. - CFRetain(image); - cachedImage->addFakeClient(); + cachedImage->addClient(dummyCachedImageClient()); cachedImage->setDecodedSize(bitmapImage->decodedSize()); -#if ENABLE(CACHE_PARTITIONING) - cachedImage->resourceRequest().setCachePartition(cachePartition); -#endif - add(cachedImage); - return true; + cachedImage->resourceRequest().setDomainForCachePartition(domainForCachePartition); + + return add(*cachedImage.release()); } -void MemoryCache::removeImageFromCache(const URL& url, const String& cachePartition) +void MemoryCache::removeImageFromCache(const URL& url, const String& domainForCachePartition) { -#if ENABLE(CACHE_PARTITIONING) - CachedResource* resource; - if (CachedResourceItem* item = m_resources.get(url)) - resource = item->get(ResourceRequest::partitionName(cachePartition)); - else - resource = nullptr; -#else - UNUSED_PARAM(cachePartition); - CachedResource* resource = m_resources.get(url); -#endif + auto* resources = sessionResourceMap(SessionID::defaultSessionID()); + if (!resources) + return; + + auto key = std::make_pair(url, ResourceRequest::partitionName(domainForCachePartition)); + + CachedResource* resource = resources->get(key); if (!resource) return; // A resource exists and is not a manually cached image, so just remove it. - if (!resource->isImage() || !static_cast<CachedImage*>(resource)->isManual()) { - evict(resource); + if (!is<CachedImage>(*resource) || !downcast<CachedImage>(*resource).isManuallyCached()) { + remove(*resource); return; } @@ -265,15 +250,11 @@ void MemoryCache::removeImageFromCache(const URL& url, const String& cachePartit // dead resources are pruned. That might be immediately since // removing the last client triggers a MemoryCache::prune, so the // resource may be deleted after this call. - static_cast<CachedImageManual*>(resource)->removeFakeClient(); + downcast<CachedImage>(*resource).removeClient(dummyCachedImageClient()); } -#endif void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources) { - if (!m_pruneEnabled) - return; - unsigned capacity = shouldDestroyDecodedDataForAllLiveResources ? 0 : liveCapacity(); if (capacity && m_liveSize <= capacity) return; @@ -283,41 +264,67 @@ void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveReso pruneLiveResourcesToSize(targetSize, shouldDestroyDecodedDataForAllLiveResources); } -void MemoryCache::pruneLiveResourcesToPercentage(float prunePercentage) +void MemoryCache::forEachResource(const std::function<void(CachedResource&)>& function) { - if (!m_pruneEnabled) - return; + for (auto& unprotectedLRUList : m_allResources) { + Vector<CachedResourceHandle<CachedResource>> lruList; + copyToVector(*unprotectedLRUList, lruList); + for (auto& resource : lruList) + function(*resource); + } +} - if (prunePercentage < 0.0f || prunePercentage > 0.95f) +void MemoryCache::forEachSessionResource(SessionID sessionID, const std::function<void (CachedResource&)>& function) +{ + auto it = m_sessionResources.find(sessionID); + if (it == m_sessionResources.end()) return; - unsigned currentSize = m_liveSize + m_deadSize; - unsigned targetSize = static_cast<unsigned>(currentSize * prunePercentage); + Vector<CachedResourceHandle<CachedResource>> resourcesForSession; + copyValuesToVector(*it->value, resourcesForSession); + + for (auto& resource : resourcesForSession) + function(*resource); +} - pruneLiveResourcesToSize(targetSize); +void MemoryCache::destroyDecodedDataForAllImages() +{ + MemoryCache::singleton().forEachResource([](CachedResource& resource) { + if (resource.isImage()) + resource.destroyDecodedData(); + }); } void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources) { if (m_inPruneResources) return; - TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); + SetForScope<bool> reentrancyProtector(m_inPruneResources, true); double currentTime = FrameView::currentPaintTimeStamp(); if (!currentTime) // In case prune is called directly, outside of a Frame paint. currentTime = monotonicallyIncreasingTime(); // Destroy any decoded data in live objects that we can. - // Start from the tail, since this is the least recently accessed of the objects. + // Start from the head, since this is the least recently accessed of the objects. // The list might not be sorted by the m_lastDecodedAccessTime. The impact // of this weaker invariant is minor as the below if statement to check the // elapsedTime will evaluate to false as the currentTime will be a lot // greater than the current->m_lastDecodedAccessTime. // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209 - CachedResource* current = m_liveDecodedResources.m_tail; - while (current) { - CachedResource* prev = current->m_prevInLiveResourcesList; + auto it = m_liveDecodedResources.begin(); + while (it != m_liveDecodedResources.end()) { + auto* current = *it; + + // Increment the iterator now because the call to destroyDecodedData() below + // may cause a call to ListHashSet::remove() and invalidate the current + // iterator. Note that this is safe because unlike iteration of most + // WTF Hash data structures, iteration is guaranteed safe against mutation + // of the ListHashSet, except for removal of the item currently pointed to + // by a given iterator. + ++it; + ASSERT(current->hasClients()); if (current->isLoaded() && current->decodedSize()) { // Check to see if the remaining resources are too new to prune. @@ -325,23 +332,18 @@ void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestr if (!shouldDestroyDecodedDataForAllLiveResources && elapsedTime < cMinDelayBeforeLiveDecodedPrune) return; - // Destroy our decoded data. This will remove us from - // m_liveDecodedResources, and possibly move us to a different LRU - // list in m_allResources. + // Destroy our decoded data. This will remove us from m_liveDecodedResources, and possibly move us + // to a different LRU list in m_allResources. current->destroyDecodedData(); if (targetSize && m_liveSize <= targetSize) return; } - current = prev; } } void MemoryCache::pruneDeadResources() { - if (!m_pruneEnabled) - return; - unsigned capacity = deadCapacity(); if (capacity && m_deadSize <= capacity) return; @@ -350,135 +352,61 @@ void MemoryCache::pruneDeadResources() pruneDeadResourcesToSize(targetSize); } -void MemoryCache::pruneDeadResourcesToPercentage(float prunePercentage) -{ - if (!m_pruneEnabled) - return; - - if (prunePercentage < 0.0f || prunePercentage > 0.95f) - return; - - unsigned currentSize = m_liveSize + m_deadSize; - unsigned targetSize = static_cast<unsigned>(currentSize * prunePercentage); - - pruneDeadResourcesToSize(targetSize); -} - void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; - TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); - - int size = m_allResources.size(); + SetForScope<bool> reentrancyProtector(m_inPruneResources, true); - // See if we have any purged resources we can evict. - for (int i = 0; i < size; i++) { - CachedResource* current = m_allResources[i].m_tail; - while (current) { - CachedResource* prev = current->m_prevInAllResourcesList; - if (current->wasPurged()) { - ASSERT(!current->hasClients()); - ASSERT(!current->isPreloaded()); - evict(current); - } - current = prev; - } - } if (targetSize && m_deadSize <= targetSize) return; bool canShrinkLRULists = true; - for (int i = size - 1; i >= 0; i--) { - // Remove from the tail, since this is the least frequently accessed of the objects. - CachedResource* current = m_allResources[i].m_tail; - + for (int i = m_allResources.size() - 1; i >= 0; i--) { + // Make a copy of the LRUList first (and ref the resources) as calling + // destroyDecodedData() can alter the LRUList. + Vector<CachedResourceHandle<CachedResource>> lruList; + copyToVector(*m_allResources[i], lruList); + // First flush all the decoded data in this queue. - while (current) { - // Protect 'previous' so it can't get deleted during destroyDecodedData(). - CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; - ASSERT(!previous || previous->inCache()); - if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) { + // Remove from the head, since this is the least frequently accessed of the objects. + for (auto& resource : lruList) { + if (!resource->inCache()) + continue; + + if (!resource->hasClients() && !resource->isPreloaded() && resource->isLoaded()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a different // LRU list in m_allResources. - current->destroyDecodedData(); + resource->destroyDecodedData(); if (targetSize && m_deadSize <= targetSize) return; } - // Decoded data may reference other resources. Stop iterating if 'previous' somehow got - // kicked out of cache during destroyDecodedData(). - if (previous && !previous->inCache()) - break; - current = previous.get(); } - // Now evict objects from this queue. - current = m_allResources[i].m_tail; - while (current) { - CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; - ASSERT(!previous || previous->inCache()); - if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) { - if (!makeResourcePurgeable(current)) - evict(current); + // Now evict objects from this list. + // Remove from the head, since this is the least frequently accessed of the objects. + for (auto& resource : lruList) { + if (!resource->inCache()) + continue; + if (!resource->hasClients() && !resource->isPreloaded() && !resource->isCacheValidator()) { + remove(*resource); if (targetSize && m_deadSize <= targetSize) return; } - if (previous && !previous->inCache()) - break; - current = previous.get(); } // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. - if (m_allResources[i].m_head) + if (!m_allResources[i]->isEmpty()) canShrinkLRULists = false; else if (canShrinkLRULists) - m_allResources.resize(i); + m_allResources.shrink(i); } } -#if ENABLE(DISK_IMAGE_CACHE) -void MemoryCache::flushCachedImagesToDisk() -{ - if (!diskImageCache().isEnabled()) - return; - -#ifndef NDEBUG - double start = WTF::currentTimeMS(); - unsigned resourceCount = 0; - unsigned cachedSize = 0; -#endif - - for (size_t i = m_allResources.size(); i; ) { - --i; - CachedResource* current = m_allResources[i].m_tail; - while (current) { - CachedResource* previous = current->m_prevInAllResourcesList; - - if (!current->isUsingDiskImageCache() && current->canUseDiskImageCache()) { - current->useDiskImageCache(); - current->destroyDecodedData(); -#ifndef NDEBUG - LOG(DiskImageCache, "Cache::diskCacheResources(): attempting to save (%d) bytes", current->resourceBuffer()->sharedBuffer()->size()); - ++resourceCount; - cachedSize += current->resourceBuffer()->sharedBuffer()->size(); -#endif - } - - current = previous; - } - } - -#ifndef NDEBUG - double end = WTF::currentTimeMS(); - LOG(DiskImageCache, "DiskImageCache: took (%f) ms to cache (%d) bytes for (%d) resources", end - start, cachedSize, resourceCount); -#endif -} -#endif // ENABLE(DISK_IMAGE_CACHE) - void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) { ASSERT(minDeadBytes <= maxDeadBytes); @@ -489,400 +417,269 @@ void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, un prune(); } -bool MemoryCache::makeResourcePurgeable(CachedResource* resource) -{ - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) - return false; - - if (!resource->inCache()) - return false; - - if (resource->isPurgeable()) - return true; - - if (!resource->isSafeToMakePurgeable()) - return false; - - if (!resource->makePurgeable(true)) - return false; - - adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); - - return true; -} - -void MemoryCache::evict(CachedResource* resource) +void MemoryCache::remove(CachedResource& resource) { ASSERT(WTF::isMainThread()); - LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resource, resource->url().string().latin1().data()); + LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", &resource, resource.url().string().latin1().data()); // The resource may have already been removed by someone other than our caller, // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>. - if (resource->inCache()) { - // Remove from the resource map. -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem* item = m_resources.get(resource->url()); - if (item) { - item->remove(resource->cachePartition()); - if (!item->size()) - m_resources.remove(resource->url()); - } -#else - m_resources.remove(resource->url()); -#endif - resource->setInCache(false); - - // Remove from the appropriate LRU list. - removeFromLRUList(resource); - removeFromLiveDecodedResourcesList(resource); - - // If the resource was purged, it means we had already decremented the size when we made the - // resource purgeable in makeResourcePurgeable(). So adjust the size if we are evicting a - // resource that was not marked as purgeable. - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() || !resource->isPurgeable()) - adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); - } else -#if ENABLE(CACHE_PARTITIONING) - ASSERT(!m_resources.get(resource->url()) || m_resources.get(resource->url())->get(resource->cachePartition()) != resource); -#else - ASSERT(m_resources.get(resource->url()) != resource); -#endif + if (auto* resources = sessionResourceMap(resource.sessionID())) { + auto key = std::make_pair(resource.url(), resource.cachePartition()); + + if (resource.inCache()) { + // Remove resource from the resource map. + resources->remove(key); + resource.setInCache(false); + + // If the resource map is now empty, remove it from m_sessionResources. + if (resources->isEmpty()) + m_sessionResources.remove(resource.sessionID()); + + // Remove from the appropriate LRU list. + removeFromLRUList(resource); + removeFromLiveDecodedResourcesList(resource); + adjustSize(resource.hasClients(), -static_cast<long long>(resource.size())); + } else + ASSERT(resources->get(key) != &resource); + } - resource->deleteIfPossible(); + resource.deleteIfPossible(); } -MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource) +auto MemoryCache::lruListFor(CachedResource& resource) -> LRUList& { - unsigned accessCount = std::max(resource->accessCount(), 1U); - unsigned queueIndex = WTF::fastLog2(resource->size() / accessCount); + unsigned accessCount = std::max(resource.accessCount(), 1U); + unsigned queueIndex = WTF::fastLog2(resource.size() / accessCount); #ifndef NDEBUG - resource->m_lruIndex = queueIndex; + resource.m_lruIndex = queueIndex; #endif - if (m_allResources.size() <= queueIndex) - m_allResources.grow(queueIndex + 1); - return &m_allResources[queueIndex]; + + m_allResources.reserveCapacity(queueIndex + 1); + while (m_allResources.size() <= queueIndex) + m_allResources.uncheckedAppend(std::make_unique<LRUList>()); + return *m_allResources[queueIndex]; } -void MemoryCache::removeFromLRUList(CachedResource* resource) +void MemoryCache::removeFromLRUList(CachedResource& resource) { // If we've never been accessed, then we're brand new and not in any list. - if (resource->accessCount() == 0) + if (!resource.accessCount()) return; #if !ASSERT_DISABLED - unsigned oldListIndex = resource->m_lruIndex; + unsigned oldListIndex = resource.m_lruIndex; #endif - LRUList* list = lruListFor(resource); + LRUList& list = lruListFor(resource); -#if !ASSERT_DISABLED // Verify that the list we got is the list we want. - ASSERT(resource->m_lruIndex == oldListIndex); - - // Verify that we are in fact in this list. - bool found = false; - for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) { - if (current == resource) { - found = true; - break; - } - } - ASSERT(found); -#endif - - CachedResource* next = resource->m_nextInAllResourcesList; - CachedResource* prev = resource->m_prevInAllResourcesList; - - if (next == 0 && prev == 0 && list->m_head != resource) - return; - - resource->m_nextInAllResourcesList = 0; - resource->m_prevInAllResourcesList = 0; - - if (next) - next->m_prevInAllResourcesList = prev; - else if (list->m_tail == resource) - list->m_tail = prev; + ASSERT(resource.m_lruIndex == oldListIndex); - if (prev) - prev->m_nextInAllResourcesList = next; - else if (list->m_head == resource) - list->m_head = next; + bool removed = list.remove(&resource); + ASSERT_UNUSED(removed, removed); } -void MemoryCache::insertInLRUList(CachedResource* resource) +void MemoryCache::insertInLRUList(CachedResource& resource) { - // Make sure we aren't in some list already. - ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList); - ASSERT(resource->inCache()); - ASSERT(resource->accessCount() > 0); - - LRUList* list = lruListFor(resource); - - resource->m_nextInAllResourcesList = list->m_head; - if (list->m_head) - list->m_head->m_prevInAllResourcesList = resource; - list->m_head = resource; + ASSERT(resource.inCache()); + ASSERT(resource.accessCount() > 0); - if (!resource->m_nextInAllResourcesList) - list->m_tail = resource; - -#if !ASSERT_DISABLED - // Verify that we are in now in the list like we should be. - list = lruListFor(resource); - bool found = false; - for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) { - if (current == resource) { - found = true; - break; - } - } - ASSERT(found); -#endif - + auto addResult = lruListFor(resource).add(&resource); + ASSERT_UNUSED(addResult, addResult.isNewEntry); } -void MemoryCache::resourceAccessed(CachedResource* resource) +void MemoryCache::resourceAccessed(CachedResource& resource) { - ASSERT(resource->inCache()); + ASSERT(resource.inCache()); // Need to make sure to remove before we increase the access count, since // the queue will possibly change. removeFromLRUList(resource); // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size. - if (!resource->accessCount()) - adjustSize(resource->hasClients(), resource->size()); + if (!resource.accessCount()) + adjustSize(resource.hasClients(), resource.size()); // Add to our access count. - resource->increaseAccessCount(); + resource.increaseAccessCount(); // Now insert into the new queue. insertInLRUList(resource); } -void MemoryCache::removeResourcesWithOrigin(SecurityOrigin* origin) +void MemoryCache::removeResourcesWithOrigin(SecurityOrigin& origin) { - Vector<CachedResource*> resourcesWithOrigin; - - CachedResourceMap::iterator e = m_resources.end(); -#if ENABLE(CACHE_PARTITIONING) - String originPartition = ResourceRequest::partitionName(origin->host()); -#endif + String originPartition = ResourceRequest::partitionName(origin.host()); - for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) { -#if ENABLE(CACHE_PARTITIONING) - for (CachedResourceItem::iterator itemIterator = it->value->begin(); itemIterator != it->value->end(); ++itemIterator) { - CachedResource* resource = itemIterator->value; - String partition = itemIterator->key; - if (partition == originPartition) { - resourcesWithOrigin.append(resource); + Vector<CachedResource*> resourcesWithOrigin; + for (auto& resources : m_sessionResources.values()) { + for (auto& keyValue : *resources) { + auto& resource = *keyValue.value; + auto& partitionName = keyValue.key.second; + if (partitionName == originPartition) { + resourcesWithOrigin.append(&resource); continue; } -#else - CachedResource* resource = it->value; -#endif - RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::createFromString(resource->url()); - if (!resourceOrigin) - continue; - if (resourceOrigin->equal(origin)) - resourcesWithOrigin.append(resource); -#if ENABLE(CACHE_PARTITIONING) + RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(resource.url()); + if (resourceOrigin->equal(&origin)) + resourcesWithOrigin.append(&resource); } -#endif } - for (size_t i = 0; i < resourcesWithOrigin.size(); ++i) - remove(resourcesWithOrigin[i]); + for (auto* resource : resourcesWithOrigin) + remove(*resource); +} + +void MemoryCache::removeResourcesWithOrigins(SessionID sessionID, const HashSet<RefPtr<SecurityOrigin>>& origins) +{ + auto* resourceMap = sessionResourceMap(sessionID); + if (!resourceMap) + return; + + HashSet<String> originPartitions; + + for (auto& origin : origins) + originPartitions.add(ResourceRequest::partitionName(origin->host())); + + Vector<CachedResource*> resourcesToRemove; + for (auto& keyValuePair : *resourceMap) { + auto& resource = *keyValuePair.value; + auto& partitionName = keyValuePair.key.second; + if (originPartitions.contains(partitionName)) { + resourcesToRemove.append(&resource); + continue; + } + if (origins.contains(SecurityOrigin::create(resource.url()).ptr())) + resourcesToRemove.append(&resource); + } + + for (auto& resource : resourcesToRemove) + remove(*resource); } void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins) { -#if ENABLE(CACHE_PARTITIONING) - DEFINE_STATIC_LOCAL(String, httpString, ("http")); -#endif - CachedResourceMap::iterator e = m_resources.end(); - for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) { -#if ENABLE(CACHE_PARTITIONING) - if (it->value->begin()->key == emptyString()) - origins.add(SecurityOrigin::createFromString(it->value->begin()->value->url())); - else - origins.add(SecurityOrigin::create(httpString, it->value->begin()->key, 0)); -#else - origins.add(SecurityOrigin::createFromString(it->value->url())); -#endif + for (auto& resources : m_sessionResources.values()) { + for (auto& keyValue : *resources) { + auto& resource = *keyValue.value; + auto& partitionName = keyValue.key.second; + if (!partitionName.isEmpty()) + origins.add(SecurityOrigin::create(ASCIILiteral("http"), partitionName, 0)); + else + origins.add(SecurityOrigin::create(resource.url())); + } } } -void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource) +HashSet<RefPtr<SecurityOrigin>> MemoryCache::originsWithCache(SessionID sessionID) const { - // If we've never been accessed, then we're brand new and not in any list. - if (!resource->m_inLiveDecodedResourcesList) - return; - resource->m_inLiveDecodedResourcesList = false; + HashSet<RefPtr<SecurityOrigin>> origins; -#if !ASSERT_DISABLED - // Verify that we are in fact in this list. - bool found = false; - for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) { - if (current == resource) { - found = true; - break; + auto it = m_sessionResources.find(sessionID); + if (it != m_sessionResources.end()) { + for (auto& keyValue : *it->value) { + auto& resource = *keyValue.value; + auto& partitionName = keyValue.key.second; + if (!partitionName.isEmpty()) + origins.add(SecurityOrigin::create("http", partitionName, 0)); + else + origins.add(SecurityOrigin::create(resource.url())); } } - ASSERT(found); -#endif - CachedResource* next = resource->m_nextInLiveResourcesList; - CachedResource* prev = resource->m_prevInLiveResourcesList; - - if (next == 0 && prev == 0 && m_liveDecodedResources.m_head != resource) - return; - - resource->m_nextInLiveResourcesList = 0; - resource->m_prevInLiveResourcesList = 0; - - if (next) - next->m_prevInLiveResourcesList = prev; - else if (m_liveDecodedResources.m_tail == resource) - m_liveDecodedResources.m_tail = prev; + return origins; +} - if (prev) - prev->m_nextInLiveResourcesList = next; - else if (m_liveDecodedResources.m_head == resource) - m_liveDecodedResources.m_head = next; +void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource& resource) +{ + m_liveDecodedResources.remove(&resource); } -void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource) +void MemoryCache::insertInLiveDecodedResourcesList(CachedResource& resource) { // Make sure we aren't in the list already. - ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList); - resource->m_inLiveDecodedResourcesList = true; - - resource->m_nextInLiveResourcesList = m_liveDecodedResources.m_head; - if (m_liveDecodedResources.m_head) - m_liveDecodedResources.m_head->m_prevInLiveResourcesList = resource; - m_liveDecodedResources.m_head = resource; - - if (!resource->m_nextInLiveResourcesList) - m_liveDecodedResources.m_tail = resource; - -#if !ASSERT_DISABLED - // Verify that we are in now in the list like we should be. - bool found = false; - for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) { - if (current == resource) { - found = true; - break; - } - } - ASSERT(found); -#endif - + ASSERT(!m_liveDecodedResources.contains(&resource)); + m_liveDecodedResources.add(&resource); } -void MemoryCache::addToLiveResourcesSize(CachedResource* resource) +void MemoryCache::addToLiveResourcesSize(CachedResource& resource) { - m_liveSize += resource->size(); - m_deadSize -= resource->size(); + m_liveSize += resource.size(); + m_deadSize -= resource.size(); } -void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource) +void MemoryCache::removeFromLiveResourcesSize(CachedResource& resource) { - m_liveSize -= resource->size(); - m_deadSize += resource->size(); + m_liveSize -= resource.size(); + m_deadSize += resource.size(); } -void MemoryCache::adjustSize(bool live, int delta) +void MemoryCache::adjustSize(bool live, long long delta) { if (live) { - ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0)); + ASSERT(delta >= 0 || (static_cast<long long>(m_liveSize) + delta >= 0)); m_liveSize += delta; } else { - ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0)); + ASSERT(delta >= 0 || (static_cast<long long>(m_deadSize) + delta >= 0)); m_deadSize += delta; } } -void MemoryCache::removeUrlFromCache(ScriptExecutionContext* context, const String& urlString) -{ - removeRequestFromCache(context, ResourceRequest(urlString)); -} - -void MemoryCache::removeRequestFromCache(ScriptExecutionContext* context, const ResourceRequest& request) +void MemoryCache::removeRequestFromSessionCaches(ScriptExecutionContext& context, const ResourceRequest& request) { - if (context->isWorkerGlobalScope()) { - WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context); - workerGlobalScope->thread()->workerLoaderProxy().postTaskToLoader(createCallbackTask(&crossThreadRemoveRequestFromCache, request)); + if (is<WorkerGlobalScope>(context)) { + downcast<WorkerGlobalScope>(context).thread().workerLoaderProxy().postTaskToLoader([request = request.isolatedCopy()] (ScriptExecutionContext& context) { + MemoryCache::removeRequestFromSessionCaches(context, request); + }); return; } - removeRequestFromCacheImpl(context, request); -} - -void MemoryCache::removeRequestFromCacheImpl(ScriptExecutionContext*, const ResourceRequest& request) -{ - if (CachedResource* resource = memoryCache()->resourceForRequest(request)) - memoryCache()->remove(resource); -} - -void MemoryCache::crossThreadRemoveRequestFromCache(ScriptExecutionContext* context, PassOwnPtr<WebCore::CrossThreadResourceRequestData> requestData) -{ - OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData)); - MemoryCache::removeRequestFromCacheImpl(context, *request); + auto& memoryCache = MemoryCache::singleton(); + for (auto& resources : memoryCache.m_sessionResources) { + if (CachedResource* resource = memoryCache.resourceForRequestImpl(request, *resources.value)) + memoryCache.remove(*resource); + } } -void MemoryCache::TypeStatistic::addResource(CachedResource* o) +void MemoryCache::TypeStatistic::addResource(CachedResource& resource) { - bool purged = o->wasPurged(); - bool purgeable = o->isPurgeable() && !purged; - int pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095; count++; - size += purged ? 0 : o->size(); - liveSize += o->hasClients() ? o->size() : 0; - decodedSize += o->decodedSize(); - purgeableSize += purgeable ? pageSize : 0; - purgedSize += purged ? pageSize : 0; -#if ENABLE(DISK_IMAGE_CACHE) - // Only the data inside the resource was mapped, not the entire resource. - mappedSize += o->isUsingDiskImageCache() ? o->resourceBuffer()->sharedBuffer()->size() : 0; -#endif + size += resource.size(); + liveSize += resource.hasClients() ? resource.size() : 0; + decodedSize += resource.decodedSize(); } MemoryCache::Statistics MemoryCache::getStatistics() { Statistics stats; - CachedResourceMap::iterator e = m_resources.end(); - for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) { -#if ENABLE(CACHE_PARTITIONING) - for (CachedResourceItem::iterator itemIterator = i->value->begin(); itemIterator != i->value->end(); ++itemIterator) { - CachedResource* resource = itemIterator->value; -#else - CachedResource* resource = i->value; -#endif + + for (auto& resources : m_sessionResources.values()) { + for (auto* resource : resources->values()) { switch (resource->type()) { case CachedResource::ImageResource: - stats.images.addResource(resource); + stats.images.addResource(*resource); break; case CachedResource::CSSStyleSheet: - stats.cssStyleSheets.addResource(resource); + stats.cssStyleSheets.addResource(*resource); break; case CachedResource::Script: - stats.scripts.addResource(resource); + stats.scripts.addResource(*resource); break; #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: - stats.xslStyleSheets.addResource(resource); + stats.xslStyleSheets.addResource(*resource); break; #endif +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif case CachedResource::FontResource: - stats.fonts.addResource(resource); + stats.fonts.addResource(*resource); break; default: break; } -#if ENABLE(CACHE_PARTITIONING) } -#endif } return stats; } @@ -893,16 +690,10 @@ void MemoryCache::setDisabled(bool disabled) if (!m_disabled) return; - for (;;) { - CachedResourceMap::iterator outerIterator = m_resources.begin(); - if (outerIterator == m_resources.end()) - break; -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem::iterator innerIterator = outerIterator->value->begin(); - evict(innerIterator->value); -#else - evict(outerIterator->value); -#endif + while (!m_sessionResources.isEmpty()) { + auto& resources = *m_sessionResources.begin()->value; + ASSERT(!resources.isEmpty()); + remove(*resources.begin()->value); } } @@ -915,66 +706,65 @@ void MemoryCache::evictResources() setDisabled(false); } +void MemoryCache::evictResources(SessionID sessionID) +{ + if (disabled()) + return; + + forEachSessionResource(sessionID, [this] (CachedResource& resource) { remove(resource); }); + + ASSERT(!m_sessionResources.contains(sessionID)); +} + +bool MemoryCache::needsPruning() const +{ + return m_liveSize + m_deadSize > m_capacity || m_deadSize > m_maxDeadCapacity; +} + void MemoryCache::prune() { - if (m_liveSize + m_deadSize <= m_capacity && m_deadSize <= m_maxDeadCapacity) // Fast path. + if (!needsPruning()) return; pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. pruneLiveResources(); } -void MemoryCache::pruneToPercentage(float targetPercentLive) +void MemoryCache::pruneSoon() { - pruneDeadResourcesToPercentage(targetPercentLive); // Prune dead first, in case it was "borrowing" capacity from live. - pruneLiveResourcesToPercentage(targetPercentLive); + if (m_pruneTimer.isActive()) + return; + if (!needsPruning()) + return; + m_pruneTimer.startOneShot(0); } - #ifndef NDEBUG void MemoryCache::dumpStats() { Statistics s = getStatistics(); -#if ENABLE(DISK_IMAGE_CACHE) - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize", "Mapped", "\"Real\""); - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); - printf("%-13s %13d %13d %13d %13d %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize, s.images.mappedSize, s.images.size - s.images.mappedSize); -#else - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize"); - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize); -#endif - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize); + printf("%-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize"); + printf("%-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------"); + printf("%-13s %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize); + printf("%-13s %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize); #if ENABLE(XSLT) - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize); + printf("%-13s %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize); #endif - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize); - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize); - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); + printf("%-13s %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize); + printf("%-13s %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize); + printf("%-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------"); } void MemoryCache::dumpLRULists(bool includeLive) const { -#if ENABLE(DISK_IMAGE_CACHE) - printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged, isMemoryMapped):\n"); -#else - printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n"); -#endif + printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n"); int size = m_allResources.size(); for (int i = size - 1; i >= 0; i--) { printf("\n\nList %d: ", i); - CachedResource* current = m_allResources[i].m_tail; - while (current) { - CachedResource* prev = current->m_prevInAllResourcesList; - if (includeLive || !current->hasClients()) -#if ENABLE(DISK_IMAGE_CACHE) - printf("(%.1fK, %.1fK, %uA, %dR, %d, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged(), current->isUsingDiskImageCache()); -#else - printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged()); -#endif - - current = prev; + for (auto* resource : *m_allResources[i]) { + if (includeLive || !resource->hasClients()) + printf("(%.1fK, %.1fK, %uA, %dR); ", resource->decodedSize() / 1024.0f, (resource->encodedSize() + resource->overheadSize()) / 1024.0f, resource->accessCount(), resource->hasClients()); } } } diff --git a/Source/WebCore/loader/cache/MemoryCache.h b/Source/WebCore/loader/cache/MemoryCache.h index c023a9725..c5d97e1d3 100644 --- a/Source/WebCore/loader/cache/MemoryCache.h +++ b/Source/WebCore/loader/cache/MemoryCache.h @@ -22,13 +22,16 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef Cache_h -#define Cache_h +#pragma once -#include "NativeImagePtr.h" +#include "NativeImage.h" #include "SecurityOriginHash.h" +#include "SessionID.h" +#include "Timer.h" +#include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/ListHashSet.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> #include <wtf/text/StringHash.h> @@ -36,16 +39,12 @@ namespace WebCore { -class CachedCSSStyleSheet; class CachedResource; -class CachedResourceLoader; class URL; class ResourceRequest; class ResourceResponse; class ScriptExecutionContext; class SecurityOrigin; -struct CrossThreadResourceRequestData; -struct SecurityOriginHash; // This cache holds subresources used by Web pages: images, scripts, stylesheets, etc. @@ -60,54 +59,26 @@ struct SecurityOriginHash; // -------|-----+++++++++++++++| // -------|-----+++++++++++++++|+++++ -// The behavior of the cache changes in the following way if shouldMakeResourcePurgeableOnEviction -// returns true. -// -// 1. Dead resources in the cache are kept in non-purgeable memory. -// 2. When we prune dead resources, instead of freeing them, we mark their memory as purgeable and -// keep the resources until the kernel reclaims the purgeable memory. -// -// By leaving the in-cache dead resources in dirty resident memory, we decrease the likelihood of -// the kernel claiming that memory and forcing us to refetch the resource (for example when a user -// presses back). -// -// And by having an unbounded number of resource objects using purgeable memory, we can use as much -// memory as is available on the machine. The trade-off here is that the CachedResource object (and -// its member variables) are allocated in non-purgeable TC-malloc'd memory so we would see slightly -// more memory use due to this. - class MemoryCache { WTF_MAKE_NONCOPYABLE(MemoryCache); WTF_MAKE_FAST_ALLOCATED; + friend NeverDestroyed<MemoryCache>; + friend class Internals; public: - friend MemoryCache* memoryCache(); - -#if ENABLE(CACHE_PARTITIONING) - typedef HashMap<String, CachedResource*> CachedResourceItem; - typedef HashMap<String, OwnPtr<CachedResourceItem>> CachedResourceMap; -#else - typedef HashMap<String, CachedResource*> CachedResourceMap; -#endif - - struct LRUList { - CachedResource* m_head; - CachedResource* m_tail; - LRUList() : m_head(0), m_tail(0) { } - }; - struct TypeStatistic { int count; int size; int liveSize; int decodedSize; - int purgeableSize; - int purgedSize; -#if ENABLE(DISK_IMAGE_CACHE) - int mappedSize; - TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0), purgeableSize(0), purgedSize(0), mappedSize(0) { } -#else - TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0), purgeableSize(0), purgedSize(0) { } -#endif - void addResource(CachedResource*); + + TypeStatistic() + : count(0) + , size(0) + , liveSize(0) + , decodedSize(0) + { + } + + void addResource(CachedResource&); }; struct Statistics { @@ -118,98 +89,92 @@ public: TypeStatistic fonts; }; - CachedResource* resourceForURL(const URL&); - CachedResource* resourceForRequest(const ResourceRequest&); - - bool add(CachedResource* resource); - void remove(CachedResource* resource) { evict(resource); } + WEBCORE_EXPORT static MemoryCache& singleton(); - static URL removeFragmentIdentifierIfNeeded(const URL& originalURL); - - void revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse&); - void revalidationFailed(CachedResource* revalidatingResource); - - // Sets the cache's memory capacities, in bytes. These will hold only approximately, + WEBCORE_EXPORT CachedResource* resourceForRequest(const ResourceRequest&, SessionID); + + bool add(CachedResource&); + void remove(CachedResource&); + + static bool shouldRemoveFragmentIdentifier(const URL&); + static URL removeFragmentIdentifierIfNeeded(const URL&); + + void revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse&); + void revalidationFailed(CachedResource& revalidatingResource); + + void forEachResource(const std::function<void(CachedResource&)>&); + void forEachSessionResource(SessionID, const std::function<void(CachedResource&)>&); + void destroyDecodedDataForAllImages(); + + // Sets the cache's memory capacities, in bytes. These will hold only approximately, // since the decoded cost of resources like scripts and stylesheets is not known. // - minDeadBytes: The maximum number of bytes that dead resources should consume when the cache is under pressure. // - maxDeadBytes: The maximum number of bytes that dead resources should consume when the cache is not under pressure. // - totalBytes: The maximum number of bytes that the cache should consume overall. - void setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes); + WEBCORE_EXPORT void setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes); // Turn the cache on and off. Disabling the cache will remove all resources from the cache. They may // still live on if they are referenced by some Web page though. - void setDisabled(bool); + WEBCORE_EXPORT void setDisabled(bool); bool disabled() const { return m_disabled; } - void evictResources(); - - void setPruneEnabled(bool enabled) { m_pruneEnabled = enabled; } + WEBCORE_EXPORT void evictResources(); + WEBCORE_EXPORT void evictResources(SessionID); + void prune(); - void pruneToPercentage(float targetPercentLive); + void pruneSoon(); + unsigned size() const { return m_liveSize + m_deadSize; } - void setDeadDecodedDataDeletionInterval(double interval) { m_deadDecodedDataDeletionInterval = interval; } - double deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; } + void setDeadDecodedDataDeletionInterval(std::chrono::milliseconds interval) { m_deadDecodedDataDeletionInterval = interval; } + std::chrono::milliseconds deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; } // Calls to put the cached resource into and out of LRU lists. - void insertInLRUList(CachedResource*); - void removeFromLRUList(CachedResource*); + void insertInLRUList(CachedResource&); + void removeFromLRUList(CachedResource&); // Called to adjust the cache totals when a resource changes size. - void adjustSize(bool live, int delta); + void adjustSize(bool live, long long delta); // Track decoded resources that are in the cache and referenced by a Web page. - void insertInLiveDecodedResourcesList(CachedResource*); - void removeFromLiveDecodedResourcesList(CachedResource*); - - void addToLiveResourcesSize(CachedResource*); - void removeFromLiveResourcesSize(CachedResource*); + void insertInLiveDecodedResourcesList(CachedResource&); + void removeFromLiveDecodedResourcesList(CachedResource&); - static bool shouldMakeResourcePurgeableOnEviction(); + void addToLiveResourcesSize(CachedResource&); + void removeFromLiveResourcesSize(CachedResource&); -#if ENABLE(DISK_IMAGE_CACHE) - void flushCachedImagesToDisk(); // Flush encoded data from resources still referenced by web pages. -#endif - - static void removeUrlFromCache(ScriptExecutionContext*, const String& urlString); - static void removeRequestFromCache(ScriptExecutionContext*, const ResourceRequest&); + static void removeRequestFromSessionCaches(ScriptExecutionContext&, const ResourceRequest&); // Function to collect cache statistics for the caches window in the Safari Debug menu. - Statistics getStatistics(); + WEBCORE_EXPORT Statistics getStatistics(); - void resourceAccessed(CachedResource*); + void resourceAccessed(CachedResource&); + bool inLiveDecodedResourcesList(CachedResource& resource) const { return m_liveDecodedResources.contains(&resource); } typedef HashSet<RefPtr<SecurityOrigin>> SecurityOriginSet; - void removeResourcesWithOrigin(SecurityOrigin*); - void getOriginsWithCache(SecurityOriginSet& origins); - - unsigned minDeadCapacity() const { return m_minDeadCapacity; } - unsigned maxDeadCapacity() const { return m_maxDeadCapacity; } - unsigned capacity() const { return m_capacity; } - unsigned liveSize() const { return m_liveSize; } - unsigned deadSize() const { return m_deadSize; } - -#if USE(CG) - // FIXME: Remove the USE(CG) once we either make NativeImagePtr a smart pointer on all platforms or - // remove the usage of CFRetain() in MemoryCache::addImageToCache() so as to make the code platform-independent. - bool addImageToCache(NativeImagePtr, const URL&, const String& cachePartition); - void removeImageFromCache(const URL&, const String& cachePartition); -#endif + WEBCORE_EXPORT void removeResourcesWithOrigin(SecurityOrigin&); + WEBCORE_EXPORT void removeResourcesWithOrigins(SessionID, const HashSet<RefPtr<SecurityOrigin>>&); + WEBCORE_EXPORT void getOriginsWithCache(SecurityOriginSet& origins); + WEBCORE_EXPORT HashSet<RefPtr<SecurityOrigin>> originsWithCache(SessionID) const; + + WEBCORE_EXPORT bool addImageToCache(NativeImagePtr&&, const URL&, const String& domainForCachePartition); + WEBCORE_EXPORT void removeImageFromCache(const URL&, const String& domainForCachePartition); // pruneDead*() - Flush decoded and encoded data from resources not referenced by Web pages. // pruneLive*() - Flush decoded data from resources still referenced by Web pages. - void pruneDeadResources(); // Automatically decide how much to prune. - void pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources = false); + WEBCORE_EXPORT void pruneDeadResources(); // Automatically decide how much to prune. + WEBCORE_EXPORT void pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources = false); + + WEBCORE_EXPORT void pruneDeadResourcesToSize(unsigned targetSize); + WEBCORE_EXPORT void pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources = false); private: - void pruneDeadResourcesToPercentage(float prunePercentage); // Prune to % current size - void pruneLiveResourcesToPercentage(float prunePercentage); - void pruneDeadResourcesToSize(unsigned targetSize); - void pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources = false); + typedef HashMap<std::pair<URL, String /* partitionName */>, CachedResource*> CachedResourceMap; + typedef ListHashSet<CachedResource*> LRUList; MemoryCache(); ~MemoryCache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons. - LRUList* lruListFor(CachedResource*); + LRUList& lruListFor(CachedResource&); #ifndef NDEBUG void dumpStats(); void dumpLRULists(bool includeLive) const; @@ -217,21 +182,20 @@ private: unsigned liveCapacity() const; unsigned deadCapacity() const; + bool needsPruning() const; - bool makeResourcePurgeable(CachedResource*); - void evict(CachedResource*); + CachedResource* resourceForRequestImpl(const ResourceRequest&, CachedResourceMap&); - static void removeRequestFromCacheImpl(ScriptExecutionContext*, const ResourceRequest&); - static void crossThreadRemoveRequestFromCache(ScriptExecutionContext*, PassOwnPtr<CrossThreadResourceRequestData>); + CachedResourceMap& ensureSessionResourceMap(SessionID); + CachedResourceMap* sessionResourceMap(SessionID) const; bool m_disabled; // Whether or not the cache is enabled. - bool m_pruneEnabled; bool m_inPruneResources; unsigned m_capacity; unsigned m_minDeadCapacity; unsigned m_maxDeadCapacity; - double m_deadDecodedDataDeletionInterval; + std::chrono::milliseconds m_deadDecodedDataDeletionInterval; unsigned m_liveSize; // The number of bytes currently consumed by "live" resources in the cache. unsigned m_deadSize; // The number of bytes currently consumed by "dead" resources in the cache. @@ -239,28 +203,17 @@ private: // Size-adjusted and popularity-aware LRU list collection for cache objects. This collection can hold // more resources than the cached resource map, since it can also hold "stale" multiple versions of objects that are // waiting to die when the clients referencing them go away. - Vector<LRUList, 32> m_allResources; + Vector<std::unique_ptr<LRUList>, 32> m_allResources; // List just for live resources with decoded data. Access to this list is based off of painting the resource. LRUList m_liveDecodedResources; // A URL-based map of all resources that are in the cache (including the freshest version of objects that are currently being // referenced by a Web page). - CachedResourceMap m_resources; -}; - -inline bool MemoryCache::shouldMakeResourcePurgeableOnEviction() -{ -#if PLATFORM(IOS) - return true; -#else - return false; -#endif -} + typedef HashMap<SessionID, std::unique_ptr<CachedResourceMap>> SessionCachedResourceMap; + SessionCachedResourceMap m_sessionResources; -// Function to obtain the global cache. -MemoryCache* memoryCache(); - -} + Timer m_pruneTimer; +}; -#endif +} // namespace WebCore diff --git a/Source/WebCore/loader/icon/IconController.cpp b/Source/WebCore/loader/icon/IconController.cpp index 06c7a5e45..9a1a93148 100644 --- a/Source/WebCore/loader/icon/IconController.cpp +++ b/Source/WebCore/loader/icon/IconController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006-2011, 2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008 Alp Toker <alp@atoker.com> @@ -16,7 +16,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. * @@ -37,12 +37,14 @@ #include "Document.h" #include "DocumentLoader.h" +#include "ElementChildIterator.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" +#include "HTMLHeadElement.h" +#include "HTMLLinkElement.h" #include "IconDatabase.h" -#include "IconDatabaseBase.h" #include "IconLoader.h" -#include "IconURL.h" +#include "LinkIconType.h" #include "Logging.h" #include "MainFrame.h" #include "Page.h" @@ -53,7 +55,6 @@ namespace WebCore { IconController::IconController(Frame& frame) : m_frame(frame) - , m_waitingForLoadDecision(false) { } @@ -61,63 +62,57 @@ IconController::~IconController() { } -URL IconController::url() +static URL iconFromLinkElements(Frame& frame) { - IconURLs iconURLs = urlsForTypes(Favicon); - return iconURLs.isEmpty() ? URL() : iconURLs[0].m_iconURL; -} - -IconURL IconController::iconURL(IconType iconType) const -{ - IconURL result; - const Vector<IconURL>& iconURLs = m_frame.document()->iconURLs(iconType); - Vector<IconURL>::const_iterator iter(iconURLs.begin()); - for (; iter != iconURLs.end(); ++iter) { - if (result.m_iconURL.isEmpty() || !iter->m_mimeType.isEmpty()) - result = *iter; + // This function returns the first icon with a mime type. + // If no icon with mime type exists, the last icon is returned. + // It may make more sense to always return the last icon, + // but this implementation is consistent with previous behavior. + + URL result; + + auto* document = frame.document(); + if (!document) + return result; + + auto* head = document->head(); + if (!head) + return result; + + for (auto& linkElement : childrenOfType<HTMLLinkElement>(*head)) { + if (!linkElement.iconType()) + continue; + if (*linkElement.iconType() != LinkIconType::Favicon) + continue; + if (linkElement.href().isEmpty()) + continue; + result = linkElement.href(); + if (!linkElement.type().isEmpty()) + break; } return result; } -IconURLs IconController::urlsForTypes(int iconTypesMask) +URL IconController::url() { - IconURLs iconURLs; - if (m_frame.tree().parent()) - return iconURLs; - - if (iconTypesMask & Favicon && !appendToIconURLs(Favicon, &iconURLs)) - iconURLs.append(defaultURL(Favicon)); - -#if ENABLE(TOUCH_ICON_LOADING) - int missedIcons = 0; - if (iconTypesMask & TouchPrecomposedIcon) - missedIcons += appendToIconURLs(TouchPrecomposedIcon, &iconURLs) ? 0:1; - - if (iconTypesMask & TouchIcon) - missedIcons += appendToIconURLs(TouchIcon, &iconURLs) ? 0:1; - - // Only return the default touch icons when the both were required and neither was gotten. - if (missedIcons == 2) { - iconURLs.append(defaultURL(TouchPrecomposedIcon)); - iconURLs.append(defaultURL(TouchIcon)); - } -#endif - - // Finally, append all remaining icons of this type. - const Vector<IconURL>& allIconURLs = m_frame.document()->iconURLs(iconTypesMask); - for (Vector<IconURL>::const_iterator iter = allIconURLs.begin(); iter != allIconURLs.end(); ++iter) { - int i; - int iconCount = iconURLs.size(); - for (i = 0; i < iconCount; ++i) { - if (*iter == iconURLs.at(i)) - break; - } - if (i == iconCount) - iconURLs.append(*iter); + if (!m_frame.isMainFrame()) + return URL(); + + auto icon = iconFromLinkElements(m_frame); + if (!icon.isEmpty()) + return icon; + + icon = m_frame.document()->completeURL(ASCIILiteral("/favicon.ico")); + if (icon.protocolIsInHTTPFamily()) { + // FIXME: Not sure we need to remove credentials like this. + // However this preserves behavior this code path has historically had. + icon.setUser(String()); + icon.setPass(String()); + return icon; } - return iconURLs; + return URL(); } void IconController::commitToDatabase(const URL& icon) @@ -142,9 +137,8 @@ void IconController::startLoader() if (!documentCanHaveIcon(m_frame.document()->url())) return; - URL iconURL(url()); - String urlString(iconURL.string()); - if (urlString.isEmpty()) + URL iconURL = url(); + if (iconURL.isEmpty()) return; // People who want to avoid loading images generally want to avoid loading all images, unless an exception has been made for site icons. @@ -154,30 +148,30 @@ void IconController::startLoader() // If we're reloading the page, always start the icon load now. // FIXME: How can this condition ever be true? - if (m_frame.loader().loadType() == FrameLoadTypeReload && m_frame.loader().loadType() == FrameLoadTypeReloadFromOrigin) { + if (m_frame.loader().loadType() == FrameLoadType::Reload && m_frame.loader().loadType() == FrameLoadType::ReloadFromOrigin) { continueLoadWithDecision(IconLoadYes); return; } if (iconDatabase().supportsAsynchronousMode()) { // FIXME (<rdar://problem/9168605>) - We should support in-memory-only private browsing icons in asynchronous icon database mode. - if (m_frame.page() && m_frame.page()->settings().privateBrowsingEnabled()) + if (m_frame.page() && m_frame.page()->usesEphemeralSession()) return; - m_frame.loader().documentLoader()->getIconLoadDecisionForIconURL(urlString); + m_frame.loader().documentLoader()->getIconLoadDecisionForIconURL(iconURL.string()); // Commit the icon url mapping to the database just in case we don't end up loading later. commitToDatabase(iconURL); return; } - IconLoadDecision decision = iconDatabase().synchronousLoadDecisionForIconURL(urlString, m_frame.loader().documentLoader()); + IconLoadDecision decision = iconDatabase().synchronousLoadDecisionForIconURL(iconURL.string(), m_frame.loader().documentLoader()); if (decision == IconLoadUnknown) { // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the // icon is later read in from disk - LOG(IconDatabase, "IconController %p might load icon %s later", this, urlString.ascii().data()); + LOG(IconDatabase, "IconController %p might load icon %s later", this, iconURL.string().utf8().data()); m_waitingForLoadDecision = true; m_frame.loader().client().registerForIconNotification(); commitToDatabase(iconURL); @@ -208,16 +202,15 @@ void IconController::continueLoadWithDecision(IconLoadDecision iconLoadDecision) ASSERT(iconLoadDecision != IconLoadUnknown); if (iconLoadDecision == IconLoadNo) { - URL iconURL(url()); - String urlString(iconURL.string()); - if (urlString.isEmpty()) + URL iconURL = url(); + if (iconURL.isEmpty()) return; - LOG(IconDatabase, "IconController::startLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data()); + LOG(IconDatabase, "IconController::startLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", iconURL.string().utf8().data()); commitToDatabase(iconURL); if (iconDatabase().supportsAsynchronousMode()) { - m_frame.loader().documentLoader()->getIconDataForIconURL(urlString); + m_frame.loader().documentLoader()->getIconDataForIconURL(iconURL.string()); return; } @@ -225,8 +218,8 @@ void IconController::continueLoadWithDecision(IconLoadDecision iconLoadDecision) // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method. // Otherwise if the icon data *is* available, notify the delegate - if (!iconDatabase().synchronousIconDataKnownForIconURL(urlString)) { - LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data()); + if (!iconDatabase().synchronousIconDataKnownForIconURL(iconURL.string())) { + LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", iconURL.string().ascii().data()); m_frame.loader().client().registerForIconNotification(); iconDatabase().synchronousIconForPageURL(m_frame.document()->url().string(), IntSize(0, 0)); iconDatabase().synchronousIconForPageURL(m_frame.loader().initialRequest().url().string(), IntSize(0, 0)); @@ -242,45 +235,4 @@ void IconController::continueLoadWithDecision(IconLoadDecision iconLoadDecision) m_iconLoader->startLoading(); } -bool IconController::appendToIconURLs(IconType iconType, IconURLs* iconURLs) -{ - IconURL faviconURL = iconURL(iconType); - if (faviconURL.m_iconURL.isEmpty()) - return false; - - iconURLs->append(faviconURL); - return true; -} - -IconURL IconController::defaultURL(IconType iconType) -{ - // Don't return a favicon iconURL unless we're http or https - URL documentURL = m_frame.document()->url(); - if (!documentURL.protocolIsInHTTPFamily()) - return IconURL(); - - URL url; - bool couldSetProtocol = url.setProtocol(documentURL.protocol()); - ASSERT_UNUSED(couldSetProtocol, couldSetProtocol); - url.setHost(documentURL.host()); - if (documentURL.hasPort()) - url.setPort(documentURL.port()); - - if (iconType == Favicon) { - url.setPath("/favicon.ico"); - return IconURL::defaultIconURL(url, Favicon); - } -#if ENABLE(TOUCH_ICON_LOADING) - if (iconType == TouchPrecomposedIcon) { - url.setPath("/apple-touch-icon-precomposed.png"); - return IconURL::defaultIconURL(url, TouchPrecomposedIcon); - } - if (iconType == TouchIcon) { - url.setPath("/apple-touch-icon.png"); - return IconURL::defaultIconURL(url, TouchIcon); - } -#endif - return IconURL(); -} - } diff --git a/Source/WebCore/loader/icon/IconController.h b/Source/WebCore/loader/icon/IconController.h index 83844b9d4..31b1562a1 100644 --- a/Source/WebCore/loader/icon/IconController.h +++ b/Source/WebCore/loader/icon/IconController.h @@ -12,7 +12,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. * @@ -28,28 +28,23 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IconController_h -#define IconController_h +#pragma once #include "IconDatabaseBase.h" -#include "IconURL.h" -#include "URL.h" namespace WebCore { class Frame; class IconLoader; +class URL; class IconController { - WTF_MAKE_NONCOPYABLE(IconController); WTF_MAKE_FAST_ALLOCATED; public: explicit IconController(Frame&); ~IconController(); - URL url(); - IconURLs urlsForTypes(int iconTypesMask); - IconURL iconURL(IconType) const; + WEBCORE_EXPORT URL url(); void startLoader(); void stopLoader(); @@ -60,15 +55,9 @@ public: void commitToDatabase(const URL& icon); private: - bool appendToIconURLs(IconType, IconURLs*); - IconURL defaultURL(IconType); - Frame& m_frame; - std::unique_ptr<IconLoader> m_iconLoader; - bool m_waitingForLoadDecision; + bool m_waitingForLoadDecision { false }; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/loader/icon/IconDatabase.cpp b/Source/WebCore/loader/icon/IconDatabase.cpp index 39272e4d6..4cdcfaad6 100644 --- a/Source/WebCore/loader/icon/IconDatabase.cpp +++ b/Source/WebCore/loader/icon/IconDatabase.cpp @@ -11,10 +11,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -34,16 +34,14 @@ #include "IconDatabaseClient.h" #include "IconRecord.h" #include "Image.h" -#include "IntSize.h" #include "Logging.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" #include "SuddenTermination.h" #include <wtf/AutodrainedPool.h> -#include <wtf/CurrentTime.h> #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> -#include <wtf/text/CString.h> // For methods that are meant to support API from the main thread - should not be called internally #define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD()) @@ -82,7 +80,7 @@ static const int notUsedIconExpirationTime = 60*60*24*30; #if !LOG_DISABLED || !ERROR_DISABLED static String urlForLogging(const String& url) { - static unsigned urlTruncationLength = 120; + static const unsigned urlTruncationLength = 120; if (url.length() < urlTruncationLength) return url; @@ -90,19 +88,19 @@ static String urlForLogging(const String& url) } #endif -class DefaultIconDatabaseClient : public IconDatabaseClient { +class DefaultIconDatabaseClient final : public IconDatabaseClient { WTF_MAKE_FAST_ALLOCATED; public: - virtual void didImportIconURLForPageURL(const String&) { } - virtual void didImportIconDataForPageURL(const String&) { } - virtual void didChangeIconForPageURL(const String&) { } - virtual void didRemoveAllIcons() { } - virtual void didFinishURLImport() { } + void didImportIconURLForPageURL(const String&) override { } + void didImportIconDataForPageURL(const String&) override { } + void didChangeIconForPageURL(const String&) override { } + void didRemoveAllIcons() override { } + void didFinishURLImport() override { } }; static IconDatabaseClient* defaultClient() { - static IconDatabaseClient* defaultClient = new DefaultIconDatabaseClient(); + static IconDatabaseClient* defaultClient = new DefaultIconDatabaseClient; return defaultClient; } @@ -171,7 +169,11 @@ void IconDatabase::close() m_removeIconsRequested = false; m_syncDB.close(); - ASSERT(!isOpen()); + + // If there are still main thread callbacks in flight then the database might not actually be closed yet. + // But if it is closed, notify the client now. + if (!isOpen() && m_client) + m_client->didClose(); } void IconDatabase::removeAllIcons() @@ -185,28 +187,26 @@ void IconDatabase::removeAllIcons() // Clear the in-memory record of every IconRecord, anything waiting to be read from disk, and anything waiting to be written to disk { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); // Clear the IconRecords for every page URL - RefCounting will cause the IconRecords themselves to be deleted // We don't delete the actual PageRecords because we have the "retain icon for url" count to keep track of - HashMap<String, PageURLRecord*>::iterator iter = m_pageURLToRecordMap.begin(); - HashMap<String, PageURLRecord*>::iterator end = m_pageURLToRecordMap.end(); - for (; iter != end; ++iter) - (*iter).value->setIconRecord(0); - + for (auto& pageURL : m_pageURLToRecordMap.values()) + pageURL->setIconRecord(nullptr); + // Clear the iconURL -> IconRecord map m_iconURLToRecordMap.clear(); // Clear all in-memory records of things that need to be synced out to disk { - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); m_pageURLsPendingSync.clear(); m_iconsPendingSync.clear(); } // Clear all in-memory records of things that need to be read in from disk { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); m_pageURLsPendingImport.clear(); m_pageURLsInterestedInIcons.clear(); m_iconsPendingReading.clear(); @@ -226,9 +226,9 @@ Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, co // We should go our of our way to only copy it if we have to store it if (!isOpen() || !documentCanHaveIcon(pageURLOriginal)) - return 0; + return nullptr; - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); performPendingRetainAndReleaseOperations(); @@ -244,14 +244,14 @@ Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, co // 1 - The initial url import is incomplete and this pageURL was marked to be notified once it is complete if an iconURL exists // 2 - The initial url import IS complete and this pageURL has no icon if (!pageRecord) { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); // Import is ongoing, there might be an icon. In this case, register to be notified when the icon comes in // If we ever reach this condition, we know we've already made the pageURL copy if (!m_iconURLImportComplete) m_pageURLsInterestedInIcons.add(pageURLCopy); - return 0; + return nullptr; } IconRecord* iconRecord = pageRecord->iconRecord(); @@ -260,14 +260,14 @@ Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, co // In this case, the pageURL is already in the set to alert the client when the iconURL mapping is complete so // we can just bail now if (!m_iconURLImportComplete && !iconRecord) - return 0; + return nullptr; // Assuming we're done initializing and cleanup is allowed, // the only way we should *not* have an icon record is if this pageURL is retained but has no icon yet. ASSERT(iconRecord || databaseCleanupCounter || m_retainedPageURLs.contains(pageURLOriginal)); if (!iconRecord) - return 0; + return nullptr; // If it's a new IconRecord object that doesn't have its imageData set yet, // mark it to be read by the background thread @@ -275,17 +275,17 @@ Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, co if (pageURLCopy.isNull()) pageURLCopy = pageURLOriginal.isolatedCopy(); - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); m_pageURLsInterestedInIcons.add(pageURLCopy); m_iconsPendingReading.add(iconRecord); wakeSyncThread(); - return 0; + return nullptr; } // If the size parameter was (0, 0) that means the caller of this method just wanted the read from disk to be kicked off // and isn't actually interested in the image return value if (size == IntSize(0, 0)) - return 0; + return nullptr; // PARANOID DISCUSSION: This method makes some assumptions. It returns a WebCore::image which the icon database might dispose of at anytime in the future, // and Images aren't ref counted. So there is no way for the client to guarantee continued existence of the image. @@ -300,13 +300,13 @@ Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, co return iconRecord->image(size); } -PassNativeImagePtr IconDatabase::synchronousNativeIconForPageURL(const String& pageURLOriginal, const IntSize& size) +NativeImagePtr IconDatabase::synchronousNativeIconForPageURL(const String& pageURLOriginal, const IntSize& size) { Image* icon = synchronousIconForPageURL(pageURLOriginal, size); if (!icon) - return 0; + return nullptr; - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); return icon->nativeImageForCurrentFrame(); } @@ -328,7 +328,7 @@ String IconDatabase::synchronousIconURLForPageURL(const String& pageURLOriginal) if (!isOpen() || !documentCanHaveIcon(pageURLOriginal)) return String(); - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal); if (!pageRecord) @@ -390,8 +390,8 @@ static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord) 0x00, 0x00, 0x01, 0x52, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10 }; - static SharedBuffer* defaultIconBuffer = SharedBuffer::create(defaultIconData, sizeof(defaultIconData)).leakRef(); - defaultIconRecord->setImageData(defaultIconBuffer); + static auto& defaultIconBuffer = SharedBuffer::create(defaultIconData, sizeof(defaultIconData)).leakRef(); + defaultIconRecord->setImageData(&defaultIconBuffer); } #endif @@ -416,7 +416,7 @@ void IconDatabase::retainIconForPageURL(const String& pageURL) return; { - MutexLocker locker(m_urlsToRetainOrReleaseLock); + LockHolder locker(m_urlsToRetainOrReleaseLock); m_urlsToRetain.add(pageURL.isolatedCopy()); m_retainOrReleaseIconRequested = true; } @@ -449,7 +449,7 @@ void IconDatabase::performRetainIconForPageURL(const String& pageURLOriginal, in if (!m_iconURLImportComplete) return; - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); // If this pageURL waiting to be sync'ed, update the sync record // This saves us in the case where a page was ready to be deleted from the database but was just retained - so theres no need to delete it! if (!m_privateBrowsingEnabled && m_pageURLsPendingSync.contains(pageURL)) { @@ -469,7 +469,7 @@ void IconDatabase::releaseIconForPageURL(const String& pageURL) return; { - MutexLocker locker(m_urlsToRetainOrReleaseLock); + LockHolder locker(m_urlsToRetainOrReleaseLock); m_urlsToRelease.add(pageURL.isolatedCopy()); m_retainOrReleaseIconRequested = true; } @@ -505,7 +505,7 @@ void IconDatabase::performReleaseIconForPageURL(const String& pageURLOriginal, i ASSERT(!iconRecord || (iconRecord && m_iconURLToRecordMap.get(iconRecord->iconURL()) == iconRecord)); { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); // Since this pageURL is going away, there's no reason anyone would ever be interested in its read results if (!m_iconURLImportComplete) @@ -521,7 +521,7 @@ void IconDatabase::performReleaseIconForPageURL(const String& pageURLOriginal, i // Mark stuff for deletion from the database only if we're not in private browsing if (!m_privateBrowsingEnabled) { - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); m_pageURLsPendingSync.set(pageURLOriginal.isolatedCopy(), pageRecord->snapshot(true)); // If this page is the last page to refer to a particular IconRecord, that IconRecord needs to @@ -533,7 +533,7 @@ void IconDatabase::performReleaseIconForPageURL(const String& pageURLOriginal, i delete pageRecord; } -void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, const String& iconURLOriginal) +void IconDatabase::setIconDataForIconURL(SharedBuffer* dataOriginal, const String& iconURLOriginal) { ASSERT_NOT_SYNC_THREAD(); @@ -542,23 +542,23 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, if (!isOpen() || iconURLOriginal.isEmpty()) return; - RefPtr<SharedBuffer> data = dataOriginal ? dataOriginal->copy() : PassRefPtr<SharedBuffer>(0); + auto data = dataOriginal ? RefPtr<SharedBuffer> { dataOriginal->copy() } : nullptr; String iconURL = iconURLOriginal.isolatedCopy(); Vector<String> pageURLs; { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); // If this icon was pending a read, remove it from that set because this new data should override what is on disk RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL); if (icon) { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); m_iconsPendingReading.remove(icon.get()); } else icon = getOrCreateIconRecord(iconURL); // Update the data and set the time stamp - icon->setImageData(data.release()); + icon->setImageData(WTFMove(data)); icon->setTimestamp((int)currentTime()); // Copy the current retaining pageURLs - if any - to notify them of the change @@ -566,7 +566,7 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, // Mark the IconRecord as requiring an update to the database only if private browsing is disabled if (!m_privateBrowsingEnabled) { - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); m_iconsPendingSync.set(iconURL, icon->snapshot()); } @@ -584,11 +584,11 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, // Start the timer to commit this change - or further delay the timer if it was already started scheduleOrDeferSyncTimer(); - for (unsigned i = 0; i < pageURLs.size(); ++i) { + for (auto& pageURL : pageURLs) { AutodrainedPool pool; - LOG(IconDatabase, "Dispatching notification that retaining pageURL %s has a new icon", urlForLogging(pageURLs[i]).ascii().data()); - m_client->didChangeIconForPageURL(pageURLs[i]); + LOG(IconDatabase, "Dispatching notification that retaining pageURL %s has a new icon", urlForLogging(pageURL).ascii().data()); + m_client->didChangeIconForPageURL(pageURL); } } } @@ -607,7 +607,7 @@ void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const Str String iconURL, pageURL; { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal); @@ -635,13 +635,13 @@ void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const Str ASSERT(iconRecord->retainingPageURLs().size() == 0); LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(iconRecord->iconURL()).ascii().data()); m_iconURLToRecordMap.remove(iconRecord->iconURL()); - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); m_iconsPendingReading.remove(iconRecord.get()); } // And mark this mapping to be added to the database if (!m_privateBrowsingEnabled) { - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot()); // If the icon is on its last ref, mark it for deletion @@ -673,7 +673,7 @@ IconLoadDecision IconDatabase::synchronousLoadDecisionForIconURL(const String& i // 1 - When we read the icon urls from disk, getting the timeStamp at the same time // 2 - When we get a new icon from the loader, in which case the timestamp is set at that time { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) { LOG(IconDatabase, "Found expiration time on a present icon based on existing IconRecord"); return static_cast<int>(currentTime()) - static_cast<int>(icon->getTimestamp()) > iconExpirationTime ? IconLoadYes : IconLoadNo; @@ -681,7 +681,7 @@ IconLoadDecision IconDatabase::synchronousLoadDecisionForIconURL(const String& i } // If we don't have a record for it, but we *have* imported all iconURLs from disk, then we should load it now - MutexLocker readingLocker(m_pendingReadingLock); + LockHolder readingLocker(m_pendingReadingLock); if (m_iconURLImportComplete) return IconLoadYes; @@ -698,7 +698,7 @@ bool IconDatabase::synchronousIconDataKnownForIconURL(const String& iconURL) { ASSERT_NOT_SYNC_THREAD(); - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) return icon->imageDataStatus() != ImageDataStatusUnknown; @@ -753,39 +753,36 @@ void IconDatabase::checkIntegrityBeforeOpening() size_t IconDatabase::pageURLMappingCount() { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); return m_pageURLToRecordMap.size(); } size_t IconDatabase::retainedPageURLCount() { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); performPendingRetainAndReleaseOperations(); return m_retainedPageURLs.size(); } size_t IconDatabase::iconRecordCount() { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); return m_iconURLToRecordMap.size(); } size_t IconDatabase::iconRecordCountWithData() { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); size_t result = 0; - - HashMap<String, IconRecord*>::iterator i = m_iconURLToRecordMap.begin(); - HashMap<String, IconRecord*>::iterator end = m_iconURLToRecordMap.end(); - - for (; i != end; ++i) - result += ((*i).value->imageDataStatus() == ImageDataStatusPresent); - + + for (auto& iconRecord : m_iconURLToRecordMap.values()) + result += (iconRecord->imageDataStatus() == ImageDataStatusPresent); + return result; } IconDatabase::IconDatabase() - : m_syncTimer(this, &IconDatabase::syncTimerFired) + : m_syncTimer(*this, &IconDatabase::syncTimerFired) , m_syncThreadRunning(false) , m_scheduleOrDeferSyncTimerRequested(false) , m_isEnabled(false) @@ -796,6 +793,7 @@ IconDatabase::IconDatabase() , m_syncThreadHasWorkToDo(false) , m_retainOrReleaseIconRequested(false) , m_initialPruningComplete(false) + , m_mainThreadCallbackCount(0) , m_client(defaultClient()) { LOG(IconDatabase, "Creating IconDatabase %p", this); @@ -807,11 +805,6 @@ IconDatabase::~IconDatabase() ASSERT(!isOpen()); } -void IconDatabase::notifyPendingLoadDecisionsOnMainThread(void* context) -{ - static_cast<IconDatabase*>(context)->notifyPendingLoadDecisions(); -} - void IconDatabase::notifyPendingLoadDecisions() { ASSERT_NOT_SYNC_THREAD(); @@ -819,37 +812,24 @@ void IconDatabase::notifyPendingLoadDecisions() // This method should only be called upon completion of the initial url import from the database ASSERT(m_iconURLImportComplete); LOG(IconDatabase, "Notifying all DocumentLoaders that were waiting on a load decision for their icons"); - - HashSet<RefPtr<DocumentLoader>>::iterator i = m_loadersPendingDecision.begin(); - HashSet<RefPtr<DocumentLoader>>::iterator end = m_loadersPendingDecision.end(); - - for (; i != end; ++i) - if ((*i)->refCount() > 1) - (*i)->iconLoadDecisionAvailable(); - + + for (auto& loader : m_loadersPendingDecision) { + if (loader->refCount() > 1) + loader->iconLoadDecisionAvailable(); + } + m_loadersPendingDecision.clear(); } void IconDatabase::wakeSyncThread() { - MutexLocker locker(m_syncLock); + LockHolder locker(m_syncLock); if (!m_disableSuddenTerminationWhileSyncThreadHasWorkToDo) m_disableSuddenTerminationWhileSyncThreadHasWorkToDo = std::make_unique<SuddenTerminationDisabler>(); m_syncThreadHasWorkToDo = true; - m_syncCondition.signal(); -} - -void IconDatabase::performScheduleOrDeferSyncTimer() -{ - m_syncTimer.startOneShot(updateTimerDelay); - m_scheduleOrDeferSyncTimerRequested = false; -} - -void IconDatabase::performScheduleOrDeferSyncTimerOnMainThread(void* context) -{ - static_cast<IconDatabase*>(context)->performScheduleOrDeferSyncTimer(); + m_syncCondition.notifyOne(); } void IconDatabase::scheduleOrDeferSyncTimer() @@ -863,10 +843,13 @@ void IconDatabase::scheduleOrDeferSyncTimer() m_disableSuddenTerminationWhileSyncTimerScheduled = std::make_unique<SuddenTerminationDisabler>(); m_scheduleOrDeferSyncTimerRequested = true; - callOnMainThread(performScheduleOrDeferSyncTimerOnMainThread, this); + callOnMainThread([this] { + m_syncTimer.startOneShot(updateTimerDelay); + m_scheduleOrDeferSyncTimerRequested = false; + }); } -void IconDatabase::syncTimerFired(Timer<IconDatabase>&) +void IconDatabase::syncTimerFired() { ASSERT_NOT_SYNC_THREAD(); wakeSyncThread(); @@ -880,35 +863,39 @@ void IconDatabase::syncTimerFired(Timer<IconDatabase>&) bool IconDatabase::isOpen() const { - MutexLocker locker(m_syncLock); - return m_syncDB.isOpen(); + return isOpenBesidesMainThreadCallbacks() || m_mainThreadCallbackCount; +} + +bool IconDatabase::isOpenBesidesMainThreadCallbacks() const +{ + LockHolder locker(m_syncLock); + return m_syncThreadRunning || m_syncDB.isOpen(); } String IconDatabase::databasePath() const { - MutexLocker locker(m_syncLock); + LockHolder locker(m_syncLock); return m_completeDatabasePath.isolatedCopy(); } String IconDatabase::defaultDatabaseFilename() { - DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, (ASCIILiteral("WebpageIcons.db"))); - return defaultDatabaseFilename.isolatedCopy(); + static NeverDestroyed<String> defaultDatabaseFilename(ASCIILiteral("WebpageIcons.db")); + return defaultDatabaseFilename.get().isolatedCopy(); } // Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import" -PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL) +Ref<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL) { // Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method ASSERT(!m_urlAndIconLock.tryLock()); - if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) - return icon; + if (auto* icon = m_iconURLToRecordMap.get(iconURL)) + return *icon; - RefPtr<IconRecord> newIcon = IconRecord::create(iconURL); - m_iconURLToRecordMap.set(iconURL, newIcon.get()); - - return newIcon.release(); + auto newIcon = IconRecord::create(iconURL); + m_iconURLToRecordMap.set(iconURL, newIcon.ptr()); + return newIcon; } // This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification @@ -918,11 +905,11 @@ PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL) ASSERT(!m_urlAndIconLock.tryLock()); if (!documentCanHaveIcon(pageURL)) - return 0; + return nullptr; PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL); - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); if (!m_iconURLImportComplete) { // If the initial import of all URLs hasn't completed and we have no page record, we assume we *might* know about this later and create a record for it if (!pageRecord) { @@ -935,7 +922,7 @@ PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL) // Mark the URL as "interested in the result of the import" then bail if (!pageRecord->iconRecord()) { m_pageURLsPendingImport.add(pageURL); - return 0; + return nullptr; } } @@ -991,7 +978,7 @@ void IconDatabase::iconDatabaseSyncThread() } { - MutexLocker locker(m_syncLock); + LockHolder locker(m_syncLock); if (!m_syncDB.open(m_completeDatabasePath)) { LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg()); return; @@ -1119,7 +1106,7 @@ void IconDatabase::performOpenInitialization() m_syncDB.close(); { - MutexLocker locker(m_syncLock); + LockHolder locker(m_syncLock); // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future; deleteFile(m_completeDatabasePath + "-journal"); deleteFile(m_completeDatabasePath); @@ -1165,16 +1152,16 @@ bool IconDatabase::checkIntegrity() ASSERT_ICON_SYNC_THREAD(); SQLiteStatement integrity(m_syncDB, "PRAGMA integrity_check;"); - if (integrity.prepare() != SQLResultOk) { + if (integrity.prepare() != SQLITE_OK) { LOG_ERROR("checkIntegrity failed to execute"); return false; } int resultCode = integrity.step(); - if (resultCode == SQLResultOk) + if (resultCode == SQLITE_OK) return true; - - if (resultCode != SQLResultRow) + + if (resultCode != SQLITE_ROW) return false; int columns = integrity.columnCount(); @@ -1209,25 +1196,25 @@ void IconDatabase::performURLImport() SQLiteStatement query(m_syncDB, importQuery); - if (query.prepare() != SQLResultOk) { + if (query.prepare() != SQLITE_OK) { LOG_ERROR("Unable to prepare icon url import query"); return; } int result = query.step(); - while (result == SQLResultRow) { + while (result == SQLITE_ROW) { AutodrainedPool pool; String pageURL = query.getColumnText(0); String iconURL = query.getColumnText(1); { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL); // If the pageRecord doesn't exist in this map, then no one has retained this pageURL // If the s_databaseCleanupCounter count is non-zero, then we're not supposed to be pruning the database in any manner, - // so go ahead and actually create a pageURLRecord for this url even though it's not retained. + // so actually create a pageURLRecord for this url even though it's not retained. // If database cleanup *is* allowed, we don't want to bother pulling in a page url from disk that noone is actually interested // in - we'll prune it later instead! if (!pageRecord && databaseCleanupCounter && documentCanHaveIcon(pageURL)) { @@ -1254,7 +1241,7 @@ void IconDatabase::performURLImport() // one for the URL and one for the Image itself // Note that WebIconDatabase is not neccessarily API so we might be able to make this change { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); if (m_pageURLsPendingImport.contains(pageURL)) { dispatchDidImportIconURLForPageURLOnMainThread(pageURL); m_pageURLsPendingImport.remove(pageURL); @@ -1270,14 +1257,14 @@ void IconDatabase::performURLImport() result = query.step(); } - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG(IconDatabase, "Error reading page->icon url mappings from database"); // Clear the m_pageURLsPendingImport set - either the page URLs ended up with an iconURL (that we'll notify about) or not, // but after m_iconURLImportComplete is set to true, we don't care about this set anymore Vector<String> urls; { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); urls.appendRange(m_pageURLsPendingImport.begin(), m_pageURLsPendingImport.end()); m_pageURLsPendingImport.clear(); @@ -1290,15 +1277,15 @@ void IconDatabase::performURLImport() // Remove unretained ones if database cleanup is allowed // Keep a set of ones that are retained and pending notification { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); performPendingRetainAndReleaseOperations(); - for (unsigned i = 0; i < urls.size(); ++i) { - if (!m_retainedPageURLs.contains(urls[i])) { - PageURLRecord* record = m_pageURLToRecordMap.get(urls[i]); + for (auto& url : urls) { + if (!m_retainedPageURLs.contains(url)) { + PageURLRecord* record = m_pageURLToRecordMap.get(url); if (record && !databaseCleanupCounter) { - m_pageURLToRecordMap.remove(urls[i]); + m_pageURLToRecordMap.remove(url); IconRecord* iconRecord = record->iconRecord(); // If this page is the only remaining retainer of its icon, mark that icon for deletion and don't bother @@ -1307,12 +1294,12 @@ void IconDatabase::performURLImport() m_iconURLToRecordMap.remove(iconRecord->iconURL()); { - MutexLocker locker(m_pendingReadingLock); - m_pageURLsInterestedInIcons.remove(urls[i]); + LockHolder locker(m_pendingReadingLock); + m_pageURLsInterestedInIcons.remove(url); m_iconsPendingReading.remove(iconRecord); } { - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true)); } } @@ -1320,18 +1307,18 @@ void IconDatabase::performURLImport() delete record; } } else { - urlsToNotify.append(urls[i]); + urlsToNotify.append(url); } } } LOG(IconDatabase, "Notifying %lu interested page URLs that their icon URL is known due to the import", static_cast<unsigned long>(urlsToNotify.size())); // Now that we don't hold any locks, perform the actual notifications - for (unsigned i = 0; i < urlsToNotify.size(); ++i) { + for (auto& url : urlsToNotify) { AutodrainedPool pool; - LOG(IconDatabase, "Notifying icon info known for pageURL %s", urlsToNotify[i].ascii().data()); - dispatchDidImportIconURLForPageURLOnMainThread(urlsToNotify[i]); + LOG(IconDatabase, "Notifying icon info known for pageURL %s", url.ascii().data()); + dispatchDidImportIconURLForPageURLOnMainThread(url); if (shouldStopThreadActivity()) return; } @@ -1340,7 +1327,9 @@ void IconDatabase::performURLImport() dispatchDidFinishURLImportOnMainThread(); // Notify all DocumentLoaders that were waiting for an icon load decision on the main thread - callOnMainThread(notifyPendingLoadDecisionsOnMainThread, this); + callOnMainThread([this] { + notifyPendingLoadDecisions(); + }); } void IconDatabase::syncThreadMainLoop() @@ -1349,7 +1338,7 @@ void IconDatabase::syncThreadMainLoop() m_syncLock.lock(); - std::unique_ptr<SuddenTerminationDisabler> disableSuddenTermination = std::move(m_disableSuddenTerminationWhileSyncThreadHasWorkToDo); + std::unique_ptr<SuddenTerminationDisabler> disableSuddenTermination = WTFMove(m_disableSuddenTerminationWhileSyncThreadHasWorkToDo); // We'll either do any pending work on our first pass through the loop, or we'll terminate // without doing any work. Either way we're dealing with any currently-pending work. @@ -1371,11 +1360,13 @@ void IconDatabase::syncThreadMainLoop() } // Then, if the thread should be quitting, quit now! - if (m_threadTerminationRequested) - break; + if (m_threadTerminationRequested) { + cleanupSyncThread(); + return; + } { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); performPendingRetainAndReleaseOperations(); } @@ -1437,7 +1428,7 @@ void IconDatabase::syncThreadMainLoop() m_syncThreadHasWorkToDo = false; ASSERT(m_disableSuddenTerminationWhileSyncThreadHasWorkToDo); - disableSuddenTermination = std::move(m_disableSuddenTerminationWhileSyncThreadHasWorkToDo); + disableSuddenTermination = WTFMove(m_disableSuddenTerminationWhileSyncThreadHasWorkToDo); } m_syncLock.unlock(); @@ -1455,7 +1446,7 @@ void IconDatabase::performPendingRetainAndReleaseOperations() HashCountedSet<String> toRelease; { - MutexLocker pendingWorkLocker(m_urlsToRetainOrReleaseLock); + LockHolder pendingWorkLocker(m_urlsToRetainOrReleaseLock); if (!m_retainOrReleaseIconRequested) return; @@ -1467,14 +1458,14 @@ void IconDatabase::performPendingRetainAndReleaseOperations() m_retainOrReleaseIconRequested = false; } - for (HashCountedSet<String>::const_iterator it = toRetain.begin(), end = toRetain.end(); it != end; ++it) { - ASSERT(!it->key.impl() || it->key.impl()->hasOneRef()); - performRetainIconForPageURL(it->key, it->value); + for (auto& entry : toRetain) { + ASSERT(!entry.key.impl() || entry.key.impl()->hasOneRef()); + performRetainIconForPageURL(entry.key, entry.value); } - for (HashCountedSet<String>::const_iterator it = toRelease.begin(), end = toRelease.end(); it != end; ++it) { - ASSERT(!it->key.impl() || it->key.impl()->hasOneRef()); - performReleaseIconForPageURL(it->key, it->value); + for (auto& entry : toRelease) { + ASSERT(!entry.key.impl() || entry.key.impl()->hasOneRef()); + performReleaseIconForPageURL(entry.key, entry.value); } } @@ -1492,7 +1483,7 @@ bool IconDatabase::readFromDatabase() // This way we won't hold the lock for a long period of time Vector<IconRecord*> icons; { - MutexLocker locker(m_pendingReadingLock); + LockHolder locker(m_pendingReadingLock); icons.appendRange(m_iconsPendingReading.begin(), m_iconsPendingReading.end()); } @@ -1501,17 +1492,17 @@ bool IconDatabase::readFromDatabase() for (unsigned i = 0; i < icons.size(); ++i) { didAnyWork = true; - RefPtr<SharedBuffer> imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL()); + auto imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL()); // Verify this icon still wants to be read from disk { - MutexLocker urlLocker(m_urlAndIconLock); + LockHolder urlLocker(m_urlAndIconLock); { - MutexLocker readLocker(m_pendingReadingLock); + LockHolder readLocker(m_pendingReadingLock); if (m_iconsPendingReading.contains(icons[i])) { // Set the new data - icons[i]->setImageData(imageData.release()); + icons[i]->setImageData(WTFMove(imageData)); // Remove this icon from the set that needs to be read m_iconsPendingReading.remove(icons[i]); @@ -1530,12 +1521,10 @@ bool IconDatabase::readFromDatabase() outerHash = &(icons[i]->retainingPageURLs()); } - HashSet<String>::const_iterator iter = outerHash->begin(); - HashSet<String>::const_iterator end = outerHash->end(); - for (; iter != end; ++iter) { - if (innerHash->contains(*iter)) { - LOG(IconDatabase, "%s is interested in the icon we just read. Adding it to the notification list and removing it from the interested set", urlForLogging(*iter).ascii().data()); - urlsToNotify.add(*iter); + for (auto& outer : *outerHash) { + if (innerHash->contains(outer)) { + LOG(IconDatabase, "%s is interested in the icon we just read. Adding it to the notification list and removing it from the interested set", urlForLogging(outer).ascii().data()); + urlsToNotify.add(outer); } // If we ever get to the point were we've seen every url interested in this icon, break early @@ -1547,10 +1536,8 @@ bool IconDatabase::readFromDatabase() if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size()) m_pageURLsInterestedInIcons.clear(); else { - iter = urlsToNotify.begin(); - end = urlsToNotify.end(); - for (; iter != end; ++iter) - m_pageURLsInterestedInIcons.remove(*iter); + for (auto& url : urlsToNotify) + m_pageURLsInterestedInIcons.remove(url); } } } @@ -1595,11 +1582,11 @@ bool IconDatabase::writeToDatabase() // we'll pick it up on the next pass. This greatly simplifies the locking strategy for this method and remains cohesive with changes // asked for by the database on the main thread { - MutexLocker locker(m_urlAndIconLock); + LockHolder locker(m_urlAndIconLock); Vector<IconSnapshot> iconSnapshots; Vector<PageURLSnapshot> pageSnapshots; { - MutexLocker locker(m_pendingSyncLock); + LockHolder locker(m_pendingSyncLock); iconSnapshots.appendRange(m_iconsPendingSync.begin().values(), m_iconsPendingSync.end().values()); m_iconsPendingSync.clear(); @@ -1614,19 +1601,19 @@ bool IconDatabase::writeToDatabase() SQLiteTransaction syncTransaction(m_syncDB); syncTransaction.begin(); - for (unsigned i = 0; i < iconSnapshots.size(); ++i) { - writeIconSnapshotToSQLDatabase(iconSnapshots[i]); - LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %i to the DB", urlForLogging(iconSnapshots[i].iconURL()).ascii().data(), iconSnapshots[i].timestamp()); + for (auto& snapshot : iconSnapshots) { + writeIconSnapshotToSQLDatabase(snapshot); + LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %i to the DB", urlForLogging(snapshot.iconURL()).ascii().data(), snapshot.timestamp()); } - for (unsigned i = 0; i < pageSnapshots.size(); ++i) { + for (auto& snapshot : pageSnapshots) { // If the icon URL is empty, this page is meant to be deleted // ASSERTs are sanity checks to make sure the mappings exist if they should and don't if they shouldn't - if (pageSnapshots[i].iconURL().isEmpty()) - removePageURLFromSQLDatabase(pageSnapshots[i].pageURL()); + if (snapshot.iconURL().isEmpty()) + removePageURLFromSQLDatabase(snapshot.pageURL()); else - setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL(), pageSnapshots[i].pageURL()); - LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL()).ascii().data()); + setIconURLForPageURLInSQLDatabase(snapshot.iconURL(), snapshot.pageURL()); + LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(snapshot.pageURL()).ascii().data()); } syncTransaction.commit(); @@ -1661,13 +1648,13 @@ void IconDatabase::pruneUnretainedIcons() pageSQL.prepare(); int result; - while ((result = pageSQL.step()) == SQLResultRow) { - MutexLocker locker(m_urlAndIconLock); + while ((result = pageSQL.step()) == SQLITE_ROW) { + LockHolder locker(m_urlAndIconLock); if (!m_pageURLToRecordMap.contains(pageSQL.getColumnText(1))) pageIDsToDelete.append(pageSQL.getColumnInt64(0)); } - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Error reading PageURL table from on-disk DB"); pageSQL.finalize(); @@ -1683,7 +1670,7 @@ void IconDatabase::pruneUnretainedIcons() LOG(IconDatabase, "Pruning page with rowid %lli from disk", static_cast<long long>(pageIDsToDelete[i])); pageDeleteSQL.bindInt64(1, pageIDsToDelete[i]); int result = pageDeleteSQL.step(); - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("Unabled to delete page with id %lli from disk", static_cast<long long>(pageIDsToDelete[i])); pageDeleteSQL.reset(); @@ -1762,20 +1749,20 @@ void IconDatabase::deleteAllPreparedStatements() { ASSERT_ICON_SYNC_THREAD(); - m_setIconIDForPageURLStatement.clear(); - m_removePageURLStatement.clear(); - m_getIconIDForIconURLStatement.clear(); - m_getImageDataForIconURLStatement.clear(); - m_addIconToIconInfoStatement.clear(); - m_addIconToIconDataStatement.clear(); - m_getImageDataStatement.clear(); - m_deletePageURLsForIconURLStatement.clear(); - m_deleteIconFromIconInfoStatement.clear(); - m_deleteIconFromIconDataStatement.clear(); - m_updateIconInfoStatement.clear(); - m_updateIconDataStatement.clear(); - m_setIconInfoStatement.clear(); - m_setIconDataStatement.clear(); + m_setIconIDForPageURLStatement = nullptr; + m_removePageURLStatement = nullptr; + m_getIconIDForIconURLStatement = nullptr; + m_getImageDataForIconURLStatement = nullptr; + m_addIconToIconInfoStatement = nullptr; + m_addIconToIconDataStatement = nullptr; + m_getImageDataStatement = nullptr; + m_deletePageURLsForIconURLStatement = nullptr; + m_deleteIconFromIconInfoStatement = nullptr; + m_deleteIconFromIconDataStatement = nullptr; + m_updateIconInfoStatement = nullptr; + m_updateIconDataStatement = nullptr; + m_setIconInfoStatement = nullptr; + m_setIconDataStatement = nullptr; } void* IconDatabase::cleanupSyncThread() @@ -1795,7 +1782,7 @@ void* IconDatabase::cleanupSyncThread() writeToDatabase(); // Close the database - MutexLocker locker(m_syncLock); + LockHolder locker(m_syncLock); m_databaseDirectory = String(); m_completeDatabasePath = String(); @@ -1807,23 +1794,23 @@ void* IconDatabase::cleanupSyncThread() #endif m_syncThreadRunning = false; - return 0; + return nullptr; } // readySQLiteStatement() handles two things // 1 - If the SQLDatabase& argument is different, the statement must be destroyed and remade. This happens when the user // switches to and from private browsing // 2 - Lazy construction of the Statement in the first place, in case we've never made this query before -inline void readySQLiteStatement(OwnPtr<SQLiteStatement>& statement, SQLiteDatabase& db, const String& str) +inline void readySQLiteStatement(std::unique_ptr<SQLiteStatement>& statement, SQLiteDatabase& db, const String& str) { - if (statement && (statement->database() != &db || statement->isExpired())) { + if (statement && (&statement->database() != &db || statement->isExpired())) { if (statement->isExpired()) LOG(IconDatabase, "SQLiteStatement associated with %s is expired", str.ascii().data()); - statement.clear(); + statement = nullptr; } if (!statement) { - statement = adoptPtr(new SQLiteStatement(db, str)); - if (statement->prepare() != SQLResultOk) + statement = std::make_unique<SQLiteStatement>(db, str); + if (statement->prepare() != SQLITE_OK) LOG_ERROR("Preparing statement %s failed", str.ascii().data()); } } @@ -1855,7 +1842,7 @@ void IconDatabase::setIconIDForPageURLInSQLDatabase(int64_t iconID, const String m_setIconIDForPageURLStatement->bindInt64(2, iconID); int result = m_setIconIDForPageURLStatement->step(); - if (result != SQLResultDone) { + if (result != SQLITE_DONE) { ASSERT_NOT_REACHED(); LOG_ERROR("setIconIDForPageURLQuery failed for url %s", urlForLogging(pageURL).ascii().data()); } @@ -1870,7 +1857,7 @@ void IconDatabase::removePageURLFromSQLDatabase(const String& pageURL) readySQLiteStatement(m_removePageURLStatement, m_syncDB, "DELETE FROM PageURL WHERE url = (?);"); m_removePageURLStatement->bindText(1, pageURL); - if (m_removePageURLStatement->step() != SQLResultDone) + if (m_removePageURLStatement->step() != SQLITE_DONE) LOG_ERROR("removePageURLFromSQLDatabase failed for url %s", urlForLogging(pageURL).ascii().data()); m_removePageURLStatement->reset(); @@ -1885,10 +1872,10 @@ int64_t IconDatabase::getIconIDForIconURLFromSQLDatabase(const String& iconURL) m_getIconIDForIconURLStatement->bindText(1, iconURL); int64_t result = m_getIconIDForIconURLStatement->step(); - if (result == SQLResultRow) + if (result == SQLITE_ROW) result = m_getIconIDForIconURLStatement->getColumnInt64(0); else { - if (result != SQLResultDone) + if (result != SQLITE_DONE) LOG_ERROR("getIconIDForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data()); result = 0; } @@ -1910,7 +1897,7 @@ int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL) int result = m_addIconToIconInfoStatement->step(); m_addIconToIconInfoStatement->reset(); - if (result != SQLResultDone) { + if (result != SQLITE_DONE) { LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconInfo", urlForLogging(iconURL).ascii().data()); return 0; } @@ -1921,7 +1908,7 @@ int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL) result = m_addIconToIconDataStatement->step(); m_addIconToIconDataStatement->reset(); - if (result != SQLResultDone) { + if (result != SQLITE_DONE) { LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconData", urlForLogging(iconURL).ascii().data()); return 0; } @@ -1929,7 +1916,7 @@ int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL) return iconID; } -PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(const String& iconURL) +RefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(const String& iconURL) { ASSERT_ICON_SYNC_THREAD(); @@ -1939,16 +1926,16 @@ PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(con m_getImageDataForIconURLStatement->bindText(1, iconURL); int result = m_getImageDataForIconURLStatement->step(); - if (result == SQLResultRow) { + if (result == SQLITE_ROW) { Vector<char> data; m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data); imageData = SharedBuffer::create(data.data(), data.size()); - } else if (result != SQLResultDone) + } else if (result != SQLITE_DONE) LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data()); m_getImageDataForIconURLStatement->reset(); - return imageData.release(); + return imageData; } void IconDatabase::removeIconFromSQLDatabase(const String& iconURL) @@ -1970,21 +1957,21 @@ void IconDatabase::removeIconFromSQLDatabase(const String& iconURL) readySQLiteStatement(m_deletePageURLsForIconURLStatement, m_syncDB, "DELETE FROM PageURL WHERE PageURL.iconID = (?);"); m_deletePageURLsForIconURLStatement->bindInt64(1, iconID); - if (m_deletePageURLsForIconURLStatement->step() != SQLResultDone) + if (m_deletePageURLsForIconURLStatement->step() != SQLITE_DONE) LOG_ERROR("m_deletePageURLsForIconURLStatement failed for url %s", urlForLogging(iconURL).ascii().data()); readySQLiteStatement(m_deleteIconFromIconInfoStatement, m_syncDB, "DELETE FROM IconInfo WHERE IconInfo.iconID = (?);"); m_deleteIconFromIconInfoStatement->bindInt64(1, iconID); - if (m_deleteIconFromIconInfoStatement->step() != SQLResultDone) + if (m_deleteIconFromIconInfoStatement->step() != SQLITE_DONE) LOG_ERROR("m_deleteIconFromIconInfoStatement failed for url %s", urlForLogging(iconURL).ascii().data()); - + readySQLiteStatement(m_deleteIconFromIconDataStatement, m_syncDB, "DELETE FROM IconData WHERE IconData.iconID = (?);"); m_deleteIconFromIconDataStatement->bindInt64(1, iconID); - if (m_deleteIconFromIconDataStatement->step() != SQLResultDone) + if (m_deleteIconFromIconDataStatement->step() != SQLITE_DONE) LOG_ERROR("m_deleteIconFromIconDataStatement failed for url %s", urlForLogging(iconURL).ascii().data()); - + m_deletePageURLsForIconURLStatement->reset(); m_deleteIconFromIconInfoStatement->reset(); m_deleteIconFromIconDataStatement->reset(); @@ -2018,7 +2005,7 @@ void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot) m_updateIconInfoStatement->bindText(2, snapshot.iconURL()); m_updateIconInfoStatement->bindInt64(3, iconID); - if (m_updateIconInfoStatement->step() != SQLResultDone) + if (m_updateIconInfoStatement->step() != SQLITE_DONE) LOG_ERROR("Failed to update icon info for url %s", urlForLogging(snapshot.iconURL()).ascii().data()); m_updateIconInfoStatement->reset(); @@ -2033,7 +2020,7 @@ void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot) else m_updateIconDataStatement->bindNull(1); - if (m_updateIconDataStatement->step() != SQLResultDone) + if (m_updateIconDataStatement->step() != SQLITE_DONE) LOG_ERROR("Failed to update icon data for url %s", urlForLogging(snapshot.iconURL()).ascii().data()); m_updateIconDataStatement->reset(); @@ -2042,7 +2029,7 @@ void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot) m_setIconInfoStatement->bindText(1, snapshot.iconURL()); m_setIconInfoStatement->bindInt64(2, snapshot.timestamp()); - if (m_setIconInfoStatement->step() != SQLResultDone) + if (m_setIconInfoStatement->step() != SQLITE_DONE) LOG_ERROR("Failed to set icon info for url %s", urlForLogging(snapshot.iconURL()).ascii().data()); m_setIconInfoStatement->reset(); @@ -2059,7 +2046,7 @@ void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot) else m_setIconDataStatement->bindNull(2); - if (m_setIconDataStatement->step() != SQLResultDone) + if (m_setIconDataStatement->step() != SQLITE_DONE) LOG_ERROR("Failed to set icon data for url %s", urlForLogging(snapshot.iconURL()).ascii().data()); m_setIconDataStatement->reset(); @@ -2080,133 +2067,71 @@ void IconDatabase::setWasExcludedFromBackup() SQLiteStatement(m_syncDB, "INSERT INTO IconDatabaseInfo (key, value) VALUES ('ExcludedFromBackup', 1)").executeCommand(); } -class ClientWorkItem { - WTF_MAKE_FAST_ALLOCATED; -public: - ClientWorkItem(IconDatabaseClient* client) - : m_client(client) - { } - virtual void performWork() = 0; - virtual ~ClientWorkItem() { } - -protected: - IconDatabaseClient* m_client; -}; - -class ImportedIconURLForPageURLWorkItem : public ClientWorkItem { -public: - ImportedIconURLForPageURLWorkItem(IconDatabaseClient* client, const String& pageURL) - : ClientWorkItem(client) - , m_pageURL(new String(pageURL.isolatedCopy())) - { } - - virtual ~ImportedIconURLForPageURLWorkItem() - { - delete m_pageURL; - } - - virtual void performWork() - { - ASSERT(m_client); - m_client->didImportIconURLForPageURL(*m_pageURL); - m_client = 0; - } - -private: - String* m_pageURL; -}; - -class ImportedIconDataForPageURLWorkItem : public ClientWorkItem { -public: - ImportedIconDataForPageURLWorkItem(IconDatabaseClient* client, const String& pageURL) - : ClientWorkItem(client) - , m_pageURL(new String(pageURL.isolatedCopy())) - { } - - virtual ~ImportedIconDataForPageURLWorkItem() - { - delete m_pageURL; - } - - virtual void performWork() - { - ASSERT(m_client); - m_client->didImportIconDataForPageURL(*m_pageURL); - m_client = 0; - } - -private: - String* m_pageURL; -}; - -class RemovedAllIconsWorkItem : public ClientWorkItem { -public: - RemovedAllIconsWorkItem(IconDatabaseClient* client) - : ClientWorkItem(client) - { } - - virtual void performWork() - { - ASSERT(m_client); - m_client->didRemoveAllIcons(); - m_client = 0; - } -}; +void IconDatabase::checkClosedAfterMainThreadCallback() +{ + ASSERT_NOT_SYNC_THREAD(); -class FinishedURLImport : public ClientWorkItem { -public: - FinishedURLImport(IconDatabaseClient* client) - : ClientWorkItem(client) - { } + // If there are still callbacks in flight from the sync thread we cannot possibly be closed. + if (--m_mainThreadCallbackCount) + return; - virtual void performWork() - { - ASSERT(m_client); - m_client->didFinishURLImport(); - m_client = 0; - } -}; + // Even if there's no more pending callbacks the database might otherwise still be open. + if (isOpenBesidesMainThreadCallbacks()) + return; -static void performWorkItem(void* context) -{ - ClientWorkItem* item = static_cast<ClientWorkItem*>(context); - item->performWork(); - delete item; + // This database is now actually closed! But first notify the client. + if (m_client) + m_client->didClose(); } void IconDatabase::dispatchDidImportIconURLForPageURLOnMainThread(const String& pageURL) { ASSERT_ICON_SYNC_THREAD(); + ++m_mainThreadCallbackCount; - ImportedIconURLForPageURLWorkItem* work = new ImportedIconURLForPageURLWorkItem(m_client, pageURL); - callOnMainThread(performWorkItem, work); + callOnMainThread([this, pageURL = pageURL.isolatedCopy()] { + if (m_client) + m_client->didImportIconURLForPageURL(pageURL); + checkClosedAfterMainThreadCallback(); + }); } void IconDatabase::dispatchDidImportIconDataForPageURLOnMainThread(const String& pageURL) { ASSERT_ICON_SYNC_THREAD(); + ++m_mainThreadCallbackCount; - ImportedIconDataForPageURLWorkItem* work = new ImportedIconDataForPageURLWorkItem(m_client, pageURL); - callOnMainThread(performWorkItem, work); + callOnMainThread([this, pageURL = pageURL.isolatedCopy()] { + if (m_client) + m_client->didImportIconDataForPageURL(pageURL); + checkClosedAfterMainThreadCallback(); + }); } void IconDatabase::dispatchDidRemoveAllIconsOnMainThread() { ASSERT_ICON_SYNC_THREAD(); + ++m_mainThreadCallbackCount; - RemovedAllIconsWorkItem* work = new RemovedAllIconsWorkItem(m_client); - callOnMainThread(performWorkItem, work); + callOnMainThread([this] { + if (m_client) + m_client->didRemoveAllIcons(); + checkClosedAfterMainThreadCallback(); + }); } void IconDatabase::dispatchDidFinishURLImportOnMainThread() { ASSERT_ICON_SYNC_THREAD(); + ++m_mainThreadCallbackCount; - FinishedURLImport* work = new FinishedURLImport(m_client); - callOnMainThread(performWorkItem, work); + callOnMainThread([this] { + if (m_client) + m_client->didFinishURLImport(); + checkClosedAfterMainThreadCallback(); + }); } - } // namespace WebCore #endif // ENABLE(ICONDATABASE) diff --git a/Source/WebCore/loader/icon/IconDatabase.h b/Source/WebCore/loader/icon/IconDatabase.h index 04db323a7..061af4a12 100644 --- a/Source/WebCore/loader/icon/IconDatabase.h +++ b/Source/WebCore/loader/icon/IconDatabase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2014 Apple Inc. All rights reserved. * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -24,113 +24,101 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IconDatabase_h -#define IconDatabase_h +#pragma once #include "IconDatabaseBase.h" -#include "Timer.h" -#include <wtf/HashCountedSet.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/text/StringHash.h> #include <wtf/text/WTFString.h> #if ENABLE(ICONDATABASE) #include "SQLiteDatabase.h" -#include <wtf/Threading.h> -#endif // ENABLE(ICONDATABASE) +#include "Timer.h" +#include <wtf/Condition.h> +#include <wtf/HashCountedSet.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#endif namespace WebCore { -class DocumentLoader; -class Image; -class IntSize; -class IconDatabaseClient; -class IconRecord; -class IconSnapshot; -class URL; -class PageURLRecord; -class PageURLSnapshot; -class SharedBuffer; -class SuddenTerminationDisabler; +#if !ENABLE(ICONDATABASE) -#if ENABLE(ICONDATABASE) -class SQLTransaction; -#endif +// Dummy version of IconDatabase that does nothing. +class IconDatabase final : public IconDatabaseBase { + WTF_MAKE_FAST_ALLOCATED; -#if !ENABLE(ICONDATABASE) -// For builds with IconDatabase disabled, they'll just use a default derivation of IconDatabaseBase. Which does nothing. -class IconDatabase : public IconDatabaseBase { public: - static PassOwnPtr<IconDatabase> create() { return adoptPtr(new IconDatabase); } static void delayDatabaseCleanup() { } static void allowDatabaseCleanup() { } static void checkIntegrityBeforeOpening() { } - static String defaultDatabaseFilename() { return "WebpageIcons.db"; } + + // FIXME: Is it really helpful to return a filename here rather than just the null string? + static String defaultDatabaseFilename() { return ASCIILiteral("WebpageIcons.db"); } }; -#else -class IconDatabase : public IconDatabaseBase { +#else + +class IconRecord; +class IconSnapshot; +class PageURLRecord; +class PageURLSnapshot; +class SuddenTerminationDisabler; + +class IconDatabase final : public IconDatabaseBase { WTF_MAKE_FAST_ALLOCATED; // *** Main Thread Only *** public: - static PassOwnPtr<IconDatabase> create() { return adoptPtr(new IconDatabase); } + WEBCORE_EXPORT IconDatabase(); ~IconDatabase(); - virtual void setClient(IconDatabaseClient*); + WEBCORE_EXPORT void setClient(IconDatabaseClient*) final; - virtual bool open(const String& directory, const String& filename); - virtual void close(); + WEBCORE_EXPORT bool open(const String& directory, const String& filename) final; + WEBCORE_EXPORT void close() final; - virtual void removeAllIcons(); + WEBCORE_EXPORT void removeAllIcons() final; void readIconForPageURLFromDisk(const String&); - virtual Image* defaultIcon(const IntSize&); + WEBCORE_EXPORT Image* defaultIcon(const IntSize&) final; - virtual void retainIconForPageURL(const String&); - virtual void releaseIconForPageURL(const String&); - virtual void setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String&); - virtual void setIconURLForPageURL(const String& iconURL, const String& pageURL); + WEBCORE_EXPORT void retainIconForPageURL(const String&) final; + WEBCORE_EXPORT void releaseIconForPageURL(const String&) final; + WEBCORE_EXPORT void setIconDataForIconURL(SharedBuffer* data, const String& iconURL) final; + WEBCORE_EXPORT void setIconURLForPageURL(const String& iconURL, const String& pageURL) final; - virtual Image* synchronousIconForPageURL(const String&, const IntSize&); - virtual PassNativeImagePtr synchronousNativeIconForPageURL(const String& pageURLOriginal, const IntSize&); - virtual String synchronousIconURLForPageURL(const String&); - virtual bool synchronousIconDataKnownForIconURL(const String&); - virtual IconLoadDecision synchronousLoadDecisionForIconURL(const String&, DocumentLoader*); - - virtual void setEnabled(bool); - virtual bool isEnabled() const; - - virtual void setPrivateBrowsingEnabled(bool flag); + WEBCORE_EXPORT Image* synchronousIconForPageURL(const String&, const IntSize&) final; + NativeImagePtr synchronousNativeIconForPageURL(const String& pageURLOriginal, const IntSize&) final; + WEBCORE_EXPORT String synchronousIconURLForPageURL(const String&) final; + bool synchronousIconDataKnownForIconURL(const String&) final; + WEBCORE_EXPORT IconLoadDecision synchronousLoadDecisionForIconURL(const String&, DocumentLoader*) final; + + WEBCORE_EXPORT void setEnabled(bool); + WEBCORE_EXPORT bool isEnabled() const final; + + WEBCORE_EXPORT void setPrivateBrowsingEnabled(bool flag) final; bool isPrivateBrowsingEnabled() const; - - static void delayDatabaseCleanup(); - static void allowDatabaseCleanup(); - static void checkIntegrityBeforeOpening(); - + + WEBCORE_EXPORT static void delayDatabaseCleanup(); + WEBCORE_EXPORT static void allowDatabaseCleanup(); + WEBCORE_EXPORT static void checkIntegrityBeforeOpening(); + // Support for WebCoreStatistics in WebKit - virtual size_t pageURLMappingCount(); - virtual size_t retainedPageURLCount(); - virtual size_t iconRecordCount(); - virtual size_t iconRecordCountWithData(); + WEBCORE_EXPORT size_t pageURLMappingCount() final; + WEBCORE_EXPORT size_t retainedPageURLCount() final; + WEBCORE_EXPORT size_t iconRecordCount() final; + WEBCORE_EXPORT size_t iconRecordCountWithData() final; private: - IconDatabase(); friend IconDatabaseBase& iconDatabase(); - static void notifyPendingLoadDecisionsOnMainThread(void*); void notifyPendingLoadDecisions(); void wakeSyncThread(); void scheduleOrDeferSyncTimer(); - void syncTimerFired(Timer<IconDatabase>&); + void syncTimerFired(); - Timer<IconDatabase> m_syncTimer; + Timer m_syncTimer; ThreadIdentifier m_syncThread; bool m_syncThreadRunning; @@ -138,27 +126,24 @@ private: RefPtr<IconRecord> m_defaultIconRecord; - static void performScheduleOrDeferSyncTimerOnMainThread(void*); - void performScheduleOrDeferSyncTimer(); - bool m_scheduleOrDeferSyncTimerRequested; std::unique_ptr<SuddenTerminationDisabler> m_disableSuddenTerminationWhileSyncTimerScheduled; // *** Any Thread *** public: - virtual bool isOpen() const; - virtual String databasePath() const; - static String defaultDatabaseFilename(); + WEBCORE_EXPORT bool isOpen() const final; + WEBCORE_EXPORT String databasePath() const final; + WEBCORE_EXPORT static String defaultDatabaseFilename(); private: - PassRefPtr<IconRecord> getOrCreateIconRecord(const String& iconURL); + Ref<IconRecord> getOrCreateIconRecord(const String& iconURL); PageURLRecord* getOrCreatePageURLRecord(const String& pageURL); bool m_isEnabled; bool m_privateBrowsingEnabled; - mutable Mutex m_syncLock; - ThreadCondition m_syncCondition; + mutable Lock m_syncLock; + Condition m_syncCondition; String m_databaseDirectory; // Holding m_syncLock is required when accessing m_completeDatabasePath String m_completeDatabasePath; @@ -169,24 +154,24 @@ private: bool m_syncThreadHasWorkToDo; std::unique_ptr<SuddenTerminationDisabler> m_disableSuddenTerminationWhileSyncThreadHasWorkToDo; - Mutex m_urlAndIconLock; + Lock m_urlAndIconLock; // Holding m_urlAndIconLock is required when accessing any of the following data structures or the objects they contain HashMap<String, IconRecord*> m_iconURLToRecordMap; HashMap<String, PageURLRecord*> m_pageURLToRecordMap; HashSet<String> m_retainedPageURLs; - Mutex m_pendingSyncLock; + Lock m_pendingSyncLock; // Holding m_pendingSyncLock is required when accessing any of the following data structures HashMap<String, PageURLSnapshot> m_pageURLsPendingSync; HashMap<String, IconSnapshot> m_iconsPendingSync; - Mutex m_pendingReadingLock; + Lock m_pendingReadingLock; // Holding m_pendingSyncLock is required when accessing any of the following data structures - when dealing with IconRecord*s, holding m_urlAndIconLock is also required HashSet<String> m_pageURLsPendingImport; HashSet<String> m_pageURLsInterestedInIcons; HashSet<IconRecord*> m_iconsPendingReading; - Mutex m_urlsToRetainOrReleaseLock; + Lock m_urlsToRetainOrReleaseLock; // Holding m_urlsToRetainOrReleaseLock is required when accessing any of the following data structures. HashCountedSet<String> m_urlsToRetain; HashCountedSet<String> m_urlsToRelease; @@ -194,7 +179,7 @@ private: // *** Sync Thread Only *** public: - virtual bool shouldStopThreadActivity() const; + WEBCORE_EXPORT bool shouldStopThreadActivity() const final; private: static void iconDatabaseSyncThreadStart(void *); @@ -219,6 +204,9 @@ private: bool wasExcludedFromBackup(); void setWasExcludedFromBackup(); + bool isOpenBesidesMainThreadCallbacks() const; + void checkClosedAfterMainThreadCallback(); + bool m_initialPruningComplete; void setIconURLForPageURLInSQLDatabase(const String&, const String&); @@ -226,7 +214,7 @@ private: void removePageURLFromSQLDatabase(const String& pageURL); int64_t getIconIDForIconURLFromSQLDatabase(const String& iconURL); int64_t addIconURLToSQLDatabase(const String&); - PassRefPtr<SharedBuffer> getImageDataForIconURLFromSQLDatabase(const String& iconURL); + RefPtr<SharedBuffer> getImageDataForIconURLFromSQLDatabase(const String& iconURL); void removeIconFromSQLDatabase(const String& iconURL); void writeIconSnapshotToSQLDatabase(const IconSnapshot&); @@ -237,30 +225,29 @@ private: void dispatchDidImportIconDataForPageURLOnMainThread(const String&); void dispatchDidRemoveAllIconsOnMainThread(); void dispatchDidFinishURLImportOnMainThread(); + std::atomic<uint32_t> m_mainThreadCallbackCount; // The client is set by the main thread before the thread starts, and from then on is only used by the sync thread IconDatabaseClient* m_client; SQLiteDatabase m_syncDB; - OwnPtr<SQLiteStatement> m_setIconIDForPageURLStatement; - OwnPtr<SQLiteStatement> m_removePageURLStatement; - OwnPtr<SQLiteStatement> m_getIconIDForIconURLStatement; - OwnPtr<SQLiteStatement> m_getImageDataForIconURLStatement; - OwnPtr<SQLiteStatement> m_addIconToIconInfoStatement; - OwnPtr<SQLiteStatement> m_addIconToIconDataStatement; - OwnPtr<SQLiteStatement> m_getImageDataStatement; - OwnPtr<SQLiteStatement> m_deletePageURLsForIconURLStatement; - OwnPtr<SQLiteStatement> m_deleteIconFromIconInfoStatement; - OwnPtr<SQLiteStatement> m_deleteIconFromIconDataStatement; - OwnPtr<SQLiteStatement> m_updateIconInfoStatement; - OwnPtr<SQLiteStatement> m_updateIconDataStatement; - OwnPtr<SQLiteStatement> m_setIconInfoStatement; - OwnPtr<SQLiteStatement> m_setIconDataStatement; + std::unique_ptr<SQLiteStatement> m_setIconIDForPageURLStatement; + std::unique_ptr<SQLiteStatement> m_removePageURLStatement; + std::unique_ptr<SQLiteStatement> m_getIconIDForIconURLStatement; + std::unique_ptr<SQLiteStatement> m_getImageDataForIconURLStatement; + std::unique_ptr<SQLiteStatement> m_addIconToIconInfoStatement; + std::unique_ptr<SQLiteStatement> m_addIconToIconDataStatement; + std::unique_ptr<SQLiteStatement> m_getImageDataStatement; + std::unique_ptr<SQLiteStatement> m_deletePageURLsForIconURLStatement; + std::unique_ptr<SQLiteStatement> m_deleteIconFromIconInfoStatement; + std::unique_ptr<SQLiteStatement> m_deleteIconFromIconDataStatement; + std::unique_ptr<SQLiteStatement> m_updateIconInfoStatement; + std::unique_ptr<SQLiteStatement> m_updateIconDataStatement; + std::unique_ptr<SQLiteStatement> m_setIconInfoStatement; + std::unique_ptr<SQLiteStatement> m_setIconDataStatement; }; #endif // !ENABLE(ICONDATABASE) } // namespace WebCore - -#endif // IconDatabase_h diff --git a/Source/WebCore/loader/icon/IconDatabaseBase.cpp b/Source/WebCore/loader/icon/IconDatabaseBase.cpp index 51ccb3b27..4c6931071 100644 --- a/Source/WebCore/loader/icon/IconDatabaseBase.cpp +++ b/Source/WebCore/loader/icon/IconDatabaseBase.cpp @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -47,7 +47,7 @@ bool IconDatabaseBase::open(const String&, const String&) return false; } -static IconDatabaseBase* vmbase = 0; +static IconDatabaseBase* vmbase = nullptr; // Functions to get/set the global icon database. IconDatabaseBase& iconDatabase() @@ -55,7 +55,7 @@ IconDatabaseBase& iconDatabase() if (vmbase) return *vmbase; - static IconDatabaseBase* defaultDatabase = 0; + static IconDatabaseBase* defaultDatabase = nullptr; if (!defaultDatabase) defaultDatabase = new IconDatabase; diff --git a/Source/WebCore/loader/icon/IconDatabaseBase.h b/Source/WebCore/loader/icon/IconDatabaseBase.h index e1f63af94..d4051b936 100644 --- a/Source/WebCore/loader/icon/IconDatabaseBase.h +++ b/Source/WebCore/loader/icon/IconDatabaseBase.h @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -23,15 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IconDatabaseBase_h -#define IconDatabaseBase_h - -#include "ImageSource.h" -#include "SharedBuffer.h" +#pragma once +#include "NativeImage.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> namespace WebCore { @@ -39,12 +36,9 @@ class DocumentLoader; class IconDatabaseClient; class Image; class IntSize; +class SharedBuffer; -enum IconLoadDecision { - IconLoadYes, - IconLoadNo, - IconLoadUnknown -}; +enum IconLoadDecision { IconLoadYes, IconLoadNo, IconLoadUnknown }; class CallbackBase : public RefCounted<CallbackBase> { public: @@ -79,9 +73,9 @@ class EnumCallback : public CallbackBase { public: typedef void (*CallbackFunction)(EnumType, void*); - static PassRefPtr<EnumCallback> create(void* context, CallbackFunction callback) + static Ref<EnumCallback> create(void* context, CallbackFunction callback) { - return adoptRef(new EnumCallback(context, callback)); + return adoptRef(*new EnumCallback(context, callback)); } virtual ~EnumCallback() @@ -94,12 +88,12 @@ public: if (!m_callback) return; m_callback(result, context()); - m_callback = 0; + m_callback = nullptr; } void invalidate() { - m_callback = 0; + m_callback = nullptr; } private: @@ -118,9 +112,9 @@ class ObjectCallback : public CallbackBase { public: typedef void (*CallbackFunction)(ObjectType, void*); - static PassRefPtr<ObjectCallback> create(void* context, CallbackFunction callback) + static Ref<ObjectCallback> create(void* context, CallbackFunction callback) { - return adoptRef(new ObjectCallback(context, callback)); + return adoptRef(*new ObjectCallback(context, callback)); } virtual ~ObjectCallback() @@ -133,12 +127,12 @@ public: if (!m_callback) return; m_callback(result, context()); - m_callback = 0; + m_callback = nullptr; } - + void invalidate() { - m_callback = 0; + m_callback = nullptr; } private: @@ -152,46 +146,42 @@ private: CallbackFunction m_callback; }; -typedef EnumCallback<IconLoadDecision> IconLoadDecisionCallback; -typedef ObjectCallback<SharedBuffer*> IconDataCallback; +using IconLoadDecisionCallback = EnumCallback<IconLoadDecision>; +using IconDataCallback = ObjectCallback<SharedBuffer*>; -class IconDatabaseBase { +class WEBCORE_EXPORT IconDatabaseBase { WTF_MAKE_NONCOPYABLE(IconDatabaseBase); -protected: - IconDatabaseBase() { } - public: virtual ~IconDatabaseBase() { } - // Used internally by WebCore virtual bool isEnabled() const { return false; } - + virtual void retainIconForPageURL(const String&) { } virtual void releaseIconForPageURL(const String&) { } - virtual void setIconURLForPageURL(const String&, const String&) { } - virtual void setIconDataForIconURL(PassRefPtr<SharedBuffer>, const String&) { } + virtual void setIconURLForPageURL(const String& /*iconURL*/, const String& /*pageURL*/) { } + virtual void setIconDataForIconURL(SharedBuffer*, const String& /*iconURL*/) { } // Synchronous calls used internally by WebCore. // Usage should be replaced by asynchronous calls. virtual String synchronousIconURLForPageURL(const String&); virtual bool synchronousIconDataKnownForIconURL(const String&) { return false; } virtual IconLoadDecision synchronousLoadDecisionForIconURL(const String&, DocumentLoader*) { return IconLoadNo; } - virtual Image* synchronousIconForPageURL(const String&, const IntSize&) { return 0; } - virtual PassNativeImagePtr synchronousNativeIconForPageURL(const String&, const IntSize&) { return 0; } + virtual Image* synchronousIconForPageURL(const String&, const IntSize&) { return nullptr; } + virtual NativeImagePtr synchronousNativeIconForPageURL(const String&, const IntSize&) { return nullptr; } // Asynchronous calls we should use to replace the above when supported. virtual bool supportsAsynchronousMode() { return false; } - virtual void loadDecisionForIconURL(const String&, PassRefPtr<IconLoadDecisionCallback>) { } - virtual void iconDataForIconURL(const String&, PassRefPtr<IconDataCallback>) { } - + virtual void loadDecisionForIconURL(const String&, IconLoadDecisionCallback&) { } + virtual void iconDataForIconURL(const String&, IconDataCallback&) { } // Used within one or more WebKit ports. // We should try to remove these dependencies from the IconDatabaseBase class. + virtual void setEnabled(bool) { } - virtual Image* defaultIcon(const IntSize&) { return 0; } + virtual Image* defaultIcon(const IntSize&) { return nullptr; } virtual size_t pageURLMappingCount() { return 0; } virtual size_t retainedPageURLCount() { return 0; } @@ -206,17 +196,17 @@ public: virtual void setPrivateBrowsingEnabled(bool) { } virtual void setClient(IconDatabaseClient*) { } - + virtual bool isOpen() const { return false; } virtual String databasePath() const; +protected: + IconDatabaseBase() = default; }; // Functions to get/set the global icon database. -IconDatabaseBase& iconDatabase(); -void setGlobalIconDatabase(IconDatabaseBase*); +WEBCORE_EXPORT IconDatabaseBase& iconDatabase(); +WEBCORE_EXPORT void setGlobalIconDatabase(IconDatabaseBase*); bool documentCanHaveIcon(const String&); } // namespace WebCore - -#endif // IconDatabaseBase_h diff --git a/Source/WebCore/loader/icon/IconDatabaseClient.h b/Source/WebCore/loader/icon/IconDatabaseClient.h index 70494ec11..33e5413a8 100644 --- a/Source/WebCore/loader/icon/IconDatabaseClient.h +++ b/Source/WebCore/loader/icon/IconDatabaseClient.h @@ -10,7 +10,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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IconDatabaseClient_h -#define IconDatabaseClient_h +#pragma once #include <wtf/Forward.h> @@ -42,8 +41,7 @@ public: virtual void didChangeIconForPageURL(const String&) = 0; virtual void didRemoveAllIcons() = 0; virtual void didFinishURLImport() = 0; + virtual void didClose() { } }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/loader/icon/IconLoader.cpp b/Source/WebCore/loader/icon/IconLoader.cpp index 819bfb7e6..ab6385e55 100644 --- a/Source/WebCore/loader/icon/IconLoader.cpp +++ b/Source/WebCore/loader/icon/IconLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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 @@ -37,14 +37,21 @@ #include "IconController.h" #include "IconDatabase.h" #include "Logging.h" -#include "ResourceBuffer.h" #include "ResourceRequest.h" +#include "SharedBuffer.h" #include <wtf/text/CString.h> namespace WebCore { IconLoader::IconLoader(Frame& frame) - : m_frame(frame) + : m_frame(&frame) + , m_url(frame.loader().icon().url()) +{ +} + +IconLoader::IconLoader(DocumentLoader& documentLoader, const URL& url) + : m_documentLoader(&documentLoader) + , m_url(url) { } @@ -55,54 +62,83 @@ IconLoader::~IconLoader() void IconLoader::startLoading() { - if (m_resource || !m_frame.document()) + ASSERT(m_frame || m_documentLoader); + + if (m_resource) return; - CachedResourceRequest request(ResourceRequest(m_frame.loader().icon().url()), ResourceLoaderOptions(SendCallbacks, SniffContent, BufferData, DoNotAllowStoredCredentials, DoNotAskClientForAnyCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType)); + if (m_frame && !m_frame->document()) + return; + + + if (m_documentLoader && !m_documentLoader->frame()) + return; + + ResourceRequest resourceRequest = m_documentLoader ? m_url : m_frame->loader().icon().url(); + resourceRequest.setPriority(ResourceLoadPriority::Low); +#if !ERROR_DISABLED + // Copy this because we may want to access it after transferring the + // `resourceRequest` to the `request`. If we don't, then the LOG_ERROR + // below won't print a URL. + auto resourceRequestURL = resourceRequest.url(); +#endif + + // ContentSecurityPolicyImposition::DoPolicyCheck is a placeholder value. It does not affect the request since Content Security Policy does not apply to raw resources. + CachedResourceRequest request(WTFMove(resourceRequest), ResourceLoaderOptions(SendCallbacks, SniffContent, BufferData, DoNotAllowStoredCredentials, ClientCredentialPolicy::CannotAskClientForCredentials, FetchOptions::Credentials::Omit, DoSecurityCheck, FetchOptions::Mode::NoCors, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, CachingPolicy::AllowCaching)); - request.mutableResourceRequest().setPriority(ResourceLoadPriorityLow); request.setInitiator(cachedResourceRequestInitiators().icon); - m_resource = m_frame.document()->cachedResourceLoader()->requestRawResource(request); + auto* frame = m_frame ? m_frame : m_documentLoader->frame(); + m_resource = frame->document()->cachedResourceLoader().requestRawResource(WTFMove(request)); if (m_resource) - m_resource->addClient(this); + m_resource->addClient(*this); else - LOG_ERROR("Failed to start load for icon at url %s", m_frame.loader().icon().url().string().ascii().data()); + LOG_ERROR("Failed to start load for icon at url %s", resourceRequestURL.string().ascii().data()); } void IconLoader::stopLoading() { if (m_resource) { - m_resource->removeClient(this); - m_resource = 0; + m_resource->removeClient(*this); + m_resource = nullptr; } } -void IconLoader::notifyFinished(CachedResource* resource) +void IconLoader::notifyFinished(CachedResource& resource) { - ASSERT(resource == m_resource); + ASSERT_UNUSED(resource, &resource == m_resource); // If we got a status code indicating an invalid response, then lets // ignore the data and not try to decode the error page as an icon. - RefPtr<ResourceBuffer> data = resource->resourceBuffer(); - int status = resource->response().httpStatusCode(); + auto* data = m_resource->resourceBuffer(); + int status = m_resource->response().httpStatusCode(); if (status && (status < 200 || status > 299)) - data = 0; + data = nullptr; static const char pdfMagicNumber[] = "%PDF"; static unsigned pdfMagicNumberLength = sizeof(pdfMagicNumber) - 1; if (data && data->size() >= pdfMagicNumberLength && !memcmp(data->data(), pdfMagicNumber, pdfMagicNumberLength)) { - LOG(IconDatabase, "IconLoader::finishLoading() - Ignoring icon at %s because it appears to be a PDF", resource->url().string().ascii().data()); - data = 0; + LOG(IconDatabase, "IconLoader::finishLoading() - Ignoring icon at %s because it appears to be a PDF", m_resource->url().string().ascii().data()); + data = nullptr; + } + + LOG(IconDatabase, "IconLoader::finishLoading() - Committing iconURL %s to database", m_resource->url().string().ascii().data()); + + if (!m_frame) { + // DocumentLoader::finishedLoadingIcon destroys this IconLoader as it finishes. This will automatically + // trigger IconLoader::stopLoading() during destruction, so we should just return here. + m_documentLoader->finishedLoadingIcon(*this, data); + return; } - LOG(IconDatabase, "IconLoader::finishLoading() - Committing iconURL %s to database", resource->url().string().ascii().data()); - m_frame.loader().icon().commitToDatabase(resource->url()); + m_frame->loader().icon().commitToDatabase(m_resource->url()); + // Setting the icon data only after committing to the database ensures that the data is // kept in memory (so it does not have to be read from the database asynchronously), since // there is a page URL referencing it. - iconDatabase().setIconDataForIconURL(data ? data->sharedBuffer() : 0, resource->url().string()); - m_frame.loader().client().dispatchDidReceiveIcon(); + iconDatabase().setIconDataForIconURL(data, m_resource->url().string()); + m_frame->loader().client().dispatchDidReceiveIcon(); + stopLoading(); } diff --git a/Source/WebCore/loader/icon/IconLoader.h b/Source/WebCore/loader/icon/IconLoader.h index 2fb79dc34..2de53e8e2 100644 --- a/Source/WebCore/loader/icon/IconLoader.h +++ b/Source/WebCore/loader/icon/IconLoader.h @@ -10,49 +10,50 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE 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 COMPUTER, INC. OR + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IconLoader_h -#define IconLoader_h +#pragma once #include "CachedRawResourceClient.h" #include "CachedResourceHandle.h" +#include "URL.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> -#include <wtf/RefPtr.h> namespace WebCore { class CachedRawResource; +class DocumentLoader; class Frame; class IconLoader final : private CachedRawResourceClient { WTF_MAKE_NONCOPYABLE(IconLoader); WTF_MAKE_FAST_ALLOCATED; public: explicit IconLoader(Frame&); + IconLoader(DocumentLoader&, const URL&); virtual ~IconLoader(); void startLoading(); void stopLoading(); private: - virtual void notifyFinished(CachedResource*) override; + void notifyFinished(CachedResource&) final; - Frame& m_frame; + Frame* m_frame { nullptr }; + DocumentLoader* m_documentLoader { nullptr }; + URL m_url; CachedResourceHandle<CachedRawResource> m_resource; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/loader/icon/IconRecord.cpp b/Source/WebCore/loader/icon/IconRecord.cpp index 7e90d8e8c..b20c26a32 100644 --- a/Source/WebCore/loader/icon/IconRecord.cpp +++ b/Source/WebCore/loader/icon/IconRecord.cpp @@ -10,7 +10,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. * @@ -62,16 +62,16 @@ Image* IconRecord::image(const IntSize&) return m_image.get(); } -void IconRecord::setImageData(PassRefPtr<SharedBuffer> data) +void IconRecord::setImageData(RefPtr<SharedBuffer>&& data) { // It's okay to delete the raw image here. Any existing clients using this icon will be // managing an image that was created with a copy of this raw image data. m_image = BitmapImage::create(); // Copy the provided data into the buffer of the new Image object. - if (!m_image->setData(data, true)) { + if (!m_image->setData(WTFMove(data), true)) { LOG(IconDatabase, "Manual image data for iconURL '%s' FAILED - it was probably invalid image data", m_iconURL.ascii().data()); - m_image.clear(); + m_image = nullptr; } m_dataSet = true; diff --git a/Source/WebCore/loader/icon/IconRecord.h b/Source/WebCore/loader/icon/IconRecord.h index e5a47f76e..f2105a39a 100644 --- a/Source/WebCore/loader/icon/IconRecord.h +++ b/Source/WebCore/loader/icon/IconRecord.h @@ -10,7 +10,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. * @@ -26,13 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IconRecord_h -#define IconRecord_h +#pragma once #include "PageURLRecord.h" #include "SharedBuffer.h" #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> #include <wtf/RefCounted.h> #include <wtf/text/StringHash.h> #include <wtf/text/WTFString.h> @@ -75,16 +73,16 @@ private: class IconRecord : public RefCounted<IconRecord> { friend class PageURLRecord; public: - static PassRefPtr<IconRecord> create(const String& url) + static Ref<IconRecord> create(const String& url) { - return adoptRef(new IconRecord(url)); + return adoptRef(*new IconRecord(url)); } ~IconRecord(); time_t getTimestamp() { return m_stamp; } void setTimestamp(time_t stamp) { m_stamp = stamp; } - void setImageData(PassRefPtr<SharedBuffer> data); + void setImageData(RefPtr<SharedBuffer>&&); Image* image(const IntSize&); String iconURL() { return m_iconURL; } @@ -119,7 +117,4 @@ private: // SizeImageMap m_images; }; - } //namespace WebCore - -#endif diff --git a/Source/WebCore/loader/icon/PageURLRecord.cpp b/Source/WebCore/loader/icon/PageURLRecord.cpp index 09d649f83..99dded8f2 100644 --- a/Source/WebCore/loader/icon/PageURLRecord.cpp +++ b/Source/WebCore/loader/icon/PageURLRecord.cpp @@ -10,7 +10,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. * @@ -35,29 +35,29 @@ namespace WebCore { PageURLRecord::PageURLRecord(const String& pageURL) : m_pageURL(pageURL) - , m_retainCount(0) { } PageURLRecord::~PageURLRecord() { - setIconRecord(0); + if (m_iconRecord) + m_iconRecord->m_retainingPageURLs.remove(m_pageURL); } -void PageURLRecord::setIconRecord(PassRefPtr<IconRecord> icon) +void PageURLRecord::setIconRecord(RefPtr<IconRecord>&& icon) { if (m_iconRecord) m_iconRecord->m_retainingPageURLs.remove(m_pageURL); - - m_iconRecord = icon; - + + m_iconRecord = WTFMove(icon); + if (m_iconRecord) m_iconRecord->m_retainingPageURLs.add(m_pageURL); } PageURLSnapshot PageURLRecord::snapshot(bool forDeletion) const { - return PageURLSnapshot(m_pageURL, (m_iconRecord && !forDeletion) ? m_iconRecord->iconURL() : String()); + return { m_pageURL, (m_iconRecord && !forDeletion) ? m_iconRecord->iconURL() : String() }; } } // namespace WebCore diff --git a/Source/WebCore/loader/icon/PageURLRecord.h b/Source/WebCore/loader/icon/PageURLRecord.h index 2b33808d3..189875e32 100644 --- a/Source/WebCore/loader/icon/PageURLRecord.h +++ b/Source/WebCore/loader/icon/PageURLRecord.h @@ -10,7 +10,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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageURLRecord_h -#define PageURLRecord_h +#pragma once #include <wtf/Noncopyable.h> #include <wtf/RefPtr.h> @@ -62,7 +61,7 @@ public: inline String url() const { return m_pageURL; } - void setIconRecord(PassRefPtr<IconRecord>); + void setIconRecord(RefPtr<IconRecord>&&); IconRecord* iconRecord() { return m_iconRecord.get(); } PageURLSnapshot snapshot(bool forDeletion = false) const; @@ -83,13 +82,12 @@ public: return m_retainCount > 0; } - inline int retainCount() const { return m_retainCount; } + int retainCount() const { return m_retainCount; } + private: String m_pageURL; RefPtr<IconRecord> m_iconRecord; - int m_retainCount; + int m_retainCount { 0 }; }; -} - -#endif // PageURLRecord_h +} // namespace WebCore diff --git a/Source/WebCore/loader/soup/CachedRawResourceSoup.cpp b/Source/WebCore/loader/soup/CachedRawResourceSoup.cpp index 4934e9df5..5492d2147 100644 --- a/Source/WebCore/loader/soup/CachedRawResourceSoup.cpp +++ b/Source/WebCore/loader/soup/CachedRawResourceSoup.cpp @@ -17,6 +17,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "CachedRawResource.h" #include "CachedRawResourceClient.h" @@ -28,11 +31,13 @@ char* CachedRawResource::getOrCreateReadBuffer(size_t requestedSize, size_t& act { CachedResourceClientWalker<CachedRawResourceClient> w(m_clients); while (CachedRawResourceClient* c = w.next()) { - if (char* bufferPtr = c->getOrCreateReadBuffer(this, requestedSize, actualSize)) + if (char* bufferPtr = c->getOrCreateReadBuffer(*this, requestedSize, actualSize)) return bufferPtr; } - return 0; + return nullptr; } } // namespace WebCore + +#endif diff --git a/Source/WebCore/loader/soup/SubresourceLoaderSoup.cpp b/Source/WebCore/loader/soup/SubresourceLoaderSoup.cpp index b7670a06d..f86bd55ba 100644 --- a/Source/WebCore/loader/soup/SubresourceLoaderSoup.cpp +++ b/Source/WebCore/loader/soup/SubresourceLoaderSoup.cpp @@ -17,6 +17,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "SubresourceLoader.h" #include "CachedResource.h" @@ -29,3 +32,5 @@ char* SubresourceLoader::getOrCreateReadBuffer(size_t requestedLength, size_t& a } } // namespace WebCore + +#endif |