diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/loader/cache')
40 files changed, 6726 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/CSSStyleSheetResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/CSSStyleSheetResource.cpp new file mode 100644 index 00000000000..53398a20d92 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/CSSStyleSheetResource.cpp @@ -0,0 +1,183 @@ +/* + 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 Apple Computer, Inc. + + 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. + + 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" +#include "core/loader/cache/CSSStyleSheetResource.h" + +#include "core/css/StyleSheetContents.h" +#include "core/loader/TextResourceDecoder.h" +#include "core/loader/cache/ResourceClientWalker.h" +#include "core/loader/cache/StyleSheetResourceClient.h" +#include "core/platform/SharedBuffer.h" +#include "core/platform/network/HTTPParsers.h" +#include "wtf/CurrentTime.h" +#include "wtf/Vector.h" + +namespace WebCore { + +CSSStyleSheetResource::CSSStyleSheetResource(const ResourceRequest& resourceRequest, const String& charset) + : Resource(resourceRequest, CSSStyleSheet) + , m_decoder(TextResourceDecoder::create("text/css", charset)) +{ + DEFINE_STATIC_LOCAL(const AtomicString, acceptCSS, ("text/css,*/*;q=0.1", AtomicString::ConstructFromLiteral)); + + // 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(acceptCSS); +} + +CSSStyleSheetResource::~CSSStyleSheetResource() +{ + if (m_parsedStyleSheetCache) + m_parsedStyleSheetCache->removedFromMemoryCache(); +} + +void CSSStyleSheetResource::didAddClient(ResourceClient* c) +{ + ASSERT(c->resourceClientType() == StyleSheetResourceClient::expectedType()); + // Resource::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. + Resource::didAddClient(c); + + if (!isLoading()) + static_cast<StyleSheetResourceClient*>(c)->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this); +} + +void CSSStyleSheetResource::setEncoding(const String& chs) +{ + m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader); +} + +String CSSStyleSheetResource::encoding() const +{ + return m_decoder->encoding().name(); +} + +const String CSSStyleSheetResource::sheetText(bool enforceMIMEType, bool* hasValidMIMEType) const +{ + ASSERT(!isPurgeable()); + + if (!m_data || m_data->isEmpty() || !canUseSheet(enforceMIMEType, 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; +} + +void CSSStyleSheetResource::checkNotify() +{ + // 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()); + } + + ResourceClientWalker<StyleSheetResourceClient> w(m_clients); + while (StyleSheetResourceClient* c = w.next()) + c->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this); + // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate. + m_decodedSheetText = String(); +} + +bool CSSStyleSheetResource::canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const +{ + if (errorOccurred()) + return false; + + if (!enforceMIMEType && !hasValidMIMEType) + return true; + + // This check exactly matches Firefox. Note that we grab the Content-Type + // header directly because we want to see what the value is BEFORE content + // sniffing. Firefox does this by setting a "type hint" on the channel. + // This implementation should be observationally equivalent. + // + // 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"); + if (hasValidMIMEType) + *hasValidMIMEType = typeOK; + if (!enforceMIMEType) + return true; + return typeOK; +} + +void CSSStyleSheetResource::destroyDecodedData() +{ + if (!m_parsedStyleSheetCache) + return; + + m_parsedStyleSheetCache->removedFromMemoryCache(); + m_parsedStyleSheetCache.clear(); + + setDecodedSize(0); + + if (isSafeToMakePurgeable()) + makePurgeable(true); +} + +PassRefPtr<StyleSheetContents> CSSStyleSheetResource::restoreParsedStyleSheet(const CSSParserContext& context) +{ + if (!m_parsedStyleSheetCache) + return 0; + if (m_parsedStyleSheetCache->hasFailedOrCanceledSubresources()) { + m_parsedStyleSheetCache->removedFromMemoryCache(); + m_parsedStyleSheetCache.clear(); + return 0; + } + + ASSERT(m_parsedStyleSheetCache->isCacheable()); + ASSERT(m_parsedStyleSheetCache->isInMemoryCache()); + + // 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; + + didAccessDecodedData(currentTime()); + + return m_parsedStyleSheetCache; +} + +void CSSStyleSheetResource::saveParsedStyleSheet(PassRefPtr<StyleSheetContents> sheet) +{ + ASSERT(sheet && sheet->isCacheable()); + + if (m_parsedStyleSheetCache) + m_parsedStyleSheetCache->removedFromMemoryCache(); + m_parsedStyleSheetCache = sheet; + m_parsedStyleSheetCache->addedToMemoryCache(); + + setDecodedSize(m_parsedStyleSheetCache->estimatedSizeInBytes()); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/CSSStyleSheetResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/CSSStyleSheetResource.h new file mode 100644 index 00000000000..b01fa8667f3 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/CSSStyleSheetResource.h @@ -0,0 +1,67 @@ +/* + 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. + + 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. + + 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 CSSStyleSheetResource_h +#define CSSStyleSheetResource_h + +#include "core/loader/cache/Resource.h" + +namespace WebCore { + +class ResourceClient; +class StyleSheetContents; +class TextResourceDecoder; +struct CSSParserContext; + +class CSSStyleSheetResource : public Resource { +public: + CSSStyleSheetResource(const ResourceRequest&, const String& charset); + virtual ~CSSStyleSheetResource(); + + const String sheetText(bool enforceMIMEType = true, bool* hasValidMIMEType = 0) const; + + virtual void didAddClient(ResourceClient*); + virtual void setEncoding(const String&); + virtual String encoding() const; + virtual void destroyDecodedData() OVERRIDE; + + PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&); + void saveParsedStyleSheet(PassRefPtr<StyleSheetContents>); + +private: + bool canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const; + +protected: + virtual void checkNotify(); + + RefPtr<TextResourceDecoder> m_decoder; + String m_decodedSheetText; + + RefPtr<StyleSheetContents> m_parsedStyleSheetCache; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/CachePolicy.h b/chromium/third_party/WebKit/Source/core/loader/cache/CachePolicy.h new file mode 100644 index 00000000000..a44c32d26cc --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/CachePolicy.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, 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 CachePolicy_h +#define CachePolicy_h + +namespace WebCore { + + enum CachePolicy { + CachePolicyCache, + CachePolicyVerify, + CachePolicyRevalidate, + CachePolicyReload, + CachePolicyHistoryBuffer + }; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResource.cpp new file mode 100644 index 00000000000..27503416f14 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResource.cpp @@ -0,0 +1,82 @@ +/* + Copyright (C) 2010 Rob Buis <rwlbuis@gmail.com> + Copyright (C) 2011 Cosmin Truta <ctruta@gmail.com> + Copyright (C) 2012 University of Szeged + Copyright (C) 2012 Renata Hodovan <reni@webkit.org> + + 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 "core/loader/cache/DocumentResource.h" + +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/platform/SharedBuffer.h" +#include "core/svg/SVGDocument.h" +#include "wtf/text/StringBuilder.h" + +namespace WebCore { + +DocumentResource::DocumentResource(const ResourceRequest& request, Type type) + : Resource(request, type) + , m_decoder(TextResourceDecoder::create("application/xml")) +{ + // FIXME: We'll support more types to support HTMLImports. + ASSERT(type == SVGDocument); +} + +DocumentResource::~DocumentResource() +{ +} + +void DocumentResource::setEncoding(const String& chs) +{ + m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader); +} + +String DocumentResource::encoding() const +{ + return m_decoder->encoding().name(); +} + +void DocumentResource::checkNotify() +{ + if (m_data) { + StringBuilder decodedText; + decodedText.append(m_decoder->decode(m_data->data(), m_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 = createDocument(response().url()); + m_document->setContent(decodedText.toString()); + } + Resource::checkNotify(); +} + +PassRefPtr<Document> DocumentResource::createDocument(const KURL& url) +{ + switch (type()) { + case SVGDocument: + return SVGDocument::create(DocumentInit(url)); + default: + // FIXME: We'll add more types to support HTMLImports. + ASSERT_NOT_REACHED(); + return 0; + } +} + +} diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResource.h new file mode 100644 index 00000000000..62379d70925 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResource.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2010 Rob Buis <rwlbuis@gmail.com> + Copyright (C) 2011 Cosmin Truta <ctruta@gmail.com> + Copyright (C) 2012 University of Szeged + Copyright (C) 2012 Renata Hodovan <reni@webkit.org> + + 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 DocumentResource_h +#define DocumentResource_h + +#include "core/loader/TextResourceDecoder.h" +#include "core/loader/cache/Resource.h" +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourcePtr.h" + +namespace WebCore { + +class Document; + +class DocumentResource : public Resource { +public: + DocumentResource(const ResourceRequest&, Type); + virtual ~DocumentResource(); + + Document* document() const { return m_document.get(); } + + virtual void setEncoding(const String&); + virtual String encoding() const; + virtual void checkNotify() OVERRIDE; + +private: + PassRefPtr<Document> createDocument(const KURL&); + + RefPtr<Document> m_document; + RefPtr<TextResourceDecoder> m_decoder; +}; + +class DocumentResourceClient : public ResourceClient { +public: + virtual ~DocumentResourceClient() { } + static ResourceClientType expectedType() { return DocumentType; } + virtual ResourceClientType resourceClientType() const { return expectedType(); } +}; + +} + +#endif // DocumentResource_h diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResourceReference.h b/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResourceReference.h new file mode 100644 index 00000000000..4d7009cb434 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/DocumentResourceReference.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. + */ + +#ifndef DocumentResourceReference_h +#define DocumentResourceReference_h + +#include "core/loader/cache/DocumentResource.h" +#include "core/loader/cache/ResourcePtr.h" + +namespace WebCore { + +class DocumentResourceReference : public DocumentResourceClient { +public: + DocumentResourceReference(DocumentResource* document) : m_document(document) { m_document->addClient(this); } + virtual ~DocumentResourceReference() { m_document->removeClient(this); } + DocumentResource* document() { return m_document.get(); } +private: + ResourcePtr<DocumentResource> m_document; +}; + +}; + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/FetchInitiatorInfo.h b/chromium/third_party/WebKit/Source/core/loader/cache/FetchInitiatorInfo.h new file mode 100644 index 00000000000..c5e9b3a67b8 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/FetchInitiatorInfo.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE 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 FetchInitiatorInfo_h +#define FetchInitiatorInfo_h + +#include "wtf/text/AtomicString.h" +#include "wtf/text/TextPosition.h" + +namespace WebCore { + +struct FetchInitiatorInfo { + FetchInitiatorInfo() + : name() + , position(TextPosition::belowRangePosition()) + , startTime(0.0) + { + } + + AtomicString name; + TextPosition position; + double startTime; +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/FetchInitiatorTypeNames.in b/chromium/third_party/WebKit/Source/core/loader/cache/FetchInitiatorTypeNames.in new file mode 100644 index 00000000000..7356271ceb4 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/FetchInitiatorTypeNames.in @@ -0,0 +1,8 @@ +css +document +icon +link +processinginstruction +texttrack +xml +xmlhttprequest diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/FetchRequest.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/FetchRequest.cpp new file mode 100644 index 00000000000..57268b2979e --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/FetchRequest.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE 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 "core/loader/cache/FetchRequest.h" + +#include "core/dom/Element.h" +#include "core/loader/CrossOriginAccessControl.h" +#include "core/loader/cache/FetchInitiatorInfo.h" +#include "core/loader/cache/ResourceFetcher.h" + +namespace WebCore { + +FetchRequest::FetchRequest(const ResourceRequest& resourceRequest, const AtomicString& initiator, const String& charset, ResourceLoadPriority priority) + : m_resourceRequest(resourceRequest) + , m_charset(charset) + , m_options(ResourceFetcher::defaultResourceOptions()) + , m_priority(priority) + , m_forPreload(false) + , m_defer(NoDefer) +{ + m_options.initiatorInfo.name = initiator; +} + +FetchRequest::FetchRequest(const ResourceRequest& resourceRequest, const AtomicString& initiator, const ResourceLoaderOptions& options) + : m_resourceRequest(resourceRequest) + , m_options(options) + , m_priority(ResourceLoadPriorityUnresolved) + , m_forPreload(false) + , m_defer(NoDefer) +{ + m_options.initiatorInfo.name = initiator; +} + +FetchRequest::FetchRequest(const ResourceRequest& resourceRequest, const FetchInitiatorInfo& initiator) + : m_resourceRequest(resourceRequest) + , m_options(ResourceFetcher::defaultResourceOptions()) + , m_priority(ResourceLoadPriorityUnresolved) + , m_forPreload(false) + , m_defer(NoDefer) +{ + m_options.initiatorInfo = initiator; +} + +FetchRequest::~FetchRequest() +{ +} + +void FetchRequest::setPotentiallyCrossOriginEnabled(SecurityOrigin* origin, StoredCredentials allowCredentials) +{ + updateRequestForAccessControl(m_resourceRequest, origin, allowCredentials); + ASSERT(m_options.requestOriginPolicy == UseDefaultOriginRestrictionsForType); // Allows only tightening from the default value. + m_options.requestOriginPolicy = PotentiallyCrossOriginEnabled; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/FetchRequest.h b/chromium/third_party/WebKit/Source/core/loader/cache/FetchRequest.h new file mode 100644 index 00000000000..974fff2b36b --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/FetchRequest.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE 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 FetchRequest_h +#define FetchRequest_h + +#include "core/dom/Element.h" +#include "core/loader/ResourceLoaderOptions.h" +#include "core/loader/cache/FetchInitiatorInfo.h" +#include "core/platform/network/ResourceLoadPriority.h" +#include "core/platform/network/ResourceRequest.h" +#include "wtf/text/AtomicString.h" + +namespace WebCore { +class SecurityOrigin; + +class FetchRequest { +public: + enum DeferOption { NoDefer, DeferredByClient }; + + explicit FetchRequest(const ResourceRequest&, const AtomicString& initiator, const String& charset = String(), ResourceLoadPriority = ResourceLoadPriorityUnresolved); + FetchRequest(const ResourceRequest&, const AtomicString& initiator, const ResourceLoaderOptions&); + FetchRequest(const ResourceRequest&, const FetchInitiatorInfo&); + ~FetchRequest(); + + ResourceRequest& mutableResourceRequest() { return m_resourceRequest; } + const ResourceRequest& resourceRequest() const { return m_resourceRequest; } + const KURL& url() const { return m_resourceRequest.url(); } + 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; } + 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 setContentSecurityCheck(ContentSecurityPolicyCheck contentSecurityPolicyOption) { m_options.contentSecurityPolicyOption = contentSecurityPolicyOption; } + void setPotentiallyCrossOriginEnabled(SecurityOrigin*, StoredCredentials); + +private: + ResourceRequest m_resourceRequest; + String m_charset; + ResourceLoaderOptions m_options; + ResourceLoadPriority m_priority; + bool m_forPreload; + DeferOption m_defer; +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/FontResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/FontResource.cpp new file mode 100644 index 00000000000..90b527783bf --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/FontResource.cpp @@ -0,0 +1,164 @@ +/* + * 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 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 "core/loader/cache/FontResource.h" + +#include "core/loader/TextResourceDecoder.h" +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourceClientWalker.h" +#include "core/platform/SharedBuffer.h" +#include "core/platform/graphics/FontCustomPlatformData.h" +#include "core/platform/graphics/FontPlatformData.h" + +#if ENABLE(SVG_FONTS) +#include "SVGNames.h" +#include "core/dom/NodeList.h" +#include "core/svg/SVGDocument.h" +#include "core/svg/SVGFontElement.h" +#endif + +namespace WebCore { + +FontResource::FontResource(const ResourceRequest& resourceRequest) + : Resource(resourceRequest, Font) + , m_loadInitiated(false) +{ +} + +FontResource::~FontResource() +{ +} + +void FontResource::load(ResourceFetcher*, const ResourceLoaderOptions& options) +{ + // Don't load the file yet. Wait for an access before triggering the load. + setLoading(true); + m_options = options; +} + +void FontResource::didAddClient(ResourceClient* c) +{ + ASSERT(c->resourceClientType() == FontResourceClient::expectedType()); + if (!isLoading()) + static_cast<FontResourceClient*>(c)->fontLoaded(this); +} + +void FontResource::beginLoadIfNeeded(ResourceFetcher* dl) +{ + if (!m_loadInitiated) { + m_loadInitiated = true; + Resource::load(dl, m_options); + + ResourceClientWalker<FontResourceClient> walker(m_clients); + while (FontResourceClient* client = walker.next()) + client->didStartFontLoad(this); + } +} + +bool FontResource::ensureCustomFontData() +{ + if (!m_fontData && !errorOccurred() && !isLoading() && m_data) { + m_fontData = FontCustomPlatformData::create(m_data.get()); + if (!m_fontData) + setStatus(DecodeError); + } + return m_fontData; +} + +FontPlatformData FontResource::platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant widthVariant) +{ +#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); +} + +#if ENABLE(SVG_FONTS) +bool FontResource::ensureSVGFontData() +{ + if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) { + m_externalSVGDocument = SVGDocument::create(); + + 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; + } + + return m_externalSVGDocument; +} + +SVGFontElement* FontResource::getSVGFontById(const String& fontName) const +{ + 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(list->item(i)->hasTagName(SVGNames::fontTag)); + } +#endif + + if (fontName.isEmpty()) + return toSVGFontElement(list->item(0)); + + for (unsigned i = 0; i < listLength; ++i) { + SVGFontElement* element = toSVGFontElement(list->item(i)); + if (element->getIdAttribute() == fontName) + return element; + } + + return 0; +} +#endif + +void FontResource::allClientsRemoved() +{ + m_fontData.clear(); + Resource::allClientsRemoved(); +} + +void FontResource::checkNotify() +{ + ResourceClientWalker<FontResourceClient> w(m_clients); + while (FontResourceClient* c = w.next()) + c->fontLoaded(this); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/FontResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/FontResource.h new file mode 100644 index 00000000000..5997f1e9b98 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/FontResource.h @@ -0,0 +1,87 @@ +/* + * 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 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 FontResource_h +#define FontResource_h + +#include "core/loader/cache/Resource.h" +#include "core/loader/cache/ResourceClient.h" +#include "core/platform/graphics/FontOrientation.h" +#include "core/platform/graphics/FontWidthVariant.h" +#include "wtf/OwnPtr.h" + +namespace WebCore { + +class ResourceFetcher; +class FontPlatformData; +class SVGDocument; +class SVGFontElement; +class FontCustomPlatformData; + +class FontResource : public Resource { +public: + FontResource(const ResourceRequest&); + virtual ~FontResource(); + + virtual void load(ResourceFetcher*, const ResourceLoaderOptions&); + + virtual void didAddClient(ResourceClient*); + + virtual void allClientsRemoved(); + void beginLoadIfNeeded(ResourceFetcher* dl); + bool stillNeedsLoad() const { return !m_loadInitiated; } + + bool ensureCustomFontData(); + FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth); + +#if ENABLE(SVG_FONTS) + bool ensureSVGFontData(); + SVGFontElement* getSVGFontById(const String&) const; +#endif + +private: + virtual void checkNotify(); + OwnPtr<FontCustomPlatformData> m_fontData; + bool m_loadInitiated; + +#if ENABLE(SVG_FONTS) + RefPtr<WebCore::SVGDocument> m_externalSVGDocument; +#endif + + friend class MemoryCache; +}; + +class FontResourceClient : public ResourceClient { +public: + virtual ~FontResourceClient() { } + static ResourceClientType expectedType() { return FontType; } + virtual ResourceClientType resourceClientType() const { return expectedType(); } + virtual void fontLoaded(FontResource*) { } + virtual void didStartFontLoad(FontResource*) { } +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ImageResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResource.cpp new file mode 100644 index 00000000000..c07d864d850 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResource.cpp @@ -0,0 +1,451 @@ +/* + 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 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 + 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 "core/loader/cache/ImageResource.h" + +#include "core/loader/cache/ImageResourceClient.h" +#include "core/loader/cache/MemoryCache.h" +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourceClientWalker.h" +#include "core/loader/cache/ResourceFetcher.h" +#include "core/page/FrameView.h" +#include "core/platform/SharedBuffer.h" +#include "core/platform/graphics/BitmapImage.h" +#include "core/rendering/RenderObject.h" +#include "core/svg/graphics/SVGImage.h" +#include "wtf/CurrentTime.h" +#include "wtf/StdLibExtras.h" +#include "wtf/Vector.h" + +using namespace std; + +namespace WebCore { + +ImageResource::ImageResource(const ResourceRequest& resourceRequest) + : Resource(resourceRequest, Image) + , m_image(0) + , m_loadingMultipartContent(false) +{ + setStatus(Unknown); + setCustomAcceptHeader(); +} + +ImageResource::ImageResource(WebCore::Image* image) + : Resource(ResourceRequest(), Image) + , m_image(image) +{ + setStatus(Cached); + setLoading(false); + setCustomAcceptHeader(); +} + +ImageResource::~ImageResource() +{ + clearImage(); +} + +void ImageResource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options) +{ + if (!fetcher || fetcher->autoLoadImages()) + Resource::load(fetcher, options); + else + setLoading(false); +} + +void ImageResource::didAddClient(ResourceClient* c) +{ + if (m_data && !m_image && !errorOccurred()) { + createImage(); + m_image->setData(m_data, true); + } + + ASSERT(c->resourceClientType() == ImageResourceClient::expectedType()); + if (m_image && !m_image->isNull()) + static_cast<ImageResourceClient*>(c)->imageChanged(this); + + Resource::didAddClient(c); +} + +void ImageResource::didRemoveClient(ResourceClient* c) +{ + ASSERT(c); + ASSERT(c->resourceClientType() == ImageResourceClient::expectedType()); + + m_pendingContainerSizeRequests.remove(static_cast<ImageResourceClient*>(c)); + if (m_svgImageCache) + m_svgImageCache->removeClientFromCache(static_cast<ImageResourceClient*>(c)); + + Resource::didRemoveClient(c); +} + +void ImageResource::switchClientsToRevalidatedResource() +{ + ASSERT(resourceToRevalidate()); + ASSERT(resourceToRevalidate()->isImage()); + // 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 Resource::switchClientsToRevalidateResouce(). + ContainerSizeRequests switchContainerSizeRequests; + for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) + switchContainerSizeRequests.set(it->key, it->value); + Resource::switchClientsToRevalidatedResource(); + ImageResource* revalidatedImageResource = static_cast<ImageResource*>(resourceToRevalidate()); + for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it) + revalidatedImageResource->setContainerSizeForRenderer(it->key, it->value.first, it->value.second); + return; + } + + Resource::switchClientsToRevalidatedResource(); +} + +void ImageResource::allClientsRemoved() +{ + m_pendingContainerSizeRequests.clear(); + if (m_image && !errorOccurred()) + m_image->resetAnimation(); + Resource::allClientsRemoved(); +} + +pair<WebCore::Image*, float> ImageResource::brokenImage(float deviceScaleFactor) const +{ + if (deviceScaleFactor >= 2) { + DEFINE_STATIC_LOCAL(WebCore::Image*, brokenImageHiRes, (WebCore::Image::loadPlatformResource("missingImage@2x").leakRef())); + return std::make_pair(brokenImageHiRes, 2); + } + + DEFINE_STATIC_LOCAL(WebCore::Image*, brokenImageLoRes, (WebCore::Image::loadPlatformResource("missingImage").leakRef())); + return std::make_pair(brokenImageLoRes, 1); +} + +bool ImageResource::willPaintBrokenImage() const +{ + return errorOccurred(); +} + +WebCore::Image* ImageResource::image() +{ + ASSERT(!isPurgeable()); + + if (errorOccurred()) { + // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate + // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage() + // when they need the real, deviceScaleFactor-appropriate broken image icon. + return brokenImage(1).first; + } + + if (m_image) + return m_image.get(); + + return WebCore::Image::nullImage(); +} + +WebCore::Image* ImageResource::imageForRenderer(const RenderObject* renderer) +{ + ASSERT(!isPurgeable()); + + if (errorOccurred()) { + // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate + // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage() + // when they need the real, deviceScaleFactor-appropriate broken image icon. + return brokenImage(1).first; + } + + if (!m_image) + return WebCore::Image::nullImage(); + + if (m_image->isSVGImage()) { + WebCore::Image* image = m_svgImageCache->imageForRenderer(renderer); + if (image != WebCore::Image::nullImage()) + return image; + } + + return m_image.get(); +} + +void ImageResource::setContainerSizeForRenderer(const ImageResourceClient* renderer, const IntSize& containerSize, float containerZoom) +{ + if (containerSize.isEmpty()) + return; + ASSERT(renderer); + ASSERT(containerZoom); + if (!m_image) { + m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom)); + return; + } + if (!m_image->isSVGImage()) { + m_image->setContainerSize(containerSize); + return; + } + + m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom); +} + +bool ImageResource::usesImageContainerSize() const +{ + if (m_image) + return m_image->usesContainerSize(); + + return false; +} + +bool ImageResource::imageHasRelativeWidth() const +{ + if (m_image) + return m_image->hasRelativeWidth(); + + return false; +} + +bool ImageResource::imageHasRelativeHeight() const +{ + if (m_image) + return m_image->hasRelativeHeight(); + + return false; +} + +LayoutSize ImageResource::imageSizeForRenderer(const RenderObject* renderer, float multiplier) +{ + ASSERT(!isPurgeable()); + + if (!m_image) + return IntSize(); + + LayoutSize imageSize; + + if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)) + imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation(); + else if (m_image->isSVGImage()) + imageSize = m_svgImageCache->imageSizeForRenderer(renderer); + else + imageSize = m_image->size(); + + if (multiplier == 1.0f) + return imageSize; + + // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. + float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; + float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; + LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); + imageSize.scale(widthScale, heightScale); + imageSize.clampToMinimumSize(minimumSize); + ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); + return imageSize; +} + +void ImageResource::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) +{ + if (m_image) + m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); +} + +void ImageResource::notifyObservers(const IntRect* changeRect) +{ + ResourceClientWalker<ImageResourceClient> w(m_clients); + while (ImageResourceClient* c = w.next()) + c->imageChanged(this, changeRect); +} + +void ImageResource::clear() +{ + destroyDecodedData(); + clearImage(); + m_pendingContainerSizeRequests.clear(); + setEncodedSize(0); +} + +void ImageResource::setCustomAcceptHeader() +{ + DEFINE_STATIC_LOCAL(const AtomicString, acceptWebP, ("image/webp,*/*;q=0.8", AtomicString::ConstructFromLiteral)); + setAccept(acceptWebP); +} + +inline void ImageResource::createImage() +{ + // Create the image if it doesn't yet exist. + if (m_image) + return; + + if (m_response.mimeType() == "image/svg+xml") { + RefPtr<SVGImage> svgImage = SVGImage::create(this); + m_svgImageCache = SVGImageCache::create(svgImage.get()); + m_image = svgImage.release(); + } else { + m_image = BitmapImage::create(this); + } + + 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); + } + m_pendingContainerSizeRequests.clear(); + } +} + +inline void ImageResource::clearImage() +{ + // 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(); +} + +void ImageResource::appendData(const char* data, int length) +{ + Resource::appendData(data, length); + if (!m_loadingMultipartContent) + updateImage(false); +} + +void ImageResource::updateImage(bool allDataReceived) +{ + if (m_data) + createImage(); + + bool sizeAvailable = false; + + // 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). + if (m_image) + sizeAvailable = m_image->setData(m_data, allDataReceived); + + // Go ahead and tell our observers to try to draw if we have either + // received all the data or the size is known. Each chunk from the + // network causes observers to repaint, which will force that chunk + // to decode. + if (sizeAvailable || allDataReceived) { + if (!m_image || m_image->isNull()) { + error(errorOccurred() ? status() : DecodeError); + if (inCache()) + memoryCache()->remove(this); + return; + } + + // 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(); + } +} + +void ImageResource::finishOnePart() +{ + if (m_loadingMultipartContent) + clear(); + updateImage(true); + if (m_loadingMultipartContent) + m_data.clear(); + Resource::finishOnePart(); +} + +void ImageResource::error(Resource::Status status) +{ + clear(); + Resource::error(status); + notifyObservers(); +} + +void ImageResource::responseReceived(const ResourceResponse& response) +{ + if (m_loadingMultipartContent && m_data) + finishOnePart(); + else if (response.isMultipart()) + m_loadingMultipartContent = true; + Resource::responseReceived(response); +} + +void ImageResource::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; + setDecodedSize(0); + makePurgeable(true); + } else if (m_image && !errorOccurred()) { + m_image->destroyDecodedData(); + } +} + +void ImageResource::decodedSizeChanged(const WebCore::Image* image, int delta) +{ + if (!image || image != m_image) + return; + + setDecodedSize(decodedSize() + delta); +} + +void ImageResource::didDraw(const WebCore::Image* image) +{ + if (!image || image != m_image) + return; + + double timeStamp = FrameView::currentPaintTimeStamp(); + if (!timeStamp) // If didDraw is called outside of a Frame paint. + timeStamp = currentTime(); + + Resource::didAccessDecodedData(timeStamp); +} + +bool ImageResource::shouldPauseAnimation(const WebCore::Image* image) +{ + if (!image || image != m_image) + return false; + + ResourceClientWalker<ImageResourceClient> w(m_clients); + while (ImageResourceClient* c = w.next()) { + if (c->willRenderImage(this)) + return false; + } + + return true; +} + +void ImageResource::animationAdvanced(const WebCore::Image* image) +{ + if (!image || image != m_image) + return; + notifyObservers(); +} + +void ImageResource::changedInRect(const WebCore::Image* image, const IntRect& rect) +{ + if (!image || image != m_image) + return; + notifyObservers(&rect); +} + +bool ImageResource::currentFrameKnownToBeOpaque(const RenderObject* renderer) +{ + WebCore::Image* image = imageForRenderer(renderer); + if (image->isBitmapImage()) + image->nativeImageForCurrentFrame(); // force decode + return image->currentFrameKnownToBeOpaque(); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ImageResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResource.h new file mode 100644 index 00000000000..5bee694c78c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResource.h @@ -0,0 +1,120 @@ +/* + 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 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 + 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 ImageResource_h +#define ImageResource_h + +#include "core/loader/cache/Resource.h" +#include "core/platform/graphics/ImageObserver.h" +#include "core/platform/graphics/IntRect.h" +#include "core/platform/graphics/IntSizeHash.h" +#include "core/platform/graphics/LayoutSize.h" +#include "core/svg/graphics/SVGImageCache.h" +#include "wtf/HashMap.h" + +namespace WebCore { + +class ImageResourceClient; +class ResourceFetcher; +class FloatSize; +class MemoryCache; +class RenderObject; +struct Length; + +class ImageResource : public Resource, public ImageObserver { + friend class MemoryCache; + +public: + ImageResource(const ResourceRequest&); + ImageResource(WebCore::Image*); + virtual ~ImageResource(); + + virtual void load(ResourceFetcher*, const ResourceLoaderOptions&); + + WebCore::Image* image(); // Returns the nullImage() if the image is not available yet. + WebCore::Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet. + bool hasImage() const { return m_image.get(); } + bool currentFrameKnownToBeOpaque(const RenderObject*); // Side effect: ensures decoded image is in cache, therefore should only be called when about to draw the image. + + std::pair<WebCore::Image*, float> brokenImage(float deviceScaleFactor) const; // Returns an image and the image's resolution scale factor. + bool willPaintBrokenImage() const; + + bool canRender(const RenderObject* renderer, float multiplier) { return !errorOccurred() && !imageSizeForRenderer(renderer, multiplier).isEmpty(); } + + void setContainerSizeForRenderer(const ImageResourceClient*, const IntSize&, float); + bool usesImageContainerSize() const; + bool imageHasRelativeWidth() const; + bool imageHasRelativeHeight() const; + + // 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); // returns the size of the complete image. + void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio); + + virtual void didAddClient(ResourceClient*); + virtual void didRemoveClient(ResourceClient*); + + virtual void allClientsRemoved(); + virtual void destroyDecodedData(); + + virtual void appendData(const char*, int) OVERRIDE; + virtual void error(Resource::Status); + virtual void responseReceived(const ResourceResponse&); + virtual void finishOnePart() OVERRIDE; + + // For compatibility, images keep loading even if there are HTTP errors. + virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return true; } + + virtual bool isImage() const { return true; } + virtual bool stillNeedsLoad() const OVERRIDE { return !errorOccurred() && status() == Unknown && !isLoading(); } + + // ImageObserver + virtual void decodedSizeChanged(const WebCore::Image*, int delta); + virtual void didDraw(const WebCore::Image*); + + virtual bool shouldPauseAnimation(const WebCore::Image*); + virtual void animationAdvanced(const WebCore::Image*); + virtual void changedInRect(const WebCore::Image*, const IntRect&); + +private: + void clear(); + + void setCustomAcceptHeader(); + void createImage(); + void updateImage(bool allDataReceived); + void clearImage(); + // If not null, changeRect is the changed part of the image. + void notifyObservers(const IntRect* changeRect = 0); + + virtual void switchClientsToRevalidatedResource() OVERRIDE; + + typedef pair<IntSize, float> SizeAndZoom; + typedef HashMap<const ImageResourceClient*, SizeAndZoom> ContainerSizeRequests; + ContainerSizeRequests m_pendingContainerSizeRequests; + + RefPtr<WebCore::Image> m_image; + OwnPtr<SVGImageCache> m_svgImageCache; + bool m_loadingMultipartContent; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ImageResourceClient.h b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResourceClient.h new file mode 100644 index 00000000000..4cea375612c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResourceClient.h @@ -0,0 +1,52 @@ +/* + 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 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 + 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 ImageResourceClient_h +#define ImageResourceClient_h + +#include "core/loader/cache/ResourceClient.h" + +namespace WebCore { + +class ImageResource; +class IntRect; + +class ImageResourceClient : public ResourceClient { +public: + virtual ~ImageResourceClient() { } + static ResourceClientType expectedType() { return ImageType; } + virtual ResourceClientType resourceClientType() const { 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(ImageResource*, const IntRect* = 0) { } + + // 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(ImageResource*) { return false; } +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ImageResourceTest.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResourceTest.cpp new file mode 100644 index 00000000000..37d558b8d57 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ImageResourceTest.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013, 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: + * + * * 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 Google 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 "core/loader/cache/ImageResource.h" + +#include "core/loader/DocumentLoader.h" +#include "core/loader/EmptyClients.h" +#include "core/loader/cache/ImageResourceClient.h" +#include "core/loader/cache/MemoryCache.h" +#include "core/loader/cache/MockImageResourceClient.h" +#include "core/loader/cache/ResourceFetcher.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/page/Frame.h" +#include "core/page/FrameView.h" +#include "core/page/Page.h" +#include "core/platform/SharedBuffer.h" +#include "public/platform/Platform.h" +#include "public/platform/WebThread.h" +#include "public/platform/WebURL.h" +#include "public/platform/WebURLResponse.h" +#include "public/platform/WebUnitTestSupport.h" + +using namespace WebCore; + +namespace { + +class QuitTask : public WebKit::WebThread::Task { +public: + virtual void run() + { + WebKit::Platform::current()->currentThread()->exitRunLoop(); + } +}; + +void runPendingTasks() +{ + WebKit::Platform::current()->currentThread()->postTask(new QuitTask); + WebKit::Platform::current()->currentThread()->enterRunLoop(); +} + +TEST(ImageResourceTest, MultipartImage) +{ + ResourcePtr<ImageResource> cachedImage = new ImageResource(ResourceRequest()); + cachedImage->setLoading(true); + + MockImageResourceClient client; + cachedImage->addClient(&client); + + // Send the multipart response. No image or data buffer is created. + cachedImage->responseReceived(ResourceResponse(KURL(), "multipart/x-mixed-replace", 0, String(), String())); + ASSERT_FALSE(cachedImage->resourceBuffer()); + ASSERT_FALSE(cachedImage->hasImage()); + ASSERT_EQ(client.imageChangedCount(), 0); + ASSERT_FALSE(client.notifyFinishedCalled()); + + // Send the response for the first real part. No image or data buffer is created. + const char* svgData = "<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'><rect width='1' height='1' fill='green'/></svg>"; + unsigned svgDataLength = strlen(svgData); + cachedImage->responseReceived(ResourceResponse(KURL(), "image/svg+xml", svgDataLength, String(), String())); + ASSERT_FALSE(cachedImage->resourceBuffer()); + ASSERT_FALSE(cachedImage->hasImage()); + ASSERT_EQ(client.imageChangedCount(), 0); + ASSERT_FALSE(client.notifyFinishedCalled()); + + // The first bytes arrive. The data buffer is created, but no image is created. + cachedImage->appendData(svgData, svgDataLength); + ASSERT_TRUE(cachedImage->resourceBuffer()); + ASSERT_EQ(cachedImage->resourceBuffer()->size(), svgDataLength); + ASSERT_FALSE(cachedImage->hasImage()); + ASSERT_EQ(client.imageChangedCount(), 0); + ASSERT_FALSE(client.notifyFinishedCalled()); + + // This part finishes. The image is created, callbacks are sent, and the data buffer is cleared. + cachedImage->finish(); + ASSERT_FALSE(cachedImage->resourceBuffer()); + ASSERT_FALSE(cachedImage->errorOccurred()); + ASSERT_TRUE(cachedImage->hasImage()); + ASSERT_FALSE(cachedImage->image()->isNull()); + ASSERT_EQ(cachedImage->image()->width(), 1); + ASSERT_EQ(cachedImage->image()->height(), 1); + ASSERT_EQ(client.imageChangedCount(), 2); + ASSERT_TRUE(client.notifyFinishedCalled()); +} + +TEST(ImageResourceTest, CancelOnDetach) +{ + KURL testURL(ParsedURLString, "http://www.test.com/cancelTest.html"); + + WebKit::WebURLResponse response; + response.initialize(); + response.setMIMEType("text/html"); + WTF::String localPath = WebKit::Platform::current()->unitTestSupport()->webKitRootDir(); + localPath.append("/Source/web/tests/data/cancelTest.html"); + WebKit::Platform::current()->unitTestSupport()->registerMockedURL(testURL, response, localPath); + + // Create enough of a mocked world to get a functioning ResourceLoader. + Page::PageClients pageClients; + fillWithEmptyClients(pageClients); + EmptyFrameLoaderClient frameLoaderClient; + Page page(pageClients); + RefPtr<Frame> frame = Frame::create(&page, 0, &frameLoaderClient); + frame->setView(FrameView::create(frame.get())); + frame->init(); + RefPtr<DocumentLoader> documentLoader = DocumentLoader::create(ResourceRequest(testURL), SubstituteData()); + documentLoader->setFrame(frame.get()); + + // Emulate starting a real load. + ResourcePtr<ImageResource> cachedImage = new ImageResource(ResourceRequest(testURL)); + cachedImage->load(documentLoader->fetcher(), ResourceLoaderOptions()); + memoryCache()->add(cachedImage.get()); + + MockImageResourceClient client; + cachedImage->addClient(&client); + EXPECT_EQ(Resource::Pending, cachedImage->status()); + + // The load should still be alive, but a timer should be started to cancel the load inside removeClient(). + cachedImage->removeClient(&client); + EXPECT_EQ(Resource::Pending, cachedImage->status()); + EXPECT_NE(reinterpret_cast<Resource*>(0), memoryCache()->resourceForURL(testURL)); + + // Trigger the cancel timer, ensure the load was cancelled and the resource was evicted from the cache. + runPendingTasks(); + EXPECT_EQ(Resource::LoadError, cachedImage->status()); + EXPECT_EQ(reinterpret_cast<Resource*>(0), memoryCache()->resourceForURL(testURL)); + + WebKit::Platform::current()->unitTestSupport()->unregisterMockedURL(testURL); +} + +} // namespace diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCache.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCache.cpp new file mode 100644 index 00000000000..c51f5446e3c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCache.cpp @@ -0,0 +1,619 @@ +/* + 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 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 + 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 "core/loader/cache/MemoryCache.h" + +#include <stdio.h> +#include "core/dom/CrossThreadTask.h" +#include "core/dom/Document.h" +#include "core/loader/cache/Resource.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/page/FrameView.h" +#include "core/platform/Logging.h" +#include "core/platform/chromium/TraceEvent.h" +#include "core/workers/WorkerGlobalScope.h" +#include "core/workers/WorkerLoaderProxy.h" +#include "core/workers/WorkerThread.h" +#include "weborigin/SecurityOrigin.h" +#include "weborigin/SecurityOriginHash.h" +#include "wtf/Assertions.h" +#include "wtf/CurrentTime.h" +#include "wtf/MathExtras.h" +#include "wtf/TemporaryChange.h" +#include "wtf/text/CString.h" + +using namespace std; + +namespace WebCore { + +static MemoryCache* gMemoryCache; + +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. + +MemoryCache* memoryCache() +{ + ASSERT(WTF::isMainThread()); + if (!gMemoryCache) + gMemoryCache = new MemoryCache(); + return gMemoryCache; +} + +void setMemoryCacheForTesting(MemoryCache* memoryCache) +{ + gMemoryCache = memoryCache; +} + +MemoryCache::MemoryCache() + : m_inPruneResources(false) + , m_capacity(cDefaultCacheCapacity) + , m_minDeadCapacity(0) + , m_maxDeadCapacity(cDefaultCacheCapacity) + , m_liveSize(0) + , m_deadSize(0) + , m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune) +#ifdef MEMORY_CACHE_STATS + , m_statsTimer(this, &MemoryCache::dumpStats) +#endif +{ +#ifdef MEMORY_CACHE_STATS + const double statsIntervalInSeconds = 15; + m_statsTimer.startRepeating(statsIntervalInSeconds); +#endif +} + +KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) +{ + if (!originalURL.hasFragmentIdentifier()) + return originalURL; + // Strip away fragment identifier from HTTP URLs. + // 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; + KURL url = originalURL; + url.removeFragmentIdentifier(); + return url; +} + +void MemoryCache::add(Resource* resource) +{ + ASSERT(WTF::isMainThread()); + m_resources.set(resource->url(), resource); + resource->setInCache(true); + resource->updateForAccess(); + + LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().string().latin1().data(), resource); +} + +void MemoryCache::replace(Resource* newResource, Resource* oldResource) +{ + evict(oldResource); + ASSERT(!m_resources.get(newResource->url())); + m_resources.set(newResource->url(), newResource); + newResource->setInCache(true); + insertInLRUList(newResource); + int delta = newResource->size(); + if (newResource->decodedSize() && newResource->hasClients()) + insertInLiveDecodedResourcesList(newResource); + if (delta) + adjustSize(newResource->hasClients(), delta); +} + +Resource* MemoryCache::resourceForURL(const KURL& resourceURL) +{ + ASSERT(WTF::isMainThread()); + KURL url = removeFragmentIdentifierIfNeeded(resourceURL); + Resource* resource = m_resources.get(url); + if (resource && !resource->makePurgeable(false)) { + ASSERT(!resource->hasClients()); + evict(resource); + return 0; + } + return resource; +} + +unsigned MemoryCache::deadCapacity() const +{ + // Dead resource capacity is whatever space is not occupied by live resources, bounded by an independent minimum and maximum. + unsigned capacity = m_capacity - min(m_liveSize, m_capacity); // Start with available capacity. + capacity = max(capacity, m_minDeadCapacity); // Make sure it's above the minimum. + capacity = min(capacity, m_maxDeadCapacity); // Make sure it's below the maximum. + return capacity; +} + +unsigned MemoryCache::liveCapacity() const +{ + // Live resource capacity is whatever is left over after calculating dead resource capacity. + return m_capacity - deadCapacity(); +} + +void MemoryCache::pruneLiveResources() +{ + unsigned capacity = liveCapacity(); + if (!m_liveSize || (capacity && m_liveSize <= capacity)) + return; + + unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. + + double currentTime = FrameView::currentPaintTimeStamp(); + if (!currentTime) // In case prune is called directly, outside of a Frame paint. + currentTime = WTF::currentTime(); + + // Destroy any decoded data in live objects that we can. + // Start from the tail, since this is the lowest priority + // and 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 + + // Start pruning from the lowest priority list. + for (int priority = Resource::CacheLiveResourcePriorityLow; priority <= Resource::CacheLiveResourcePriorityHigh; ++priority) { + Resource* current = m_liveDecodedResources[priority].m_tail; + while (current) { + Resource* prev = current->m_prevInLiveResourcesList; + ASSERT(current->hasClients()); + if (current->isLoaded() && current->decodedSize()) { + // Check to see if the remaining resources are too new to prune. + double elapsedTime = currentTime - current->m_lastDecodedAccessTime; + if (elapsedTime < m_delayBeforeLiveDecodedPrune) + return; + + // 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() +{ + unsigned capacity = deadCapacity(); + if (!m_deadSize || (capacity && m_deadSize <= capacity)) + return; + + unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. + + int size = m_allResources.size(); + + // See if we have any purged resources we can evict. + for (int i = 0; i < size; i++) { + Resource* current = m_allResources[i].m_tail; + while (current) { + Resource* 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. + Resource* current = m_allResources[i].m_tail; + + // First flush all the decoded data in this queue. + while (current) { + // Protect 'previous' so it can't get deleted during destroyDecodedData(). + ResourcePtr<Resource> previous = current->m_prevInAllResourcesList; + ASSERT(!previous || previous->inCache()); + if (!current->hasClients() && !current->isPreloaded() && current->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(); + + 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) { + ResourcePtr<Resource> previous = current->m_prevInAllResourcesList; + ASSERT(!previous || previous->inCache()); + if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) { + evict(current); + 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) + canShrinkLRULists = false; + else if (canShrinkLRULists) + m_allResources.resize(i); + } +} + +void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) +{ + ASSERT(minDeadBytes <= maxDeadBytes); + ASSERT(maxDeadBytes <= totalBytes); + m_minDeadCapacity = minDeadBytes; + m_maxDeadCapacity = maxDeadBytes; + m_capacity = totalBytes; + prune(); +} + +void MemoryCache::evict(Resource* resource) +{ + ASSERT(WTF::isMainThread()); + 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. + m_resources.remove(resource->url()); + resource->setInCache(false); + + // Remove from the appropriate LRU list. + removeFromLRUList(resource); + removeFromLiveDecodedResourcesList(resource); + adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); + } else + ASSERT(m_resources.get(resource->url()) != resource); + + resource->deleteIfPossible(); +} + +MemoryCache::LRUList* MemoryCache::lruListFor(Resource* resource) +{ + unsigned accessCount = max(resource->accessCount(), 1U); + unsigned queueIndex = WTF::fastLog2(resource->size() / accessCount); +#ifndef NDEBUG + resource->m_lruIndex = queueIndex; +#endif + if (m_allResources.size() <= queueIndex) + m_allResources.grow(queueIndex + 1); + return &m_allResources[queueIndex]; +} + +void MemoryCache::removeFromLRUList(Resource* resource) +{ + // If we've never been accessed, then we're brand new and not in any list. + if (resource->accessCount() == 0) + return; + +#if !ASSERT_DISABLED + unsigned oldListIndex = resource->m_lruIndex; +#endif + + 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 (Resource* current = list->m_head; current; current = current->m_nextInAllResourcesList) { + if (current == resource) { + found = true; + break; + } + } + ASSERT(found); +#endif + + Resource* next = resource->m_nextInAllResourcesList; + Resource* 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; + + if (prev) + prev->m_nextInAllResourcesList = next; + else if (list->m_head == resource) + list->m_head = next; +} + +void MemoryCache::insertInLRUList(Resource* 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; + + 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 (Resource* current = list->m_head; current; current = current->m_nextInAllResourcesList) { + if (current == resource) { + found = true; + break; + } + } + ASSERT(found); +#endif + +} + +void MemoryCache::removeFromLiveDecodedResourcesList(Resource* resource) +{ + // 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; + + LRUList* list = &m_liveDecodedResources[resource->cacheLiveResourcePriority()]; + +#if !ASSERT_DISABLED + // Verify that we are in fact in this list. + bool found = false; + for (Resource* current = list->m_head; current; current = current->m_nextInLiveResourcesList) { + if (current == resource) { + found = true; + break; + } + } + ASSERT(found); +#endif + + Resource* next = resource->m_nextInLiveResourcesList; + Resource* prev = resource->m_prevInLiveResourcesList; + + if (!next && !prev && list->m_head != resource) + return; + + resource->m_nextInLiveResourcesList = 0; + resource->m_prevInLiveResourcesList = 0; + + if (next) + next->m_prevInLiveResourcesList = prev; + else if (list->m_tail == resource) + list->m_tail = prev; + + if (prev) + prev->m_nextInLiveResourcesList = next; + else if (list->m_head == resource) + list->m_head = next; +} + +void MemoryCache::insertInLiveDecodedResourcesList(Resource* resource) +{ + // Make sure we aren't in the list already. + ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList); + resource->m_inLiveDecodedResourcesList = true; + + LRUList* list = &m_liveDecodedResources[resource->cacheLiveResourcePriority()]; + resource->m_nextInLiveResourcesList = list->m_head; + if (list->m_head) + list->m_head->m_prevInLiveResourcesList = resource; + list->m_head = resource; + + if (!resource->m_nextInLiveResourcesList) + list->m_tail = resource; + +#if !ASSERT_DISABLED + // Verify that we are in now in the list like we should be. + bool found = false; + for (Resource* current = list->m_head; current; current = current->m_nextInLiveResourcesList) { + if (current == resource) { + found = true; + break; + } + } + ASSERT(found); +#endif + +} + +void MemoryCache::addToLiveResourcesSize(Resource* resource) +{ + m_liveSize += resource->size(); + m_deadSize -= resource->size(); +} + +void MemoryCache::removeFromLiveResourcesSize(Resource* resource) +{ + m_liveSize -= resource->size(); + m_deadSize += resource->size(); +} + +void MemoryCache::adjustSize(bool live, int delta) +{ + if (live) { + ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0)); + m_liveSize += delta; + } else { + ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0)); + m_deadSize += delta; + } +} + +void MemoryCache::removeURLFromCache(ScriptExecutionContext* context, const KURL& url) +{ + if (context->isWorkerGlobalScope()) { + WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); + workerGlobalScope->thread()->workerLoaderProxy().postTaskToLoader(createCallbackTask(&removeURLFromCacheInternal, url)); + return; + } + removeURLFromCacheInternal(context, url); +} + +void MemoryCache::removeURLFromCacheInternal(ScriptExecutionContext*, const KURL& url) +{ + if (Resource* resource = memoryCache()->resourceForURL(url)) + memoryCache()->remove(resource); +} + +void MemoryCache::TypeStatistic::addResource(Resource* o) +{ + 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(); + encodedSize += o->encodedSize(); + encodedSizeDuplicatedInDataURLs += o->url().protocolIsData() ? o->encodedSize() : 0; + purgeableSize += purgeable ? pageSize : 0; + purgedSize += purged ? pageSize : 0; +} + +MemoryCache::Statistics MemoryCache::getStatistics() +{ + Statistics stats; + ResourceMap::iterator e = m_resources.end(); + for (ResourceMap::iterator i = m_resources.begin(); i != e; ++i) { + Resource* resource = i->value; + switch (resource->type()) { + case Resource::Image: + stats.images.addResource(resource); + break; + case Resource::CSSStyleSheet: + stats.cssStyleSheets.addResource(resource); + break; + case Resource::Script: + stats.scripts.addResource(resource); + break; + case Resource::XSLStyleSheet: + stats.xslStyleSheets.addResource(resource); + break; + case Resource::Font: + stats.fonts.addResource(resource); + break; + default: + stats.other.addResource(resource); + break; + } + } + return stats; +} + +void MemoryCache::evictResources() +{ + for (;;) { + ResourceMap::iterator i = m_resources.begin(); + if (i == m_resources.end()) + break; + evict(i->value); + } +} + +void MemoryCache::prune() +{ + TRACE_EVENT0("renderer", "MemoryCache::prune()"); + if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && m_deadSize <= m_maxDeadCapacity) // Fast path. + return; + if (m_inPruneResources) + return; + TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); + + pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. + pruneLiveResources(); +} + + +#ifdef MEMORY_CACHE_STATS + +void MemoryCache::dumpStats(Timer<MemoryCache>*) +{ + Statistics s = getStatistics(); + 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); + 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 %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 %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 %13d %13d %13d %13d %13d %13d\n", "Other", s.other.count, s.other.size, s.other.liveSize, s.other.decodedSize, s.other.purgeableSize, s.other.purgedSize); + printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); + + printf("Duplication of encoded data from data URLs\n"); + printf("%-13s %13d of %13d\n", "Images", s.images.encodedSizeDuplicatedInDataURLs, s.images.encodedSize); + printf("%-13s %13d of %13d\n", "CSS", s.cssStyleSheets.encodedSizeDuplicatedInDataURLs, s.cssStyleSheets.encodedSize); + printf("%-13s %13d of %13d\n", "XSL", s.xslStyleSheets.encodedSizeDuplicatedInDataURLs, s.xslStyleSheets.encodedSize); + printf("%-13s %13d of %13d\n", "JavaScript", s.scripts.encodedSizeDuplicatedInDataURLs, s.scripts.encodedSize); + printf("%-13s %13d of %13d\n", "Fonts", s.fonts.encodedSizeDuplicatedInDataURLs, s.fonts.encodedSize); + printf("%-13s %13d of %13d\n", "Other", s.other.encodedSizeDuplicatedInDataURLs, s.other.encodedSize); +} + +void MemoryCache::dumpLRULists(bool includeLive) const +{ + printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n"); + + int size = m_allResources.size(); + for (int i = size - 1; i >= 0; i--) { + printf("\n\nList %d: ", i); + Resource* current = m_allResources[i].m_tail; + while (current) { + Resource* prev = current->m_prevInAllResourcesList; + if (includeLive || !current->hasClients()) + 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()); + + current = prev; + } + } +} + +#endif // MEMORY_CACHE_STATS + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCache.h b/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCache.h new file mode 100644 index 00000000000..f174a11b9bf --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCache.h @@ -0,0 +1,210 @@ +/* + Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) + Copyright (C) 2001 Dirk Mueller <mueller@kde.org> + Copyright (C) 2004, 2005, 2006, 2007, 2008 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 + 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. + + 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 Cache_h +#define Cache_h + +#include "core/loader/cache/Resource.h" +#include "wtf/HashMap.h" +#include "wtf/Noncopyable.h" +#include "wtf/Vector.h" +#include "wtf/text/StringHash.h" +#include "wtf/text/WTFString.h" + +namespace WebCore { + +class CSSStyleSheetResource; +class Resource; +class ResourceFetcher; +class KURL; +class ScriptExecutionContext; +class SecurityOrigin; +struct SecurityOriginHash; + +// This cache holds subresources used by Web pages: images, scripts, stylesheets, etc. + +// The cache keeps a flexible but bounded window of dead resources that grows/shrinks +// depending on the live resource load. Here's an example of cache growth over time, +// with a min dead resource capacity of 25% and a max dead resource capacity of 50%: + +// |-----| Dead: - +// |----------| Live: + +// --|----------| Cache boundary: | (objects outside this mark have been evicted) +// --|----------++++++++++| +// -------|-----+++++++++++++++| +// -------|-----+++++++++++++++|+++++ + +// Enable this macro to periodically log information about the memory cache. +#undef MEMORY_CACHE_STATS + +class MemoryCache { + WTF_MAKE_NONCOPYABLE(MemoryCache); WTF_MAKE_FAST_ALLOCATED; +public: + MemoryCache(); + ~MemoryCache() { } + + typedef HashMap<String, Resource*> ResourceMap; + + struct LRUList { + Resource* m_head; + Resource* m_tail; + LRUList() : m_head(0), m_tail(0) { } + }; + + struct TypeStatistic { + int count; + int size; + int liveSize; + int decodedSize; + int encodedSize; + int encodedSizeDuplicatedInDataURLs; + int purgeableSize; + int purgedSize; + + TypeStatistic() + : count(0) + , size(0) + , liveSize(0) + , decodedSize(0) + , encodedSize(0) + , encodedSizeDuplicatedInDataURLs(0) + , purgeableSize(0) + , purgedSize(0) + { + } + + void addResource(Resource*); + }; + + struct Statistics { + TypeStatistic images; + TypeStatistic cssStyleSheets; + TypeStatistic scripts; + TypeStatistic xslStyleSheets; + TypeStatistic fonts; + TypeStatistic other; + }; + + Resource* resourceForURL(const KURL&); + + void add(Resource*); + void replace(Resource* newResource, Resource* oldResource); + void remove(Resource* resource) { evict(resource); } + + static KURL removeFragmentIdentifierIfNeeded(const KURL& originalURL); + + // 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); + void setDelayBeforeLiveDecodedPrune(unsigned seconds) { m_delayBeforeLiveDecodedPrune = seconds; } + + void evictResources(); + + void prune(); + + // Calls to put the cached resource into and out of LRU lists. + void insertInLRUList(Resource*); + void removeFromLRUList(Resource*); + + // Called to adjust the cache totals when a resource changes size. + void adjustSize(bool live, int delta); + + // Track decoded resources that are in the cache and referenced by a Web page. + void insertInLiveDecodedResourcesList(Resource*); + void removeFromLiveDecodedResourcesList(Resource*); + + void addToLiveResourcesSize(Resource*); + void removeFromLiveResourcesSize(Resource*); + + static void removeURLFromCache(ScriptExecutionContext*, const KURL&); + + Statistics getStatistics(); + + 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; } + +private: + LRUList* lruListFor(Resource*); + +#ifdef MEMORY_CACHE_STATS + void dumpStats(Timer<MemoryCache>*); + void dumpLRULists(bool includeLive) const; +#endif + + unsigned liveCapacity() const; + unsigned deadCapacity() const; + + // pruneDeadResources() - Flush decoded and encoded data from resources not referenced by Web pages. + // pruneLiveResources() - Flush decoded data from resources still referenced by Web pages. + void pruneDeadResources(); // Automatically decide how much to prune. + void pruneLiveResources(); + + void evict(Resource*); + + static void removeURLFromCacheInternal(ScriptExecutionContext*, const KURL&); + + bool m_inPruneResources; + + unsigned m_capacity; + unsigned m_minDeadCapacity; + unsigned m_maxDeadCapacity; + unsigned m_delayBeforeLiveDecodedPrune; + double 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. + + // 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; + + // Lists just for live resources with decoded data. Access to this list is based off of painting the resource. + // The lists are ordered by decode priority, with higher indices having higher priorities. + LRUList m_liveDecodedResources[Resource::CacheLiveResourcePriorityHigh + 1]; + + // 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). + HashMap<String, Resource*> m_resources; + +#ifdef MEMORY_CACHE_STATS + Timer<MemoryCache> m_statsTimer; +#endif +}; + +// Returns the global cache. +MemoryCache* memoryCache(); + +// Sets the global cache, used to swap in a test instance. +void setMemoryCacheForTesting(MemoryCache*); + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCacheTest.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCacheTest.cpp new file mode 100644 index 00000000000..1a365179772 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/MemoryCacheTest.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013, 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: + * + * * 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 Google 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 "core/loader/cache/MemoryCache.h" + +#include "core/loader/cache/MockImageResourceClient.h" +#include "core/loader/cache/RawResource.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/platform/network/ResourceRequest.h" +#include "wtf/OwnPtr.h" + +#include <gtest/gtest.h> + +namespace WebCore { + +class MemoryCacheTest : public ::testing::Test { +public: + class MockImageResource : public WebCore::Resource { + public: + MockImageResource(const ResourceRequest& request, Type type) + : Resource(request, type) + { + } + + virtual void appendData(const char* data, int len) + { + Resource::appendData(data, len); + setDecodedSize(this->size()); + } + + virtual void destroyDecodedData() + { + setDecodedSize(0); + } + }; + +protected: + virtual void SetUp() + { + // Save the global memory cache to restore it upon teardown. + m_globalMemoryCache = adoptPtr(memoryCache()); + // Create the test memory cache instance and hook it in. + m_testingMemoryCache = adoptPtr(new MemoryCache()); + setMemoryCacheForTesting(m_testingMemoryCache.leakPtr()); + } + + virtual void TearDown() + { + // Regain the ownership of testing memory cache, so that it will be + // destroyed. + m_testingMemoryCache = adoptPtr(memoryCache()); + // Yield the ownership of the global memory cache back. + setMemoryCacheForTesting(m_globalMemoryCache.leakPtr()); + } + + OwnPtr<MemoryCache> m_testingMemoryCache; + OwnPtr<MemoryCache> m_globalMemoryCache; +}; + +// Verifies that setters and getters for cache capacities work correcty. +TEST_F(MemoryCacheTest, CapacityAccounting) +{ + const unsigned totalCapacity = 100; + const unsigned minDeadCapacity = 10; + const unsigned maxDeadCapacity = 50; + memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity); + + ASSERT_EQ(totalCapacity, memoryCache()->capacity()); + ASSERT_EQ(minDeadCapacity, memoryCache()->minDeadCapacity()); + ASSERT_EQ(maxDeadCapacity, memoryCache()->maxDeadCapacity()); +} + +// Verifies that dead resources that exceed dead resource capacity are evicted +// from cache when pruning. +TEST_F(MemoryCacheTest, DeadResourceEviction) +{ + const unsigned totalCapacity = 1000000; + const unsigned minDeadCapacity = 0; + const unsigned maxDeadCapacity = 0; + memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity); + + ResourcePtr<Resource> cachedResource = + new Resource(ResourceRequest(""), Resource::Raw); + const char data[5] = "abcd"; + cachedResource->appendData(data, 3); + // The resource size has to be nonzero for this test to be meaningful, but + // we do not rely on it having any particular value. + ASSERT_GT(cachedResource->size(), 0u); + + ASSERT_EQ(0u, memoryCache()->deadSize()); + ASSERT_EQ(0u, memoryCache()->liveSize()); + + memoryCache()->add(cachedResource.get()); + ASSERT_EQ(cachedResource->size(), memoryCache()->deadSize()); + ASSERT_EQ(0u, memoryCache()->liveSize()); + + memoryCache()->prune(); + ASSERT_EQ(0u, memoryCache()->deadSize()); + ASSERT_EQ(0u, memoryCache()->liveSize()); +} + +// Verifies that CachedResources are evicted from the decode cache +// according to their DecodeCachePriority. +TEST_F(MemoryCacheTest, DecodeCacheOrder) +{ + memoryCache()->setDelayBeforeLiveDecodedPrune(0); + ResourcePtr<MockImageResource> cachedImageLowPriority = + new MockImageResource(ResourceRequest(""), Resource::Raw); + ResourcePtr<MockImageResource> cachedImageHighPriority = + new MockImageResource(ResourceRequest(""), Resource::Raw); + + MockImageResourceClient clientLowPriority; + MockImageResourceClient clientHighPriority; + cachedImageLowPriority->addClient(&clientLowPriority); + cachedImageHighPriority->addClient(&clientHighPriority); + + const char data[5] = "abcd"; + cachedImageLowPriority->appendData(data, 1); + cachedImageHighPriority->appendData(data, 4); + const unsigned lowPrioritySize = cachedImageLowPriority->size(); + const unsigned highPrioritySize = cachedImageHighPriority->size(); + const unsigned lowPriorityMockDecodeSize = cachedImageLowPriority->decodedSize(); + const unsigned highPriorityMockDecodeSize = cachedImageHighPriority->decodedSize(); + const unsigned totalSize = lowPrioritySize + highPrioritySize; + + // Verify that the sizes are different to ensure that we can test eviction order. + ASSERT_GT(lowPrioritySize, 0u); + ASSERT_NE(lowPrioritySize, highPrioritySize); + ASSERT_GT(lowPriorityMockDecodeSize, 0u); + ASSERT_NE(lowPriorityMockDecodeSize, highPriorityMockDecodeSize); + + ASSERT_EQ(memoryCache()->deadSize(), 0u); + ASSERT_EQ(memoryCache()->liveSize(), 0u); + + // Add the items. The item added first would normally be evicted first. + memoryCache()->add(cachedImageHighPriority.get()); + ASSERT_EQ(memoryCache()->deadSize(), 0u); + ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize); + + memoryCache()->add(cachedImageLowPriority.get()); + ASSERT_EQ(memoryCache()->deadSize(), 0u); + ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize + lowPrioritySize); + + // Insert all items in the decoded items list with the same priority + memoryCache()->insertInLiveDecodedResourcesList(cachedImageHighPriority.get()); + memoryCache()->insertInLiveDecodedResourcesList(cachedImageLowPriority.get()); + ASSERT_EQ(memoryCache()->deadSize(), 0u); + ASSERT_EQ(memoryCache()->liveSize(), totalSize); + + // Now we will assign their priority and make sure they are moved to the correct buckets. + cachedImageLowPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityLow); + cachedImageHighPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityHigh); + + // Should first prune the LowPriority item. + memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); + memoryCache()->prune(); + ASSERT_EQ(memoryCache()->deadSize(), 0u); + ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize); + + // Should prune the HighPriority item. + memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); + memoryCache()->prune(); + ASSERT_EQ(memoryCache()->deadSize(), 0u); + ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize - highPriorityMockDecodeSize); +} +} // namespace diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/MockImageResourceClient.h b/chromium/third_party/WebKit/Source/core/loader/cache/MockImageResourceClient.h new file mode 100644 index 00000000000..adaa90f527a --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/MockImageResourceClient.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, 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: + * + * * 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 Google 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. + */ + +#ifndef MockImageResourceClient_h +#define MockImageResourceClient_h + +#include "core/loader/cache/ImageResourceClient.h" +#include "core/page/Frame.h" +#include "core/platform/graphics/Image.h" + +#include <gtest/gtest.h> + +namespace WebCore { + +class MockImageResourceClient : public WebCore::ImageResourceClient { +public: + MockImageResourceClient() + : m_imageChangedCount(0) + , m_notifyFinishedCalled(false) + { + } + + virtual ~MockImageResourceClient() { } + virtual void imageChanged(ImageResource*, const IntRect*) + { + m_imageChangedCount++; + } + + virtual void notifyFinished(Resource*) + { + ASSERT_FALSE(m_notifyFinishedCalled); + m_notifyFinishedCalled = true; + } + + int imageChangedCount() const { return m_imageChangedCount; } + bool notifyFinishedCalled() const { return m_notifyFinishedCalled; } + +private: + int m_imageChangedCount; + bool m_notifyFinishedCalled; +}; + +} // namespace WebCore + +#endif // ImageResourceTest_h diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/RawResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/RawResource.cpp new file mode 100644 index 00000000000..a2a0dc4eacd --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/RawResource.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2011 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 APPLE 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 + * 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 "core/loader/cache/RawResource.h" + +#include "core/loader/ResourceLoader.h" +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourceClientWalker.h" +#include "core/loader/cache/ResourceFetcher.h" +#include "core/platform/SharedBuffer.h" + +namespace WebCore { + +RawResource::RawResource(const ResourceRequest& resourceRequest, Type type) + : Resource(resourceRequest, type) +{ +} + +void RawResource::appendData(const char* data, int length) +{ + Resource::appendData(data, length); + + ResourcePtr<RawResource> protect(this); + ResourceClientWalker<RawResourceClient> w(m_clients); + while (RawResourceClient* c = w.next()) + c->dataReceived(this, data, length); +} + +void RawResource::didAddClient(ResourceClient* 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. + ResourcePtr<RawResource> protect(this); + RawResourceClient* client = static_cast<RawResourceClient*>(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); + if (!hasClient(c)) + return; + } + ASSERT(redirectCount == m_redirectChain.size()); + + if (!m_response.isNull()) + client->responseReceived(this, m_response); + if (!hasClient(c)) + return; + if (m_data) + client->dataReceived(this, m_data->data(), m_data->size()); + if (!hasClient(c)) + return; + Resource::didAddClient(client); +} + +void RawResource::willSendRequest(ResourceRequest& request, const ResourceResponse& response) +{ + ResourcePtr<RawResource> protect(this); + if (!response.isNull()) { + ResourceClientWalker<RawResourceClient> w(m_clients); + while (RawResourceClient* c = w.next()) + c->redirectReceived(this, request, response); + m_redirectChain.append(RedirectPair(request, response)); + } + Resource::willSendRequest(request, response); +} + +void RawResource::responseReceived(const ResourceResponse& response) +{ + ResourcePtr<RawResource> protect(this); + Resource::responseReceived(response); + ResourceClientWalker<RawResourceClient> w(m_clients); + while (RawResourceClient* c = w.next()) + c->responseReceived(this, m_response); +} + +void RawResource::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ResourceClientWalker<RawResourceClient> w(m_clients); + while (RawResourceClient* c = w.next()) + c->dataSent(this, bytesSent, totalBytesToBeSent); +} + +void RawResource::didDownloadData(int dataLength) +{ + ResourceClientWalker<RawResourceClient> w(m_clients); + while (RawResourceClient* c = w.next()) + c->dataDownloaded(this, dataLength); +} + +void RawResource::setDefersLoading(bool defers) +{ + if (m_loader) + m_loader->setDefersLoading(defers); +} + +void RawResource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) +{ + m_options.dataBufferingPolicy = dataBufferingPolicy; + clear(); +} + +static bool shouldIgnoreHeaderForCacheReuse(AtomicString headerName) +{ + // 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("If-Modified-Since"); + m_headers.add("If-None-Match"); + m_headers.add("Origin"); + m_headers.add("Pragma"); + m_headers.add("Purpose"); + m_headers.add("Referer"); + m_headers.add("User-Agent"); + } + return m_headers.contains(headerName); +} + +bool RawResource::canReuse(const ResourceRequest& newRequest) const +{ + if (m_options.dataBufferingPolicy == DoNotBufferData) + return false; + + if (m_resourceRequest.httpMethod() != newRequest.httpMethod()) + return false; + + if (m_resourceRequest.httpBody() != newRequest.httpBody()) + return false; + + if (m_resourceRequest.allowCookies() != newRequest.allowCookies()) + 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 + // headers that we might permit to be different and still reuse the existing Resource. + 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)) + return false; + } + + for (size_t i = 0; i < m_redirectChain.size(); i++) { + if (m_redirectChain[i].m_redirectResponse.cacheControlContainsNoStore()) + return false; + } + + return true; +} + +void RawResource::clear() +{ + m_data.clear(); + setEncodedSize(0); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/RawResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/RawResource.h new file mode 100644 index 00000000000..05cf3f4d8bf --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/RawResource.h @@ -0,0 +1,90 @@ +/* + 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 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 + 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 RawResource_h +#define RawResource_h + +#include "core/loader/cache/Resource.h" +#include "core/loader/cache/ResourceClient.h" + +namespace WebCore { +class RawResourceCallback; +class RawResourceClient; + +class RawResource : public Resource { +public: + RawResource(const ResourceRequest&, Type); + + // FIXME: AssociatedURLLoader shouldn't be a DocumentThreadableLoader and therefore shouldn't + // use RawResource. However, it is, and it needs to be able to defer loading. + // This can be fixed by splitting CORS preflighting out of DocumentThreacableLoader. + virtual void setDefersLoading(bool); + + virtual void setDataBufferingPolicy(DataBufferingPolicy); + + void clear(); + + virtual bool canReuse(const ResourceRequest&) const; + +private: + virtual void didAddClient(ResourceClient*); + virtual void appendData(const char*, int) OVERRIDE; + + virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return true; } + + virtual void willSendRequest(ResourceRequest&, const ResourceResponse&); + virtual void responseReceived(const ResourceResponse&); + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); + virtual void didDownloadData(int); + + struct RedirectPair { + public: + explicit RedirectPair(const ResourceRequest& request, const ResourceResponse& redirectResponse) + : m_request(request) + , m_redirectResponse(redirectResponse) + { + } + + const ResourceRequest m_request; + const ResourceResponse m_redirectResponse; + }; + + Vector<RedirectPair> m_redirectChain; +}; + + +class RawResourceClient : public ResourceClient { +public: + virtual ~RawResourceClient() { } + static ResourceClientType expectedType() { return RawResourceType; } + virtual ResourceClientType resourceClientType() const { return expectedType(); } + + virtual void dataSent(Resource*, unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { } + virtual void responseReceived(Resource*, const ResourceResponse&) { } + virtual void dataReceived(Resource*, const char* /* data */, int /* length */) { } + virtual void redirectReceived(Resource*, ResourceRequest&, const ResourceResponse&) { } + virtual void dataDownloaded(Resource*, int) { } +}; + +} + +#endif // RawResource_h diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/Resource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/Resource.cpp new file mode 100644 index 00000000000..ae832e127d3 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/Resource.cpp @@ -0,0 +1,839 @@ +/* + 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, 2009, 2010, 2011 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 + 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 "core/loader/cache/Resource.h" + +#include "core/inspector/InspectorInstrumentation.h" +#include "core/loader/CachedMetadata.h" +#include "core/loader/CrossOriginAccessControl.h" +#include "core/loader/ResourceLoader.h" +#include "core/loader/cache/MemoryCache.h" +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourceClientWalker.h" +#include "core/loader/cache/ResourceFetcher.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/platform/Logging.h" +#include "core/platform/PurgeableBuffer.h" +#include "core/platform/SharedBuffer.h" +#include "public/platform/Platform.h" +#include "weborigin/KURL.h" +#include "wtf/CurrentTime.h" +#include "wtf/MathExtras.h" +#include "wtf/RefCountedLeakCounter.h" +#include "wtf/StdLibExtras.h" +#include "wtf/Vector.h" +#include "wtf/text/CString.h" + +using namespace WTF; + +namespace WebCore { + +// 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 (header == headersToIgnoreAfterRevalidation[i]) + return false; + } + for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { + if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i])) + return false; + } + return true; +} + +DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("Resource")); + +Resource::Resource(const ResourceRequest& request, Type type) + : m_resourceRequest(request) + , m_responseTimestamp(currentTime()) + , m_cancelTimer(this, &Resource::cancelTimerFired) + , m_lastDecodedAccessTime(0) + , m_loadFinishTime(0) + , m_identifier(0) + , m_encodedSize(0) + , m_decodedSize(0) + , m_accessCount(0) + , m_handleCount(0) + , m_preloadCount(0) + , m_preloadResult(PreloadNotReferenced) + , m_cacheLiveResourcePriority(CacheLiveResourcePriorityLow) + , m_inLiveDecodedResourcesList(false) + , m_requestedFromNetworkingLayer(false) + , m_inCache(false) + , m_loading(false) + , m_switchingClientsToRevalidatedResource(false) + , m_type(type) + , m_status(Pending) +#ifndef NDEBUG + , m_deleted(false) + , m_lruIndex(0) +#endif + , m_nextInAllResourcesList(0) + , m_prevInAllResourcesList(0) + , m_nextInLiveResourcesList(0) + , m_prevInLiveResourcesList(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. +#ifndef NDEBUG + cachedResourceLeakCounter.increment(); +#endif + + if (!m_resourceRequest.url().hasFragmentIdentifier()) + return; + KURL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url()); + if (urlForCache.hasFragmentIdentifier()) + return; + m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier(); + m_resourceRequest.setURL(urlForCache); +} + +Resource::~Resource() +{ + ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this. + ASSERT(canDelete()); + ASSERT(!inCache()); + ASSERT(!m_deleted); + ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this); + +#ifndef NDEBUG + m_deleted = true; + cachedResourceLeakCounter.decrement(); +#endif +} + +void Resource::failBeforeStarting() +{ + LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data()); + error(Resource::LoadError); +} + +void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options) +{ + if (!fetcher->frame()) { + failBeforeStarting(); + return; + } + + m_options = options; + m_loading = true; + + if (!accept().isEmpty()) + m_resourceRequest.setHTTPAccept(accept()); + + // 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. + ResourceRequest request(m_resourceRequest); + if (!m_fragmentIdentifierForRequest.isNull()) { + KURL url = request.url(); + url.setFragmentIdentifier(m_fragmentIdentifierForRequest); + request.setURL(url); + m_fragmentIdentifierForRequest = String(); + } + + m_loader = ResourceLoader::create(fetcher, this, request, options); + if (!m_loader) { + failBeforeStarting(); + return; + } + m_status = Pending; +} + +void Resource::checkNotify() +{ + if (isLoading()) + return; + + ResourceClientWalker<ResourceClient> w(m_clients); + while (ResourceClient* c = w.next()) + c->notifyFinished(this); +} + +void Resource::appendData(const char* data, int length) +{ + ASSERT(!m_resourceToRevalidate); + ASSERT(!errorOccurred()); + if (m_options.dataBufferingPolicy == DoNotBufferData) + return; + if (m_data) + m_data->append(data, length); + else + m_data = SharedBuffer::create(data, length); + setEncodedSize(m_data->size()); +} + +void Resource::error(Resource::Status status) +{ + if (m_resourceToRevalidate) + revalidationFailed(); + + if (!m_error.isNull() && (m_error.isCancellation() || !isPreloaded())) + memoryCache()->remove(this); + + setStatus(status); + ASSERT(errorOccurred()); + m_data.clear(); + + setLoading(false); + checkNotify(); +} + +void Resource::finishOnePart() +{ + setLoading(false); + checkNotify(); +} + +void Resource::finish(double finishTime) +{ + ASSERT(!m_resourceToRevalidate); + ASSERT(!errorOccurred()); + m_loadFinishTime = finishTime; + finishOnePart(); + if (!errorOccurred()) + m_status = Cached; +} + +bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) +{ + String ignoredErrorDescription; + return passesAccessControlCheck(securityOrigin, ignoredErrorDescription); +} + +bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin, String& errorDescription) +{ + return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription); +} + +bool Resource::isExpired() const +{ + if (m_response.isNull()) + return false; + + return currentAge() > freshnessLifetime(); +} + +double Resource::currentAge() const +{ + // 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; +} + +double Resource::freshnessLifetime() const +{ + // Cache non-http resources liberally + if (!m_response.url().protocolIsInHTTPFamily()) + return std::numeric_limits<double>::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; +} + +void Resource::responseReceived(const ResourceResponse& response) +{ + setResponse(response); + m_responseTimestamp = currentTime(); + String encoding = response.textEncodingName(); + if (!encoding.isNull()) + setEncoding(encoding); + + if (!m_resourceToRevalidate) + return; + if (response.httpStatusCode() == 304) + revalidationSucceeded(response); + else + revalidationFailed(); +} + +void Resource::setSerializedCachedMetadata(const char* data, size_t size) +{ + // We only expect to receive cached metadata from the platform once. + // If this triggers, it indicates an efficiency problem which is most + // likely unexpected in code designed to improve performance. + ASSERT(!m_cachedMetadata); + ASSERT(!m_resourceToRevalidate); + + m_cachedMetadata = CachedMetadata::deserialize(data, size); +} + +void Resource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size) +{ + // Currently, only one type of cached metadata per resource is supported. + // If the need arises for multiple types of metadata per resource this could + // be enhanced to store types of metadata in a map. + ASSERT(!m_cachedMetadata); + + m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size); + const Vector<char>& serializedData = m_cachedMetadata->serialize(); + WebKit::Platform::current()->cacheMetadata(m_response.url(), m_response.responseTime(), serializedData.data(), serializedData.size()); +} + +CachedMetadata* Resource::cachedMetadata(unsigned dataTypeID) const +{ + if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID) + return 0; + return m_cachedMetadata.get(); +} + +void Resource::setCacheLiveResourcePriority(CacheLiveResourcePriority priority) +{ + if (inCache() && m_inLiveDecodedResourcesList && m_cacheLiveResourcePriority != priority) { + memoryCache()->removeFromLiveDecodedResourcesList(this); + m_cacheLiveResourcePriority = priority; + memoryCache()->insertInLiveDecodedResourcesList(this); + memoryCache()->prune(); + } +} + +void Resource::clearLoader() +{ + m_loader = 0; +} + +void Resource::addClient(ResourceClient* client) +{ + if (addClientToSet(client)) + didAddClient(client); +} + +void Resource::didAddClient(ResourceClient* c) +{ + if (m_clientsAwaitingCallback.contains(c)) { + m_clients.add(c); + m_clientsAwaitingCallback.remove(c); + } + if (!isLoading() && !stillNeedsLoad()) + c->notifyFinished(this); +} + +bool Resource::addClientToSet(ResourceClient* client) +{ + ASSERT(!isPurgeable()); + + if (m_preloadResult == PreloadNotReferenced) { + if (isLoaded()) + m_preloadResult = PreloadReferencedWhileComplete; + else if (m_requestedFromNetworkingLayer) + m_preloadResult = PreloadReferencedWhileLoading; + else + m_preloadResult = PreloadReferenced; + } + if (!hasClients() && inCache()) + memoryCache()->addToLiveResourcesSize(this); + + if ((m_type == Raw || 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 Resources, + // we schedule the callbacks and ensure we never finish synchronously. + ASSERT(!m_clientsAwaitingCallback.contains(client)); + m_clientsAwaitingCallback.add(client, ResourceCallback::schedule(this, client)); + return false; + } + + m_clients.add(client); + return true; +} + +void Resource::removeClient(ResourceClient* client) +{ + OwnPtr<ResourceCallback> callback = m_clientsAwaitingCallback.take(client); + if (callback) { + ASSERT(!m_clients.contains(client)); + callback->cancel(); + callback.clear(); + } else { + 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(); + 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(); + } + } + // This object may be dead here. +} + +void Resource::allClientsRemoved() +{ + if (!m_loader) + return; + if (m_type == MainResource || m_type == Raw) + cancelTimerFired(&m_cancelTimer); + else if (!m_cancelTimer.isActive()) + m_cancelTimer.startOneShot(0); +} + +void Resource::cancelTimerFired(Timer<Resource>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_cancelTimer); + if (hasClients() || !m_loader) + return; + ResourcePtr<Resource> protect(this); + m_loader->cancelIfNotFinishing(); + if (m_status != Cached) + memoryCache()->remove(this); +} + +bool Resource::deleteIfPossible() +{ + if (canDelete() && !inCache()) { + InspectorInstrumentation::willDestroyResource(this); + delete this; + return true; + } + return false; +} + +void Resource::setDecodedSize(unsigned size) +{ + if (size == m_decodedSize) + return; + + int delta = size - m_decodedSize; + + // 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()) { + // Now insert into the new LRU list. + memoryCache()->insertInLRUList(this); + + // Insert into or remove from the live decoded list if necessary. + // When inserting into the LiveDecodedResourcesList it is possible + // that the m_lastDecodedAccessTime is still zero or smaller than + // the m_lastDecodedAccessTime of the current list head. This is a + // 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); + + // Update the cache's size totals. + memoryCache()->adjustSize(hasClients(), delta); + } +} + +void Resource::setEncodedSize(unsigned size) +{ + if (size == m_encodedSize) + return; + + int delta = size - m_encodedSize; + + // 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); + } +} + +void Resource::didAccessDecodedData(double timeStamp) +{ + m_lastDecodedAccessTime = timeStamp; + if (inCache()) { + if (m_inLiveDecodedResourcesList) { + memoryCache()->removeFromLiveDecodedResourcesList(this); + memoryCache()->insertInLiveDecodedResourcesList(this); + } + memoryCache()->prune(); + } +} + +void Resource::setResourceToRevalidate(Resource* resource) +{ + ASSERT(resource); + ASSERT(!m_resourceToRevalidate); + ASSERT(resource != this); + ASSERT(m_handlesToRevalidate.isEmpty()); + ASSERT(resource->type() == type()); + + LOG(ResourceLoading, "Resource %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 Resource::clearResourceToRevalidate. + ASSERT(!resource->m_proxyResource); + + resource->m_proxyResource = this; + m_resourceToRevalidate = resource; +} + +void Resource::clearResourceToRevalidate() +{ + ASSERT(m_resourceToRevalidate); + 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_handlesToRevalidate.clear(); + m_resourceToRevalidate = 0; + deleteIfPossible(); +} + +void Resource::switchClientsToRevalidatedResource() +{ + ASSERT(m_resourceToRevalidate); + ASSERT(m_resourceToRevalidate->inCache()); + ASSERT(!inCache()); + + LOG(ResourceLoading, "Resource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate); + + m_resourceToRevalidate->m_identifier = m_identifier; + + m_switchingClientsToRevalidatedResource = true; + HashSet<ResourcePtrBase*>::iterator end = m_handlesToRevalidate.end(); + for (HashSet<ResourcePtrBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { + ResourcePtrBase* handle = *it; + handle->m_resource = m_resourceToRevalidate; + m_resourceToRevalidate->registerHandle(handle); + --m_handleCount; + } + ASSERT(!m_handleCount); + m_handlesToRevalidate.clear(); + + Vector<ResourceClient*> clientsToMove; + HashCountedSet<ResourceClient*>::iterator end2 = m_clients.end(); + for (HashCountedSet<ResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) { + ResourceClient* client = it->key; + unsigned count = it->value; + while (count) { + clientsToMove.append(client); + --count; + } + } + + unsigned moveCount = clientsToMove.size(); + for (unsigned n = 0; n < moveCount; ++n) + removeClient(clientsToMove[n]); + ASSERT(m_clients.isEmpty()); + + for (unsigned n = 0; n < moveCount; ++n) + m_resourceToRevalidate->addClientToSet(clientsToMove[n]); + for (unsigned n = 0; n < moveCount; ++n) { + // 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]); + } + m_switchingClientsToRevalidatedResource = false; +} + +void Resource::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); + } +} + +void Resource::revalidationSucceeded(const ResourceResponse& response) +{ + ASSERT(m_resourceToRevalidate); + ASSERT(!m_resourceToRevalidate->inCache()); + ASSERT(m_resourceToRevalidate->isLoaded()); + ASSERT(inCache()); + + // Calling evict() 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(!canDelete()); + + m_resourceToRevalidate->updateResponseAfterRevalidation(response); + memoryCache()->replace(m_resourceToRevalidate, this); + + switchClientsToRevalidatedResource(); + ASSERT(!m_deleted); + // clearResourceToRevalidate deletes this. + clearResourceToRevalidate(); +} + +void Resource::revalidationFailed() +{ + ASSERT(WTF::isMainThread()); + LOG(ResourceLoading, "Revalidation failed for %p", this); + ASSERT(resourceToRevalidate()); + clearResourceToRevalidate(); +} + +void Resource::updateForAccess() +{ + ASSERT(inCache()); + + // Need to make sure to remove before we increase the access count, since + // the queue will possibly change. + memoryCache()->removeFromLRUList(this); + + // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size. + if (!m_accessCount) + memoryCache()->adjustSize(hasClients(), size()); + + m_accessCount++; + memoryCache()->insertInLRUList(this); +} + +void Resource::registerHandle(ResourcePtrBase* h) +{ + ++m_handleCount; + if (m_resourceToRevalidate) + m_handlesToRevalidate.add(h); +} + +void Resource::unregisterHandle(ResourcePtrBase* h) +{ + ASSERT(m_handleCount > 0); + --m_handleCount; + + if (m_resourceToRevalidate) + m_handlesToRevalidate.remove(h); + + if (!m_handleCount) + deleteIfPossible(); +} + +bool Resource::canUseCacheValidator() const +{ + if (m_loading || errorOccurred()) + return false; + + if (m_response.cacheControlContainsNoStore()) + return false; + return m_response.hasCacheValidatorFields(); +} + +bool Resource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const +{ + ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify); + + if (cachePolicy == CachePolicyRevalidate) + return true; + + if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) { + LOG(ResourceLoading, "Resource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this); + return true; + } + + if (cachePolicy == CachePolicyCache) { + if (m_response.cacheControlContainsMustRevalidate() && isExpired()) { + LOG(ResourceLoading, "Resource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this); + return true; + } + return false; + } + + // CachePolicyVerify + if (isExpired()) { + LOG(ResourceLoading, "Resource %p mustRevalidate because of isExpired()\n", this); + return true; + } + + return false; +} + +bool Resource::isSafeToMakePurgeable() const +{ + return !hasClients() && !m_proxyResource && !m_resourceToRevalidate; +} + +bool Resource::makePurgeable(bool purgeable) +{ + if (purgeable) { + ASSERT(isSafeToMakePurgeable()); + + if (m_purgeableData) { + ASSERT(!m_data); + return true; + } + 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; + + m_data->createPurgeableBuffer(); + if (!m_data->hasPurgeableBuffer()) + return false; + + m_purgeableData = m_data->releasePurgeableBuffer(); + m_purgeableData->unlock(); + m_data.clear(); + return true; + } + + if (!m_purgeableData) + return true; + + ASSERT(!m_data); + ASSERT(!hasClients()); + + if (!m_purgeableData->lock()) + return false; + + m_data = SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()); + return true; +} + +bool Resource::isPurgeable() const +{ + return m_purgeableData && m_purgeableData->isPurgeable(); +} + +bool Resource::wasPurged() const +{ + return m_purgeableData && m_purgeableData->wasPurged(); +} + +unsigned Resource::overheadSize() const +{ + static const int kAverageClientsHashMapSize = 384; + return sizeof(Resource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2; +} + +void Resource::didChangePriority(ResourceLoadPriority loadPriority) +{ + if (m_loader) + m_loader->didChangePriority(loadPriority); +} + +Resource::ResourceCallback::ResourceCallback(Resource* resource, ResourceClient* client) + : m_resource(resource) + , m_client(client) + , m_callbackTimer(this, &ResourceCallback::timerFired) +{ + m_callbackTimer.startOneShot(0); +} + +void Resource::ResourceCallback::cancel() +{ + if (m_callbackTimer.isActive()) + m_callbackTimer.stop(); +} + +void Resource::ResourceCallback::timerFired(Timer<ResourceCallback>*) +{ + m_resource->didAddClient(m_client); +} + +} + diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/Resource.h b/chromium/third_party/WebKit/Source/core/loader/cache/Resource.h new file mode 100644 index 00000000000..dd092e3e9f9 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/Resource.h @@ -0,0 +1,361 @@ +/* + 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, 2009, 2010, 2011 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 + 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 Resource_h +#define Resource_h + +#include "core/loader/ResourceLoaderOptions.h" +#include "core/loader/cache/CachePolicy.h" +#include "core/platform/Timer.h" +#include "core/platform/network/ResourceError.h" +#include "core/platform/network/ResourceLoadPriority.h" +#include "core/platform/network/ResourceRequest.h" +#include "core/platform/network/ResourceResponse.h" +#include "wtf/HashCountedSet.h" +#include "wtf/HashSet.h" +#include "wtf/OwnPtr.h" +#include "wtf/text/WTFString.h" + +namespace WebCore { + +class MemoryCache; +class CachedMetadata; +class ResourceClient; +class ResourcePtrBase; +class ResourceFetcher; +class InspectorResource; +class PurgeableBuffer; +class ResourceLoader; +class SecurityOrigin; +class SharedBuffer; + +// A resource that is held in the cache. Classes who want to use this object should derive +// from ResourceClient, to get the function calls in case the requested data has arrived. +// This class also does the actual communication with the loader to obtain the resource from the network. +class Resource { + WTF_MAKE_NONCOPYABLE(Resource); WTF_MAKE_FAST_ALLOCATED; + friend class MemoryCache; + friend class InspectorResource; + +public: + enum Type { + MainResource, + Image, + CSSStyleSheet, + Script, + Font, + Raw, + SVGDocument, + XSLStyleSheet, + LinkPrefetch, + LinkSubresource, + TextTrack, + Shader, + ImportResource + }; + + enum Status { + Unknown, // let cache decide what to do with it + Pending, // only partially loaded + Cached, // regular case + LoadError, + DecodeError + }; + + Resource(const ResourceRequest&, Type); + virtual ~Resource(); + + // Determines the order in which CachedResources are evicted + // from the decoded resources cache. + enum CacheLiveResourcePriority { + CacheLiveResourcePriorityLow = 0, + CacheLiveResourcePriorityHigh + }; + + virtual void load(ResourceFetcher*, const ResourceLoaderOptions&); + + virtual void setEncoding(const String&) { } + virtual String encoding() const { return String(); } + virtual void appendData(const char*, int); + virtual void error(Resource::Status); + + void setResourceError(const ResourceError& error) { m_error = error; } + const ResourceError& resourceError() const { return m_error; } + + void setIdentifier(unsigned long identifier) { m_identifier = identifier; } + unsigned long identifier() const { return m_identifier; } + + virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; } + + ResourceRequest& resourceRequest() { return m_resourceRequest; } + const KURL& url() const { return m_resourceRequest.url();} + Type type() const { return static_cast<Type>(m_type); } + const ResourceLoaderOptions& options() const { return m_options; } + + void didChangePriority(ResourceLoadPriority); + + void addClient(ResourceClient*); + void removeClient(ResourceClient*); + bool hasClients() const { return !m_clients.isEmpty() || !m_clientsAwaitingCallback.isEmpty(); } + bool deleteIfPossible(); + + enum PreloadResult { + PreloadNotReferenced, + PreloadReferenced, + PreloadReferencedWhileLoading, + PreloadReferencedWhileComplete + }; + PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); } + + virtual void didAddClient(ResourceClient*); + virtual void didRemoveClient(ResourceClient*) { } + virtual void allClientsRemoved(); + + unsigned count() const { return m_clients.size(); } + + Status status() const { return static_cast<Status>(m_status); } + void setStatus(Status status) { m_status = status; } + + unsigned size() const { return encodedSize() + decodedSize() + overheadSize(); } + 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; } + void setLoading(bool b) { m_loading = b; } + virtual bool stillNeedsLoad() const { return false; } + + ResourceLoader* loader() { return m_loader.get(); } + + virtual bool isImage() const { return false; } + bool ignoreForRequestCount() const + { + return type() == MainResource + || type() == LinkPrefetch + || type() == LinkSubresource + || type() == Raw; + } + + void updateForAccess(); + unsigned accessCount() const { return m_accessCount; } + + // Computes the status of an object after loading. + // Updates the expire date on the cache entry file + void finish(double finishTime = 0.0); + + // FIXME: Remove the stringless variant once all the callsites' error messages are updated. + bool passesAccessControlCheck(SecurityOrigin*); + bool passesAccessControlCheck(SecurityOrigin*, String& errorDescription); + + // 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; } + + void setCacheLiveResourcePriority(CacheLiveResourcePriority); + unsigned cacheLiveResourcePriority() const { return m_cacheLiveResourcePriority; } + bool inLiveDecodedResourcesList() { return m_inLiveDecodedResourcesList; } + + void clearLoader(); + + SharedBuffer* resourceBuffer() const { ASSERT(!m_purgeableData); return m_data.get(); } + + virtual void willSendRequest(ResourceRequest&, const ResourceResponse&) { m_requestedFromNetworkingLayer = true; } + virtual void responseReceived(const ResourceResponse&); + void setResponse(const ResourceResponse& response) { m_response = response; } + const ResourceResponse& response() const { return m_response; } + + // Sets the serialized metadata retrieved from the platform's cache. + void setSerializedCachedMetadata(const char*, size_t); + + // Caches the given metadata in association with this resource and suggests + // that the platform persist it. The dataTypeID is a pseudo-randomly chosen + // identifier that is used to distinguish data generated by the caller. + void setCachedMetadata(unsigned dataTypeID, const char*, size_t); + + // Returns cached metadata of the given type associated with this resource. + CachedMetadata* cachedMetadata(unsigned dataTypeID) const; + + 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/*". + AtomicString accept() const { return m_accept; } + void setAccept(const AtomicString& accept) { m_accept = accept; } + + bool wasCanceled() const { return m_error.isCancellation(); } + bool errorOccurred() const { return m_status == LoadError || m_status == DecodeError; } + bool loadFailedOrCanceled() { return !m_error.isNull(); } + + bool shouldSendResourceLoadCallbacks() const { return m_options.sendLoadCallbacks == SendCallbacks; } + DataBufferingPolicy dataBufferingPolicy() const { return m_options.dataBufferingPolicy; } + + virtual void destroyDecodedData() { } + + bool isPreloaded() const { return m_preloadCount; } + void increasePreloadCount() { ++m_preloadCount; } + void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } + + void registerHandle(ResourcePtrBase* h); + void unregisterHandle(ResourcePtrBase* h); + + bool canUseCacheValidator() const; + bool mustRevalidateDueToCacheHeaders(CachePolicy) const; + bool isCacheValidator() const { return m_resourceToRevalidate; } + Resource* resourceToRevalidate() const { return m_resourceToRevalidate; } + void setResourceToRevalidate(Resource*); + + 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); + + virtual void didSendData(unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { } + virtual void didDownloadData(int) { } + + double loadFinishTime() const { return m_loadFinishTime; } + + virtual bool canReuse(const ResourceRequest&) const { return true; } + +protected: + virtual void checkNotify(); + virtual void finishOnePart(); + + void setEncodedSize(unsigned); + void setDecodedSize(unsigned); + void didAccessDecodedData(double timeStamp); + + bool isSafeToMakePurgeable() const; + + virtual void switchClientsToRevalidatedResource(); + void clearResourceToRevalidate(); + void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse); + + HashCountedSet<ResourceClient*> m_clients; + + class ResourceCallback { + public: + static PassOwnPtr<ResourceCallback> schedule(Resource* resource, ResourceClient* client) { return adoptPtr(new ResourceCallback(resource, client)); } + void cancel(); + private: + ResourceCallback(Resource*, ResourceClient*); + void timerFired(Timer<ResourceCallback>*); + + Resource* m_resource; + ResourceClient* m_client; + Timer<ResourceCallback> m_callbackTimer; + }; + HashMap<ResourceClient*, OwnPtr<ResourceCallback> > m_clientsAwaitingCallback; + + bool hasClient(ResourceClient* client) { return m_clients.contains(client) || m_clientsAwaitingCallback.contains(client); } + + ResourceRequest m_resourceRequest; + AtomicString m_accept; + RefPtr<ResourceLoader> m_loader; + ResourceLoaderOptions m_options; + + ResourceResponse m_response; + double m_responseTimestamp; + + RefPtr<SharedBuffer> m_data; + OwnPtr<PurgeableBuffer> m_purgeableData; + Timer<Resource> m_cancelTimer; + +private: + bool addClientToSet(ResourceClient*); + void cancelTimerFired(Timer<Resource>*); + + void revalidationSucceeded(const ResourceResponse&); + void revalidationFailed(); + + double currentAge() const; + double freshnessLifetime() const; + + void failBeforeStarting(); + + String m_fragmentIdentifierForRequest; + + RefPtr<CachedMetadata> m_cachedMetadata; + + ResourceError m_error; + + double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache + double m_loadFinishTime; + + unsigned long m_identifier; + + unsigned m_encodedSize; + unsigned m_decodedSize; + unsigned m_accessCount; + unsigned m_handleCount; + unsigned m_preloadCount; + + unsigned m_preloadResult : 2; // PreloadResult + unsigned m_cacheLiveResourcePriority : 2; // CacheLiveResourcePriority + unsigned m_inLiveDecodedResourcesList : 1; + unsigned m_requestedFromNetworkingLayer : 1; + + unsigned m_inCache : 1; + unsigned m_loading : 1; + + unsigned m_switchingClientsToRevalidatedResource : 1; + + unsigned m_type : 4; // Type + unsigned m_status : 3; // Status + +#ifndef NDEBUG + bool m_deleted; + unsigned m_lruIndex; +#endif + + Resource* m_nextInAllResourcesList; + Resource* m_prevInAllResourcesList; + + Resource* m_nextInLiveResourcesList; + Resource* m_prevInLiveResourcesList; + + // 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. + Resource* m_resourceToRevalidate; + + // If this field is non-null, the resource has a proxy for checking whether it is still up to date (see m_resourceToRevalidate). + Resource* m_proxyResource; + + // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response. + HashSet<ResourcePtrBase*> m_handlesToRevalidate; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourceClient.h b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceClient.h new file mode 100644 index 00000000000..ae7b7bbcf0c --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceClient.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) + Copyright (C) 2001 Dirk Mueller <mueller@kde.org> + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 + 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. + + 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 ResourceClient_h +#define ResourceClient_h + +#include "wtf/FastAllocBase.h" +#include "wtf/Forward.h" + +namespace WebCore { +class Resource; + +class ResourceClient { +public: + enum ResourceClientType { + BaseResourceType, + ImageType, + FontType, + StyleSheetType, + DocumentType, + RawResourceType + }; + + virtual ~ResourceClient() { } + virtual void notifyFinished(Resource*) { } + virtual void deprecatedDidReceiveResource(Resource*) { } + + static ResourceClientType expectedType() { return BaseResourceType; } + virtual ResourceClientType resourceClientType() const { return expectedType(); } + +protected: + ResourceClient() { } +}; +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourceClientWalker.h b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceClientWalker.h new file mode 100644 index 00000000000..aff5f1dcd0b --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceClientWalker.h @@ -0,0 +1,69 @@ +/* + Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) + Copyright (C) 2001 Dirk Mueller <mueller@kde.org> + Copyright (C) 2004, 2005, 2006, 2007 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 + 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. + + 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 ResourceClientWalker_h +#define ResourceClientWalker_h + +#include "core/loader/cache/ResourceClient.h" +#include "wtf/HashCountedSet.h" +#include "wtf/Vector.h" + +namespace WebCore { + +// Call this "walker" instead of iterator so people won't expect Qt or STL-style iterator interface. +// Just keep calling next() on this. It's safe from deletions of items. +template<typename T> class ResourceClientWalker { +public: + ResourceClientWalker(const HashCountedSet<ResourceClient*>& set) + : m_clientSet(set), m_clientVector(set.size()), m_index(0) + { + typedef HashCountedSet<ResourceClient*>::const_iterator Iterator; + Iterator end = set.end(); + size_t clientIndex = 0; + for (Iterator current = set.begin(); current != end; ++current) + m_clientVector[clientIndex++] = current->key; + } + + T* next() + { + size_t size = m_clientVector.size(); + while (m_index < size) { + ResourceClient* next = m_clientVector[m_index++]; + if (m_clientSet.contains(next)) { + ASSERT(T::expectedType() == ResourceClient::expectedType() || next->resourceClientType() == T::expectedType()); + return static_cast<T*>(next); + } + } + + return 0; + } +private: + const HashCountedSet<ResourceClient*>& m_clientSet; + Vector<ResourceClient*> m_clientVector; + size_t m_index; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcher.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcher.cpp new file mode 100644 index 00000000000..28d7c8ced74 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcher.cpp @@ -0,0 +1,1301 @@ +/* + 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) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + + 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. + + 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" +#include "core/loader/cache/ResourceFetcher.h" + +#include "bindings/v8/ScriptController.h" +#include "core/dom/Document.h" +#include "core/html/HTMLElement.h" +#include "core/html/HTMLFrameOwnerElement.h" +#include "core/html/HTMLImport.h" +#include "core/inspector/InspectorInstrumentation.h" +#include "core/loader/DocumentLoader.h" +#include "core/loader/FrameLoader.h" +#include "core/loader/FrameLoaderClient.h" +#include "core/loader/PingLoader.h" +#include "core/loader/UniqueIdentifier.h" +#include "core/loader/appcache/ApplicationCacheHost.h" +#include "core/loader/cache/CSSStyleSheetResource.h" +#include "core/loader/cache/DocumentResource.h" +#include "core/loader/cache/FetchRequest.h" +#include "core/loader/cache/FontResource.h" +#include "core/loader/cache/ImageResource.h" +#include "core/loader/cache/MemoryCache.h" +#include "core/loader/cache/RawResource.h" +#include "core/loader/cache/ScriptResource.h" +#include "core/loader/cache/ShaderResource.h" +#include "core/loader/cache/TextTrackResource.h" +#include "core/loader/cache/XSLStyleSheetResource.h" +#include "core/page/ContentSecurityPolicy.h" +#include "core/page/DOMWindow.h" +#include "core/page/Frame.h" +#include "core/page/Performance.h" +#include "core/page/ResourceTimingInfo.h" +#include "core/page/Settings.h" +#include "core/platform/Logging.h" +#include "core/platform/chromium/TraceEvent.h" +#include "public/platform/Platform.h" +#include "public/platform/WebURL.h" +#include "weborigin/SecurityOrigin.h" +#include "weborigin/SecurityPolicy.h" +#include "wtf/text/CString.h" +#include "wtf/text/WTFString.h" + +#define PRELOAD_DEBUG 0 + +namespace WebCore { + +static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset) +{ + switch (type) { + case Resource::Image: + return new ImageResource(request); + case Resource::CSSStyleSheet: + return new CSSStyleSheetResource(request, charset); + case Resource::Script: + return new ScriptResource(request, charset); + case Resource::SVGDocument: + return new DocumentResource(request, Resource::SVGDocument); + case Resource::Font: + return new FontResource(request); + case Resource::Raw: + case Resource::MainResource: + return new RawResource(request, type); + case Resource::XSLStyleSheet: + return new XSLStyleSheetResource(request); + case Resource::LinkPrefetch: + return new Resource(request, Resource::LinkPrefetch); + case Resource::LinkSubresource: + return new Resource(request, Resource::LinkSubresource); + case Resource::TextTrack: + return new TextTrackResource(request); + case Resource::Shader: + return new ShaderResource(request); + case Resource::ImportResource: + return new RawResource(request, type); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request) +{ + if (request.priority() != ResourceLoadPriorityUnresolved) + return request.priority(); + + switch (type) { + case Resource::MainResource: + return ResourceLoadPriorityVeryHigh; + case Resource::CSSStyleSheet: + return ResourceLoadPriorityHigh; + case Resource::Script: + case Resource::Font: + case Resource::Raw: + case Resource::ImportResource: + return ResourceLoadPriorityMedium; + case Resource::Image: + return request.forPreload() ? ResourceLoadPriorityVeryLow : ResourceLoadPriorityLow; + case Resource::XSLStyleSheet: + return ResourceLoadPriorityHigh; + case Resource::SVGDocument: + return ResourceLoadPriorityLow; + case Resource::LinkPrefetch: + return ResourceLoadPriorityVeryLow; + case Resource::LinkSubresource: + return ResourceLoadPriorityLow; + case Resource::TextTrack: + return ResourceLoadPriorityLow; + case Resource::Shader: + return ResourceLoadPriorityMedium; + } + ASSERT_NOT_REACHED(); + return ResourceLoadPriorityUnresolved; +} + +static Resource* resourceFromDataURIRequest(const ResourceRequest& request) +{ + const KURL& url = request.url(); + ASSERT(url.protocolIsData()); + + WebKit::WebString mimetype; + WebKit::WebString charset; + RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(WebKit::Platform::current()->parseDataURL(url, mimetype, charset)); + if (!data) + return 0; + ResourceResponse response(url, mimetype, data->size(), charset, String()); + + Resource* resource = createResource(Resource::Image, request, charset); + resource->responseReceived(response); + // FIXME: AppendData causes an unnecessary memcpy. + if (data->size()) + resource->appendData(data->data(), data->size()); + resource->finish(); + return resource; +} + +ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader) + : m_document(0) + , m_documentLoader(documentLoader) + , m_requestCount(0) + , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired) + , m_autoLoadImages(true) + , m_imagesEnabled(true) + , m_allowStaleResources(false) +{ +} + +ResourceFetcher::~ResourceFetcher() +{ + m_documentLoader = 0; + m_document = 0; + + clearPreloads(); + + // Make sure no requests still point to this ResourceFetcher + ASSERT(!m_requestCount); +} + +Resource* ResourceFetcher::cachedResource(const String& resourceURL) const +{ + KURL url = m_document->completeURL(resourceURL); + return cachedResource(url); +} + +Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const +{ + KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); + return m_documentResources.get(url).get(); +} + +Frame* ResourceFetcher::frame() const +{ + if (m_documentLoader) + return m_documentLoader->frame(); + if (m_document && m_document->import()) + return m_document->import()->frame(); + return 0; +} + +ResourcePtr<ImageResource> ResourceFetcher::requestImage(FetchRequest& request) +{ + if (Frame* f = frame()) { + if (f->loader()->pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) { + KURL requestURL = request.resourceRequest().url(); + if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload())) + PingLoader::loadImage(f, requestURL); + return 0; + } + } + + if (request.resourceRequest().url().protocolIsData()) + preCacheDataURIImage(request); + + request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer); + return static_cast<ImageResource*>(requestResource(Resource::Image, request).get()); +} + +void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request) +{ + const KURL& url = request.resourceRequest().url(); + ASSERT(url.protocolIsData()); + + if (Resource* existing = memoryCache()->resourceForURL(url)) + return; + + if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest())) + memoryCache()->add(resource); +} + +ResourcePtr<FontResource> ResourceFetcher::requestFont(FetchRequest& request) +{ + return static_cast<FontResource*>(requestResource(Resource::Font, request).get()); +} + +ResourcePtr<TextTrackResource> ResourceFetcher::requestTextTrack(FetchRequest& request) +{ + return static_cast<TextTrackResource*>(requestResource(Resource::TextTrack, request).get()); +} + +ResourcePtr<ShaderResource> ResourceFetcher::requestShader(FetchRequest& request) +{ + return static_cast<ShaderResource*>(requestResource(Resource::Shader, request).get()); +} + +ResourcePtr<RawResource> ResourceFetcher::requestImport(FetchRequest& request) +{ + return static_cast<RawResource*>(requestResource(Resource::ImportResource, request).get()); +} + +ResourcePtr<CSSStyleSheetResource> ResourceFetcher::requestCSSStyleSheet(FetchRequest& request) +{ + return static_cast<CSSStyleSheetResource*>(requestResource(Resource::CSSStyleSheet, request).get()); +} + +ResourcePtr<CSSStyleSheetResource> ResourceFetcher::requestUserCSSStyleSheet(FetchRequest& request) +{ + KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url()); + + if (Resource* existing = memoryCache()->resourceForURL(url)) { + if (existing->type() == Resource::CSSStyleSheet) + return static_cast<CSSStyleSheetResource*>(existing); + memoryCache()->remove(existing); + } + + request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck, CheckContentSecurityPolicy, UseDefaultOriginRestrictionsForType, DocumentContext)); + return static_cast<CSSStyleSheetResource*>(requestResource(Resource::CSSStyleSheet, request).get()); +} + +ResourcePtr<ScriptResource> ResourceFetcher::requestScript(FetchRequest& request) +{ + return static_cast<ScriptResource*>(requestResource(Resource::Script, request).get()); +} + +ResourcePtr<XSLStyleSheetResource> ResourceFetcher::requestXSLStyleSheet(FetchRequest& request) +{ + return static_cast<XSLStyleSheetResource*>(requestResource(Resource::XSLStyleSheet, request).get()); +} + +ResourcePtr<DocumentResource> ResourceFetcher::requestSVGDocument(FetchRequest& request) +{ + return static_cast<DocumentResource*>(requestResource(Resource::SVGDocument, request).get()); +} + +ResourcePtr<Resource> ResourceFetcher::requestLinkResource(Resource::Type type, FetchRequest& request) +{ + ASSERT(frame()); + ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource); + return requestResource(type, request); +} + +ResourcePtr<RawResource> ResourceFetcher::requestRawResource(FetchRequest& request) +{ + return static_cast<RawResource*>(requestResource(Resource::Raw, request).get()); +} + +ResourcePtr<RawResource> ResourceFetcher::requestMainResource(FetchRequest& request) +{ + return static_cast<RawResource*>(requestResource(Resource::MainResource, request).get()); +} + +bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url) const +{ + switch (type) { + case Resource::Script: + case Resource::XSLStyleSheet: + case Resource::SVGDocument: + case Resource::CSSStyleSheet: + case Resource::ImportResource: + // 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)) + return false; + } + + break; + case Resource::TextTrack: + case Resource::Shader: + case Resource::Raw: + case Resource::Image: + case Resource::Font: { + // These resources can corrupt only the frame's pixels. + if (Frame* f = frame()) { + Frame* top = f->tree()->top(); + if (!top->loader()->mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url)) + return false; + } + break; + } + case Resource::MainResource: + case Resource::LinkPrefetch: + case Resource::LinkSubresource: + // Prefetch cannot affect the current document. + break; + } + return true; +} + +bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload) +{ + if (document() && !document()->securityOrigin()->canDisplay(url)) { + if (!forPreload) + FrameLoader::reportLocalLoadFailed(frame(), url.elidedString()); + LOG(ResourceLoading, "ResourceFetcher::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()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy); + + // 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 Resource::MainResource: + case Resource::Image: + case Resource::CSSStyleSheet: + case Resource::Script: + case Resource::Font: + case Resource::Raw: + case Resource::LinkPrefetch: + case Resource::LinkSubresource: + case Resource::TextTrack: + case Resource::Shader: + case Resource::ImportResource: + // By default these types of resources can be loaded from any origin. + // FIXME: Are we sure about Resource::Font? + if (options.requestOriginPolicy == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) { + printAccessDeniedMessage(url); + return false; + } + break; + case Resource::SVGDocument: + case Resource::XSLStyleSheet: + if (!m_document->securityOrigin()->canRequest(url)) { + printAccessDeniedMessage(url); + return false; + } + break; + } + + switch (type) { + case Resource::XSLStyleSheet: + if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) + return false; + break; + case Resource::Script: + case Resource::ImportResource: + if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) + return false; + + if (frame()) { + Settings* settings = frame()->settings(); + if (!frame()->loader()->client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) { + frame()->loader()->client()->didNotAllowScript(); + return false; + } + } + break; + case Resource::Shader: + // Since shaders are referenced from CSS Styles use the same rules here. + case Resource::CSSStyleSheet: + if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url)) + return false; + break; + case Resource::SVGDocument: + case Resource::Image: + if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url)) + return false; + break; + case Resource::Font: { + if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url)) + return false; + break; + } + case Resource::MainResource: + case Resource::Raw: + case Resource::LinkPrefetch: + case Resource::LinkSubresource: + break; + case Resource::TextTrack: + // Cues aren't called out in the CPS spec yet, but they only work with a media element + // so use the media policy. + if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url)) + return false; + break; + } + + // 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? + if (!checkInsecureContent(type, url)) + return false; + + return true; +} + +bool ResourceFetcher::canAccess(Resource* resource) +{ + // Redirects can change the response URL different from one of request. + if (!canRequest(resource->type(), resource->response().url(), resource->options(), false)) + return false; + + String error; + switch (resource->type()) { + case Resource::Script: + case Resource::ImportResource: + if (resource->options().requestOriginPolicy == PotentiallyCrossOriginEnabled + && !m_document->securityOrigin()->canRequest(resource->response().url()) + && !resource->passesAccessControlCheck(m_document->securityOrigin(), error)) { + if (frame() && frame()->document()) + frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Script from origin '" + SecurityOrigin::create(resource->response().url())->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + error); + return false; + } + + break; + default: + ASSERT_NOT_REACHED(); // FIXME: generalize to non-script resources + return false; + } + + return true; +} + +bool ResourceFetcher::shouldLoadNewResource() const +{ + if (!frame()) + return false; + if (m_documentLoader) { + if (m_documentLoader != frame()->loader()->activeDocumentLoader()) + return false; + if (m_documentLoader->isStopping()) + return false; + } + + return true; +} + +ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request) +{ + KURL url = request.resourceRequest().url(); + + LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.elidedString().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; + + if (!canRequest(type, url, request.options(), request.forPreload())) + return 0; + + if (Frame* f = frame()) + f->loader()->client()->dispatchWillRequestResource(&request); + + // See if we can use an existing resource from the cache. + ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url); + + const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); + switch (policy) { + case Reload: + memoryCache()->remove(resource.get()); + // Fall through + case Load: + resource = loadResource(type, request, request.charset()); + break; + case Revalidate: + resource = revalidateResource(request, resource.get()); + break; + case Use: + resource->updateForAccess(); + notifyLoadedFromMemoryCache(resource.get()); + break; + } + + if (!resource) + return 0; + + if (policy != Use) + resource->setIdentifier(createUniqueIdentifier()); + + if (!request.forPreload() || policy != Use) { + ResourceLoadPriority priority = loadPriority(type, request); + if (priority != resource->resourceRequest().priority()) { + resource->resourceRequest().setPriority(priority); + resource->didChangePriority(priority); + } + } + + if ((policy != Use || resource->stillNeedsLoad()) && FetchRequest::NoDefer == request.defer()) { + if (!shouldLoadNewResource()) { + if (resource->inCache()) + memoryCache()->remove(resource.get()); + return 0; + } + + if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest())) + resource->load(this, request.options()); + + // We don't support immediate loads, but we do support immediate failure. + if (resource->errorOccurred()) { + if (resource->inCache()) + memoryCache()->remove(resource.get()); + return 0; + } + } + + // FIXME: Temporarily leave main resource caching disabled for chromium, + // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main + // resources, we should be sure to understand the implications for memory + // use. + // + // Ensure main resources aren't preloaded, and other main resource loads + // are removed from cache to prevent reuse. + if (type == Resource::MainResource) { + ASSERT(policy != Use); + ASSERT(policy != Revalidate); + memoryCache()->remove(resource.get()); + if (request.forPreload()) + return 0; + } + + if (!request.resourceRequest().url().protocolIsData()) + m_validatedURLs.add(request.resourceRequest().url()); + + ASSERT(resource->url() == url.string()); + m_documentResources.set(resource->url(), resource); + return resource; +} + +void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type) +{ + ResourceRequest::TargetType targetType; + + switch (type) { + case Resource::MainResource: + if (frame()->tree()->parent()) + targetType = ResourceRequest::TargetIsSubframe; + else + targetType = ResourceRequest::TargetIsMainFrame; + break; + case Resource::CSSStyleSheet: + case Resource::XSLStyleSheet: + targetType = ResourceRequest::TargetIsStyleSheet; + break; + case Resource::Script: + targetType = ResourceRequest::TargetIsScript; + break; + case Resource::Font: + targetType = ResourceRequest::TargetIsFont; + break; + case Resource::Image: + targetType = ResourceRequest::TargetIsImage; + break; + case Resource::Shader: + case Resource::Raw: + case Resource::ImportResource: + targetType = ResourceRequest::TargetIsSubresource; + break; + case Resource::LinkPrefetch: + targetType = ResourceRequest::TargetIsPrefetch; + break; + case Resource::LinkSubresource: + targetType = ResourceRequest::TargetIsSubresource; + break; + case Resource::TextTrack: + targetType = ResourceRequest::TargetIsTextTrack; + break; + case Resource::SVGDocument: + targetType = ResourceRequest::TargetIsImage; + break; + default: + ASSERT_NOT_REACHED(); + targetType = ResourceRequest::TargetIsSubresource; + break; + } + request.setTargetType(targetType); +} + +ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type) +{ + if (type == Resource::MainResource) { + FrameLoadType frameLoadType = frame()->loader()->loadType(); + bool isReload = frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin; + if (request.httpMethod() == "POST" && (isReload || frameLoadType == FrameLoadTypeBackForward)) + return ReturnCacheDataDontLoad; + if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward) + return ReturnCacheDataElseLoad; + if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional()) + return ReloadIgnoringCacheData; + return UseProtocolCachePolicy; + } + + if (request.isConditional()) + return ReloadIgnoringCacheData; + + if (m_documentLoader && m_documentLoader->isLoadingInAPISense()) { + // For POST requests, we mutate the main resource's cache policy to avoid form resubmission. + // This policy should not be inherited by subresources. + ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy(); + if (mainResourceCachePolicy == ReturnCacheDataDontLoad) + return ReturnCacheDataElseLoad; + return mainResourceCachePolicy; + } + return UseProtocolCachePolicy; +} + +void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type) +{ + if (!frame()) + return; + + bool isMainResource = type == Resource::MainResource; + + FrameLoader* frameLoader = frame()->loader(); + + if (!isMainResource) { + String outgoingReferrer; + String outgoingOrigin; + if (request.httpReferrer().isNull()) { + outgoingReferrer = frameLoader->outgoingReferrer(); + outgoingOrigin = frameLoader->outgoingOrigin(); + } else { + outgoingReferrer = request.httpReferrer(); + outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString(); + } + + outgoingReferrer = SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), request.url(), outgoingReferrer); + if (outgoingReferrer.isEmpty()) + request.clearHTTPReferrer(); + else if (!request.httpReferrer()) + request.setHTTPReferrer(outgoingReferrer); + + FrameLoader::addHTTPOriginIfNeeded(request, outgoingOrigin); + } + + if (request.cachePolicy() == UseProtocolCachePolicy) + request.setCachePolicy(resourceRequestCachePolicy(request, type)); + if (request.targetType() == ResourceRequest::TargetIsUnspecified) + determineTargetType(request, type); + if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource) + request.setHTTPHeaderField("Purpose", "prefetch"); + frameLoader->addExtraFieldsToRequest(request); +} + +ResourcePtr<Resource> ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource) +{ + ASSERT(resource); + ASSERT(resource->inCache()); + ASSERT(resource->isLoaded()); + ASSERT(resource->canUseCacheValidator()); + ASSERT(!resource->resourceToRevalidate()); + + ResourceRequest revalidatingRequest(resource->resourceRequest()); + addAdditionalRequestHeaders(revalidatingRequest, resource->type()); + + const String& lastModified = resource->response().httpHeaderField("Last-Modified"); + const String& eTag = resource->response().httpHeaderField("ETag"); + if (!lastModified.isEmpty() || !eTag.isEmpty()) { + ASSERT(cachePolicy(resource->type()) != CachePolicyReload); + if (cachePolicy(resource->type()) == CachePolicyRevalidate) + revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); + if (!lastModified.isEmpty()) + revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified); + if (!eTag.isEmpty()) + revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag); + } + + ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding()); + + LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); + newResource->setResourceToRevalidate(resource); + + memoryCache()->remove(resource); + memoryCache()->add(newResource.get()); + storeResourceTimingInitiatorInformation(newResource, request); + TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", newResource.get(), "url", newResource->url().string().ascii(), "priority", newResource->resourceRequest().priority()); + return newResource; +} + +ResourcePtr<Resource> ResourceFetcher::loadResource(Resource::Type type, FetchRequest& request, const String& charset) +{ + ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url())); + + LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data()); + + addAdditionalRequestHeaders(request.mutableResourceRequest(), type); + ResourcePtr<Resource> resource = createResource(type, request.mutableResourceRequest(), charset); + + memoryCache()->add(resource.get()); + storeResourceTimingInitiatorInformation(resource, request); + TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource.get(), "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority()); + return resource; +} + +void ResourceFetcher::storeResourceTimingInitiatorInformation(const ResourcePtr<Resource>& resource, const FetchRequest& request) +{ + if (request.options().requestInitiatorContext != DocumentContext) + return; + + RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime()); + + if (resource->type() == Resource::MainResource) { + // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. + if (frame()->ownerElement() && !frame()->ownerElement()->loadedNonEmptyDocument()) { + info->setInitiatorType(frame()->ownerElement()->localName()); + m_resourceTimingInfoMap.add(resource.get(), info); + frame()->ownerElement()->didLoadNonEmptyDocument(); + } + } else { + m_resourceTimingInfoMap.add(resource.get(), info); + } +} + +ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer) const +{ + if (!existingResource) + return Load; + + // We already have a preload going for this URL. + if (forPreload && 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, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch."); + return Reload; + } + + // Do not load from cache if images are not enabled. The load for this image will be blocked + // in ImageResource::load. + if (FetchRequest::DeferredByClient == defer) + return Reload; + + // Always use data uris. + // FIXME: Extend this to non-images. + if (type == Resource::Image && request.url().protocolIsData()) + return Use; + + if (!existingResource->canReuse(request)) + return Reload; + + // Certain requests (e.g., XHRs) might have manually set headers that require revalidation. + // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch + // of things about how revalidation works that manual headers violate, so punt to Reload instead. + if (request.isConditional()) + return Reload; + + // Don't reload resources while pasting. + if (m_allowStaleResources) + return Use; + + // Alwaus use preloads. + if (existingResource->isPreloaded()) + return Use; + + // CachePolicyHistoryBuffer uses the cache no matter what. + if (cachePolicy(type) == CachePolicyHistoryBuffer) + return Use; + + // Don't reuse resources with Cache-control: no-store. + if (existingResource->response().cacheControlContainsNoStore()) { + LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store."); + return Reload; + } + + // If credentials were sent with the previous request and won't be + // with this one, or vice versa, re-fetch the resource. + // + // This helps with the case where the server sends back + // "Access-Control-Allow-Origin: *" all the time, but some of the + // client's requests are made without CORS and some with. + if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) { + LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings."); + return Reload; + } + + // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to. + if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url())) + return Use; + + // CachePolicyReload always reloads + if (cachePolicy(type) == CachePolicyReload) { + LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload."); + return Reload; + } + + // We'll try to reload the resource if it failed last time. + if (existingResource->errorOccurred()) { + LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state"); + return Reload; + } + + // For resources that are not yet loaded we ignore the cache policy. + if (existingResource->isLoading()) + return Use; + + // Check if the cache headers requires us to revalidate (cache expiration for example). + if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy(type))) { + // See if the resource has usable ETag or Last-modified headers. + if (existingResource->canUseCacheValidator()) + return Revalidate; + + // No, must reload. + LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators."); + return Reload; + } + + return Use; +} + +void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const +{ + if (url.isNull()) + return; + + if (!frame()) + return; + + String message; + if (!m_document || m_document->url().isNull()) + message = "Unsafe attempt to load URL " + url.elidedString() + '.'; + else + message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n"; + + frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); +} + +void ResourceFetcher::setAutoLoadImages(bool enable) +{ + if (enable == m_autoLoadImages) + return; + + m_autoLoadImages = enable; + + if (!m_autoLoadImages) + return; + + reloadImagesIfNotDeferred(); +} + +void ResourceFetcher::setImagesEnabled(bool enable) +{ + if (enable == m_imagesEnabled) + return; + + m_imagesEnabled = enable; + + if (!m_imagesEnabled) + return; + + reloadImagesIfNotDeferred(); +} + +bool ResourceFetcher::clientDefersImage(const KURL& url) const +{ + return frame() && !frame()->loader()->client()->allowImage(m_imagesEnabled, url); +} + +bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const +{ + return clientDefersImage(url) || !m_autoLoadImages; +} + +void ResourceFetcher::reloadImagesIfNotDeferred() +{ + DocumentResourceMap::iterator end = m_documentResources.end(); + for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { + Resource* resource = it->value.get(); + if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) + const_cast<Resource*>(resource)->load(this, defaultResourceOptions()); + } +} + +CachePolicy ResourceFetcher::cachePolicy(Resource::Type type) const +{ + if (!frame()) + return CachePolicyVerify; + + if (type != Resource::MainResource) + return frame()->loader()->subresourceCachePolicy(); + + if (frame()->loader()->loadType() == FrameLoadTypeReloadFromOrigin || frame()->loader()->loadType() == FrameLoadTypeReload) + return CachePolicyReload; + return CachePolicyVerify; +} + +void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse) +{ + ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); + if (it != m_resourceTimingInfoMap.end()) + it->value->addRedirect(redirectResponse); +} + +void ResourceFetcher::didLoadResource(Resource* resource) +{ + RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader); + RefPtr<Document> protectDocument(m_document); + + if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304) && document()) { + ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); + if (it != m_resourceTimingInfoMap.end()) { + Document* initiatorDocument = document(); + if (resource->type() == Resource::MainResource) + initiatorDocument = document()->parentDocument(); + ASSERT(initiatorDocument); + RefPtr<ResourceTimingInfo> info = it->value; + info->setInitialRequest(resource->resourceRequest()); + info->setFinalResponse(resource->response()); + info->setLoadFinishTime(resource->loadFinishTime()); + if (DOMWindow* initiatorWindow = initiatorDocument->domWindow()) + initiatorWindow->performance()->addResourceTiming(*info, initiatorDocument); + m_resourceTimingInfoMap.remove(it); + } + } + + if (frame()) + frame()->loader()->loadDone(); + performPostLoadActions(); + + if (!m_garbageCollectDocumentResourcesTimer.isActive()) + m_garbageCollectDocumentResourcesTimer.startOneShot(0); +} + +// Garbage collecting m_documentResources is a workaround for the +// ResourcePtrs on the RHS being strong references. Ideally this +// would be a weak map, however ResourcePtrs perform additional +// bookkeeping on Resources, so instead pseudo-GC them -- when the +// reference count reaches 1, m_documentResources is the only reference, so +// remove it from the map. +void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer); + garbageCollectDocumentResources(); +} + +void ResourceFetcher::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); + } + + for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it) + m_documentResources.remove(*it); +} + +void ResourceFetcher::performPostLoadActions() +{ + checkForPendingPreloads(); +} + +void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource) +{ + if (!frame() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url())) + return; + + // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load. + frame()->loader()->loadedResourceFromMemoryCache(resource); +} + +void ResourceFetcher::incrementRequestCount(const Resource* res) +{ + if (res->ignoreForRequestCount()) + return; + + ++m_requestCount; +} + +void ResourceFetcher::decrementRequestCount(const Resource* res) +{ + if (res->ignoreForRequestCount()) + return; + + --m_requestCount; + ASSERT(m_requestCount > -1); +} + +void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset) +{ + bool delaySubresourceLoad = true; + delaySubresourceLoad = false; + if (delaySubresourceLoad) { + bool hasRendering = m_document->body() && m_document->body()->renderer(); + bool canBlockParser = type == Resource::Script || type == Resource::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; + } + } + requestPreload(type, request, charset); +} + +void ResourceFetcher::checkForPendingPreloads() +{ + if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer()) + return; + 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(); +} + +void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset) +{ + String encoding; + if (type == Resource::Script || type == Resource::CSSStyleSheet) + encoding = charset.isEmpty() ? m_document->charset() : charset; + + request.setCharset(encoding); + request.setForPreload(true); + + ResourcePtr<Resource> resource = requestResource(type, request); + if (!resource || (m_preloads && m_preloads->contains(resource.get()))) + return; + TRACE_EVENT_ASYNC_STEP0("net", "Resource", resource.get(), "Preload"); + resource->increasePreloadCount(); + + if (!m_preloads) + m_preloads = adoptPtr(new ListHashSet<Resource*>); + m_preloads->add(resource.get()); + +#if PRELOAD_DEBUG + printf("PRELOADING %s\n", resource->url().latin1().data()); +#endif +} + +bool ResourceFetcher::isPreloaded(const String& urlString) const +{ + const KURL& url = m_document->completeURL(urlString); + + if (m_preloads) { + ListHashSet<Resource*>::iterator end = m_preloads->end(); + for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { + Resource* resource = *it; + 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 ResourceFetcher::clearPreloads() +{ +#if PRELOAD_DEBUG + printPreloadStats(); +#endif + if (!m_preloads) + return; + + ListHashSet<Resource*>::iterator end = m_preloads->end(); + for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { + Resource* res = *it; + res->decreasePreloadCount(); + bool deleted = res->deleteIfPossible(); + if (!deleted && res->preloadResult() == Resource::PreloadNotReferenced) + memoryCache()->remove(res); + } + m_preloads.clear(); +} + +void ResourceFetcher::clearPendingPreloads() +{ + m_pendingPreloads.clear(); +} + +inline FrameLoader* ResourceFetcher::frameLoader() +{ + if (Frame* frame = this->frame()) + return frame->loader(); + return 0; +} + +void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, const ResourceLoaderOptions& options) +{ + TRACE_EVENT_ASYNC_END0("net", "Resource", resource); + if (options.sendLoadCallbacks != SendCallbacks) + return; + if (FrameLoader* loader = frameLoader()) + loader->notifier()->dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime); +} + +void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority) +{ + TRACE_EVENT_ASYNC_STEP1("net", "Resource", resource, "ChangePriority", "priority", loadPriority); + if (FrameLoader* loader = frameLoader()) + loader->client()->dispatchDidChangeResourcePriority(resource->identifier(), loadPriority); +} + +void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error, const ResourceLoaderOptions& options) +{ + TRACE_EVENT_ASYNC_END0("net", "Resource", resource); + if (options.sendLoadCallbacks != SendCallbacks) + return; + if (FrameLoader* loader = frameLoader()) + loader->notifier()->dispatchDidFail(m_documentLoader, resource->identifier(), error); +} + +void ResourceFetcher::willSendRequest(const Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse, const ResourceLoaderOptions& options) +{ + if (options.sendLoadCallbacks == SendCallbacks) { + if (FrameLoader* loader = frameLoader()) + loader->notifier()->dispatchWillSendRequest(m_documentLoader, resource->identifier(), request, redirectResponse, options.initiatorInfo); + } else { + InspectorInstrumentation::willSendRequest(frame(), resource->identifier(), m_documentLoader, request, redirectResponse, options.initiatorInfo); + } +} + +void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response, const ResourceLoaderOptions& options) +{ + if (options.sendLoadCallbacks != SendCallbacks) + return; + if (FrameLoader* loader = frameLoader()) + loader->notifier()->dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response); +} + +void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options) +{ + // FIXME: use frame of master document for imported documents. + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength); + if (options.sendLoadCallbacks != SendCallbacks) + return; + if (FrameLoader* loader = frameLoader()) + loader->notifier()->dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength); + InspectorInstrumentation::didReceiveResourceData(cookie); +} + +void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader) +{ + if (m_documentLoader) + m_documentLoader->subresourceLoaderFinishedLoadingOnePart(loader); +} + +void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader) +{ + if (m_documentLoader) + m_documentLoader->addResourceLoader(loader); +} + +void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader) +{ + if (m_documentLoader) + m_documentLoader->removeResourceLoader(loader); +} + +void ResourceFetcher::willStartLoadingResource(ResourceRequest& request) +{ + if (m_documentLoader) + m_documentLoader->applicationCacheHost()->willStartLoadingResource(request); +} + +bool ResourceFetcher::defersLoading() const +{ + if (Frame* frame = this->frame()) + return frame->page()->defersLoading(); + return false; +} + +bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const +{ + return this == possibleOwner; +} + +bool ResourceFetcher::shouldRequest(Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) +{ + if (!canRequest(resource->type(), request.url(), options)) + return false; + if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url())) + return false; + return true; +} + +void ResourceFetcher::refResourceLoaderHost() +{ + ref(); +} + +void ResourceFetcher::derefResourceLoaderHost() +{ + deref(); +} + +#if PRELOAD_DEBUG +void ResourceFetcher::printPreloadStats() +{ + unsigned scripts = 0; + unsigned scriptMisses = 0; + unsigned stylesheets = 0; + unsigned stylesheetMisses = 0; + unsigned images = 0; + unsigned imageMisses = 0; + ListHashSet<Resource*>::iterator end = m_preloads.end(); + for (ListHashSet<Resource*>::iterator it = m_preloads.begin(); it != end; ++it) { + Resource* res = *it; + if (res->preloadResult() == Resource::PreloadNotReferenced) + printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data()); + else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete) + printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data()); + else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading) + printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data()); + + if (res->type() == Resource::Script) { + scripts++; + if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) + scriptMisses++; + } else if (res->type() == Resource::CSSStyleSheet) { + stylesheets++; + if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) + stylesheetMisses++; + } else { + images++; + if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) + imageMisses++; + } + + if (res->errorOccurred()) + memoryCache()->remove(res); + + res->decreasePreloadCount(); + } + m_preloads.clear(); + + if (scripts) + printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts); + if (stylesheets) + printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets); + if (images) + printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images); +} +#endif + +const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions() +{ + DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck, CheckContentSecurityPolicy, UseDefaultOriginRestrictionsForType, DocumentContext)); + return options; +} + +} diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcher.h b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcher.h new file mode 100644 index 00000000000..c2ffa7ade12 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcher.h @@ -0,0 +1,236 @@ +/* + Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) + Copyright (C) 2001 Dirk Mueller <mueller@kde.org> + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 + 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. + + 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 ResourceFetcher_h +#define ResourceFetcher_h + +#include "core/loader/ResourceLoaderHost.h" +#include "core/loader/cache/CachePolicy.h" +#include "core/loader/cache/FetchInitiatorInfo.h" +#include "core/loader/cache/FetchRequest.h" +#include "core/loader/cache/Resource.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/platform/Timer.h" +#include "wtf/Deque.h" +#include "wtf/HashMap.h" +#include "wtf/HashSet.h" +#include "wtf/ListHashSet.h" +#include "wtf/text/StringHash.h" + +namespace WebCore { + +class CSSStyleSheetResource; +class DocumentResource; +class FontResource; +class ImageResource; +class RawResource; +class ScriptResource; +class ShaderResource; +class TextTrackResource; +class XSLStyleSheetResource; +class Document; +class DocumentLoader; +class Frame; +class FrameLoader; +class ImageLoader; +class KURL; +class ResourceTimingInfo; + +// The ResourceFetcher provides a per-context interface to the MemoryCache +// and enforces a bunch of security checks and rules for resource revalidation. +// Its lifetime is roughly per-DocumentLoader, in that it is generally created +// in the DocumentLoader constructor and loses its ability to generate network +// requests when the DocumentLoader is destroyed. Documents also hold a +// RefPtr<ResourceFetcher> for their lifetime (and will create one if they +// are initialized without a Frame), so a Document can keep a ResourceFetcher +// alive past detach if scripts still reference the Document. +class ResourceFetcher : public RefCounted<ResourceFetcher>, public ResourceLoaderHost { + WTF_MAKE_NONCOPYABLE(ResourceFetcher); WTF_MAKE_FAST_ALLOCATED; +friend class ImageLoader; +friend class ResourceCacheValidationSuppressor; + +public: + static PassRefPtr<ResourceFetcher> create(DocumentLoader* documentLoader) { return adoptRef(new ResourceFetcher(documentLoader)); } + virtual ~ResourceFetcher(); + + using RefCounted<ResourceFetcher>::ref; + using RefCounted<ResourceFetcher>::deref; + + ResourcePtr<ImageResource> requestImage(FetchRequest&); + ResourcePtr<CSSStyleSheetResource> requestCSSStyleSheet(FetchRequest&); + ResourcePtr<CSSStyleSheetResource> requestUserCSSStyleSheet(FetchRequest&); + ResourcePtr<ScriptResource> requestScript(FetchRequest&); + ResourcePtr<FontResource> requestFont(FetchRequest&); + ResourcePtr<RawResource> requestRawResource(FetchRequest&); + ResourcePtr<RawResource> requestMainResource(FetchRequest&); + ResourcePtr<DocumentResource> requestSVGDocument(FetchRequest&); + ResourcePtr<XSLStyleSheetResource> requestXSLStyleSheet(FetchRequest&); + ResourcePtr<Resource> requestLinkResource(Resource::Type, FetchRequest&); + ResourcePtr<TextTrackResource> requestTextTrack(FetchRequest&); + ResourcePtr<ShaderResource> requestShader(FetchRequest&); + ResourcePtr<RawResource> requestImport(FetchRequest&); + + // Logs an access denied message to the console for the specified URL. + void printAccessDeniedMessage(const KURL&) const; + + Resource* cachedResource(const String& url) const; + Resource* cachedResource(const KURL&) const; + + typedef HashMap<String, ResourcePtr<Resource> > DocumentResourceMap; + const DocumentResourceMap& allResources() const { return m_documentResources; } + + bool autoLoadImages() const { return m_autoLoadImages; } + void setAutoLoadImages(bool); + + void setImagesEnabled(bool); + + bool shouldDeferImageLoad(const KURL&) const; + + CachePolicy cachePolicy(Resource::Type) const; + + Frame* frame() const; // Can be null + Document* document() const { return m_document; } // Can be null + void setDocument(Document* document) { m_document = document; } + + DocumentLoader* documentLoader() const { return m_documentLoader; } + void clearDocumentLoader() { m_documentLoader = 0; } + + void garbageCollectDocumentResources(); + + int requestCount() const { return m_requestCount; } + + bool isPreloaded(const String& urlString) const; + void clearPreloads(); + void clearPendingPreloads(); + void preload(Resource::Type, FetchRequest&, const String& charset); + void checkForPendingPreloads(); + void printPreloadStats(); + bool canRequest(Resource::Type, const KURL&, const ResourceLoaderOptions&, bool forPreload = false); + bool canAccess(Resource*); + + // ResourceLoaderHost + virtual void incrementRequestCount(const Resource*) OVERRIDE; + virtual void decrementRequestCount(const Resource*) OVERRIDE; + virtual void didLoadResource(Resource*) OVERRIDE; + virtual void redirectReceived(Resource*, const ResourceResponse&) OVERRIDE; + virtual void didFinishLoading(const Resource*, double finishTime, const ResourceLoaderOptions&) OVERRIDE; + virtual void didChangeLoadingPriority(const Resource*, ResourceLoadPriority) OVERRIDE; + virtual void didFailLoading(const Resource*, const ResourceError&, const ResourceLoaderOptions&) OVERRIDE; + virtual void willSendRequest(const Resource*, ResourceRequest&, const ResourceResponse& redirectResponse, const ResourceLoaderOptions&) OVERRIDE; + virtual void didReceiveResponse(const Resource*, const ResourceResponse&, const ResourceLoaderOptions&) OVERRIDE; + virtual void didReceiveData(const Resource*, const char* data, int dataLength, int encodedDataLength, const ResourceLoaderOptions&) OVERRIDE; + virtual void subresourceLoaderFinishedLoadingOnePart(ResourceLoader*) OVERRIDE; + virtual void didInitializeResourceLoader(ResourceLoader*) OVERRIDE; + virtual void willTerminateResourceLoader(ResourceLoader*) OVERRIDE; + virtual void willStartLoadingResource(ResourceRequest&) OVERRIDE; + virtual bool defersLoading() const OVERRIDE; + virtual bool isLoadedBy(ResourceLoaderHost*) const OVERRIDE; + virtual bool shouldRequest(Resource*, const ResourceRequest&, const ResourceLoaderOptions&) OVERRIDE; + virtual void refResourceLoaderHost() OVERRIDE; + virtual void derefResourceLoaderHost() OVERRIDE; + + static const ResourceLoaderOptions& defaultResourceOptions(); +private: + + explicit ResourceFetcher(DocumentLoader*); + + FrameLoader* frameLoader(); + bool shouldLoadNewResource() const; + + ResourcePtr<Resource> requestResource(Resource::Type, FetchRequest&); + ResourcePtr<Resource> revalidateResource(const FetchRequest&, Resource*); + ResourcePtr<Resource> loadResource(Resource::Type, FetchRequest&, const String& charset); + void preCacheDataURIImage(const FetchRequest&); + void storeResourceTimingInitiatorInformation(const ResourcePtr<Resource>&, const FetchRequest&); + void requestPreload(Resource::Type, FetchRequest&, const String& charset); + + enum RevalidationPolicy { Use, Revalidate, Reload, Load }; + RevalidationPolicy determineRevalidationPolicy(Resource::Type, ResourceRequest&, bool forPreload, Resource* existingResource, FetchRequest::DeferOption) const; + + void determineTargetType(ResourceRequest&, Resource::Type); + ResourceRequestCachePolicy resourceRequestCachePolicy(const ResourceRequest&, Resource::Type); + void addAdditionalRequestHeaders(ResourceRequest&, Resource::Type); + + void notifyLoadedFromMemoryCache(Resource*); + bool checkInsecureContent(Resource::Type, const KURL&) const; + + void garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>*); + void performPostLoadActions(); + + bool clientDefersImage(const KURL&) const; + void reloadImagesIfNotDeferred(); + + HashSet<String> m_validatedURLs; + mutable DocumentResourceMap m_documentResources; + Document* m_document; + DocumentLoader* m_documentLoader; + + int m_requestCount; + + OwnPtr<ListHashSet<Resource*> > m_preloads; + struct PendingPreload { + Resource::Type m_type; + FetchRequest m_request; + String m_charset; + }; + Deque<PendingPreload> m_pendingPreloads; + + Timer<ResourceFetcher> m_garbageCollectDocumentResourcesTimer; + + typedef HashMap<Resource*, RefPtr<ResourceTimingInfo> > ResourceTimingInfoMap; + ResourceTimingInfoMap m_resourceTimingInfoMap; + + // 29 bits left + bool m_autoLoadImages : 1; + bool m_imagesEnabled : 1; + bool m_allowStaleResources : 1; +}; + +class ResourceCacheValidationSuppressor { + WTF_MAKE_NONCOPYABLE(ResourceCacheValidationSuppressor); + WTF_MAKE_FAST_ALLOCATED; +public: + ResourceCacheValidationSuppressor(ResourceFetcher* loader) + : m_loader(loader) + , m_previousState(false) + { + if (m_loader) { + m_previousState = m_loader->m_allowStaleResources; + m_loader->m_allowStaleResources = true; + } + } + ~ResourceCacheValidationSuppressor() + { + if (m_loader) + m_loader->m_allowStaleResources = m_previousState; + } +private: + ResourceFetcher* m_loader; + bool m_previousState; +}; + +} // namespace WebCore + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcherTest.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcherTest.cpp new file mode 100644 index 00000000000..977f519ee80 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourceFetcherTest.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, 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: + * + * * 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 Google 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 "core/loader/cache/ResourceFetcher.h" + +#include "core/html/HTMLDocument.h" +#include "core/loader/DocumentLoader.h" +#include "core/loader/cache/FetchInitiatorInfo.h" +#include "core/loader/cache/FetchRequest.h" +#include "core/loader/cache/MemoryCache.h" +#include "core/loader/cache/ResourcePtr.h" +#include "core/platform/network/ResourceRequest.h" +#include <gtest/gtest.h> + +using namespace WebCore; + +namespace { + +TEST(ResourceFetcherTest, StartLoadAfterFrameDetach) +{ + KURL testURL(ParsedURLString, "http://www.test.com/cancelTest.jpg"); + + // Create a ResourceFetcher that has a real DocumentLoader and Document, but is not attached to a Frame. + // Technically, we're concerned about what happens after a Frame is detached (rather than before + // any attach occurs), but ResourceFetcher can't tell the difference. + RefPtr<DocumentLoader> documentLoader = DocumentLoader::create(ResourceRequest(testURL), SubstituteData()); + RefPtr<HTMLDocument> document = HTMLDocument::create(); + RefPtr<ResourceFetcher> fetcher(documentLoader->fetcher()); + fetcher->setDocument(document.get()); + EXPECT_EQ(fetcher->frame(), static_cast<Frame*>(0)); + + // Try to request a url. The request should fail, no resource should be returned, + // and no resource should be present in the cache. + FetchRequest fetchRequest = FetchRequest(ResourceRequest(testURL), FetchInitiatorInfo()); + ResourcePtr<ImageResource> image = fetcher->requestImage(fetchRequest); + EXPECT_EQ(image.get(), static_cast<ImageResource*>(0)); + EXPECT_EQ(memoryCache()->resourceForURL(testURL), static_cast<Resource*>(0)); +} + +} // namespace diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourcePtr.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ResourcePtr.cpp new file mode 100644 index 00000000000..bbb43520d88 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourcePtr.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 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. + */ + +#include "config.h" +#include "core/loader/cache/ResourcePtr.h" + +namespace WebCore { + +void ResourcePtrBase::setResource(Resource* resource) +{ + if (resource == m_resource) + return; + if (m_resource) + m_resource->unregisterHandle(this); + m_resource = resource; + if (m_resource) + m_resource->registerHandle(this); +} + +} diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ResourcePtr.h b/chromium/third_party/WebKit/Source/core/loader/cache/ResourcePtr.h new file mode 100644 index 00000000000..dda4d2d748b --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ResourcePtr.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 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. + */ + +#ifndef ResourcePtr_h +#define ResourcePtr_h + +#include "core/loader/cache/Resource.h" + +namespace WebCore { + +class ResourcePtrBase { +public: + ~ResourcePtrBase(); + + Resource* get() const { return m_resource; } + bool operator!() const { return !m_resource; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef Resource* ResourcePtrBase::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_resource ? &ResourcePtrBase::m_resource : 0; } + +protected: + ResourcePtrBase() : m_resource(0) { } + ResourcePtrBase(Resource*); + ResourcePtrBase(const ResourcePtrBase&); + + void setResource(Resource*); + +private: + ResourcePtrBase& operator=(const ResourcePtrBase&) { return *this; } + + friend class Resource; + + Resource* m_resource; +}; + +inline ResourcePtrBase::ResourcePtrBase(Resource* res) + : m_resource(res) +{ + if (m_resource) + m_resource->registerHandle(this); +} + +inline ResourcePtrBase::~ResourcePtrBase() +{ + if (m_resource) + m_resource->unregisterHandle(this); +} + +inline ResourcePtrBase::ResourcePtrBase(const ResourcePtrBase& o) + : m_resource(o.m_resource) +{ + if (m_resource) + m_resource->registerHandle(this); +} + +template <class R> class ResourcePtr : public ResourcePtrBase { +public: + ResourcePtr() { } + ResourcePtr(R* res) : ResourcePtrBase(res) { } + ResourcePtr(const ResourcePtr<R>& o) : ResourcePtrBase(o) { } + template<typename U> ResourcePtr(const ResourcePtr<U>& o) : ResourcePtrBase(o.get()) { } + + R* get() const { return reinterpret_cast<R*>(ResourcePtrBase::get()); } + R* operator->() const { return get(); } + + ResourcePtr& operator=(R* res) { setResource(res); return *this; } + ResourcePtr& operator=(const ResourcePtr& o) { setResource(o.get()); return *this; } + template<typename U> ResourcePtr& operator=(const ResourcePtr<U>& o) { setResource(o.get()); return *this; } + + bool operator==(const ResourcePtrBase& o) const { return get() == o.get(); } + bool operator!=(const ResourcePtrBase& o) const { return get() != o.get(); } +}; + +template <class R, class RR> bool operator==(const ResourcePtr<R>& h, const RR* res) +{ + return h.get() == res; +} +template <class R, class RR> bool operator==(const RR* res, const ResourcePtr<R>& h) +{ + return h.get() == res; +} +template <class R, class RR> bool operator!=(const ResourcePtr<R>& h, const RR* res) +{ + return h.get() != res; +} +template <class R, class RR> bool operator!=(const RR* res, const ResourcePtr<R>& h) +{ + return h.get() != res; +} +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ScriptResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ScriptResource.cpp new file mode 100644 index 00000000000..b6e9189bc26 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ScriptResource.cpp @@ -0,0 +1,92 @@ +/* + 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. + + 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. + + 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" +#include "core/loader/cache/ScriptResource.h" + +#include "core/loader/TextResourceDecoder.h" +#include "core/platform/MIMETypeRegistry.h" +#include "core/platform/SharedBuffer.h" +#include "core/platform/network/HTTPParsers.h" + +namespace WebCore { + +ScriptResource::ScriptResource(const ResourceRequest& resourceRequest, const String& charset) + : Resource(resourceRequest, Script) + , m_decoder(TextResourceDecoder::create("application/javascript", charset)) +{ + DEFINE_STATIC_LOCAL(const AtomicString, acceptScript, ("*/*", AtomicString::ConstructFromLiteral)); + + // 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(acceptScript); +} + +ScriptResource::~ScriptResource() +{ +} + +void ScriptResource::setEncoding(const String& chs) +{ + m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader); +} + +String ScriptResource::encoding() const +{ + return m_decoder->encoding().name(); +} + +String ScriptResource::mimeType() const +{ + return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")).lower(); +} + +const String& ScriptResource::script() +{ + ASSERT(!isPurgeable()); + ASSERT(isLoaded()); + + if (!m_script && m_data) { + String script = m_decoder->decode(m_data->data(), encodedSize()); + script.append(m_decoder->flush()); + m_data.clear(); + // We lie a it here and claim that script counts as encoded data (even though it's really decoded data). + // That's because the MemoryCache thinks that it can clear out decoded data by calling destroyDecodedData(), + // but we can't destroy script in destroyDecodedData because that's our only copy of the data! + setEncodedSize(script.sizeInBytes()); + m_script = script; + } + + return m_script.string(); +} + +bool ScriptResource::mimeTypeAllowedByNosniff() const +{ + return parseContentTypeOptionsHeader(m_response.httpHeaderField("X-Content-Type-Options")) != ContentTypeOptionsNosniff || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType()); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ScriptResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/ScriptResource.h new file mode 100644 index 00000000000..02d90ca986b --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ScriptResource.h @@ -0,0 +1,55 @@ +/* + 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. + + 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. + + 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 ScriptResource_h +#define ScriptResource_h + +#include "core/loader/cache/Resource.h" + +namespace WebCore { + +class ResourceFetcher; +class TextResourceDecoder; + +class ScriptResource : public Resource { +public: + ScriptResource(const ResourceRequest&, const String& charset); + virtual ~ScriptResource(); + + const String& script(); + + virtual void setEncoding(const String&); + virtual String encoding() const; + String mimeType() const; + + bool mimeTypeAllowedByNosniff() const; + +private: + AtomicString m_script; + RefPtr<TextResourceDecoder> m_decoder; +}; +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ShaderResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/ShaderResource.cpp new file mode 100644 index 00000000000..ec60ae11bef --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ShaderResource.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Adobe Systems Incorporated. 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 HOLDER “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 HOLDER 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 "core/loader/cache/ShaderResource.h" + +#include "core/loader/TextResourceDecoder.h" +#include "core/platform/SharedBuffer.h" +#include "wtf/text/StringBuilder.h" + +namespace WebCore { + +ShaderResource::ShaderResource(const ResourceRequest& resourceRequest) + : Resource(resourceRequest, Shader) + , m_decoder(TextResourceDecoder::create("application/shader")) +{ +} + +ShaderResource::~ShaderResource() +{ +} + +const String& ShaderResource::shaderString() +{ + if (m_shaderString.isNull() && m_data) { + StringBuilder builder; + builder.append(m_decoder->decode(m_data->data(), m_data->size())); + builder.append(m_decoder->flush()); + m_shaderString = builder.toString(); + } + + return m_shaderString; +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/ShaderResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/ShaderResource.h new file mode 100644 index 00000000000..f7d2ffe7fbb --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/ShaderResource.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 Adobe Systems Incorporated. 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 HOLDER “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 HOLDER 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 ShaderResource_h +#define ShaderResource_h + +#include "core/loader/cache/Resource.h" + +namespace WebCore { + +class TextResourceDecoder; + +class ShaderResource : public Resource { +public: + ShaderResource(const ResourceRequest&); + virtual ~ShaderResource(); + + const String& shaderString(); + +private: + RefPtr<TextResourceDecoder> m_decoder; + String m_shaderString; +}; + +} + + +#endif // ShaderResource_h diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/StyleSheetResourceClient.h b/chromium/third_party/WebKit/Source/core/loader/cache/StyleSheetResourceClient.h new file mode 100644 index 00000000000..02dc5bcabc6 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/StyleSheetResourceClient.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) + Copyright (C) 2001 Dirk Mueller <mueller@kde.org> + Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + Copyright (C) 2011 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. + + 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 StyleSheetResourceClient_h +#define StyleSheetResourceClient_h + +#include "core/loader/cache/ResourceClient.h" +#include "weborigin/KURL.h" +#include "wtf/Forward.h" + +namespace WebCore { +class CSSStyleSheetResource; + +class StyleSheetResourceClient : public ResourceClient { +public: + virtual ~StyleSheetResourceClient() { } + static ResourceClientType expectedType() { return StyleSheetType; } + virtual ResourceClientType resourceClientType() const { return expectedType(); } + virtual void setCSSStyleSheet(const String& /* href */, const KURL& /* baseURL */, const String& /* charset */, const CSSStyleSheetResource*) { } + virtual void setXSLStyleSheet(const String& /* href */, const KURL& /* baseURL */, const String& /* sheet */) { } +}; +} + +#endif // StyleSheetResourceClient_h diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/TextTrackResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/TextTrackResource.cpp new file mode 100644 index 00000000000..cf5e4a13f07 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/TextTrackResource.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 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 "core/loader/cache/TextTrackResource.h" + +#include "core/loader/cache/ResourceClient.h" +#include "core/loader/cache/ResourceClientWalker.h" + +namespace WebCore { + +TextTrackResource::TextTrackResource(const ResourceRequest& resourceRequest) + : Resource(resourceRequest, TextTrack) +{ +} + +TextTrackResource::~TextTrackResource() +{ +} + +void TextTrackResource::appendData(const char* data, int length) +{ + Resource::appendData(data, length); + ResourceClientWalker<ResourceClient> walker(m_clients); + while (ResourceClient *client = walker.next()) + client->deprecatedDidReceiveResource(this); +} + +} + diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/TextTrackResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/TextTrackResource.h new file mode 100644 index 00000000000..c3c0eba0955 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/TextTrackResource.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef TextTrackResource_h +#define TextTrackResource_h + +#include "core/loader/cache/Resource.h" + +namespace WebCore { + +class TextTrackResource : public Resource { +public: + TextTrackResource(const ResourceRequest&); + virtual ~TextTrackResource(); + + virtual void appendData(const char*, int) OVERRIDE; +}; + +} + +#endif diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/XSLStyleSheetResource.cpp b/chromium/third_party/WebKit/Source/core/loader/cache/XSLStyleSheetResource.cpp new file mode 100644 index 00000000000..9d9a014b78e --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/XSLStyleSheetResource.cpp @@ -0,0 +1,78 @@ +/* + 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. + + 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. + + 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" +#include "core/loader/cache/XSLStyleSheetResource.h" + +#include "core/loader/TextResourceDecoder.h" +#include "core/loader/cache/ResourceClientWalker.h" +#include "core/loader/cache/StyleSheetResourceClient.h" +#include "core/platform/SharedBuffer.h" +#include "wtf/Vector.h" + +namespace WebCore { + +XSLStyleSheetResource::XSLStyleSheetResource(const ResourceRequest& resourceRequest) + : Resource(resourceRequest, XSLStyleSheet) + , m_decoder(TextResourceDecoder::create("text/xsl")) +{ + DEFINE_STATIC_LOCAL(const AtomicString, acceptXSLT, ("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml", AtomicString::ConstructFromLiteral)); + + // It's XML we want. + // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example. + setAccept(acceptXSLT); +} + +void XSLStyleSheetResource::didAddClient(ResourceClient* c) +{ + ASSERT(c->resourceClientType() == StyleSheetResourceClient::expectedType()); + if (!isLoading()) + static_cast<StyleSheetResourceClient*>(c)->setXSLStyleSheet(m_resourceRequest.url(), m_response.url(), m_sheet); +} + +void XSLStyleSheetResource::setEncoding(const String& chs) +{ + m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader); +} + +String XSLStyleSheetResource::encoding() const +{ + return m_decoder->encoding().name(); +} + +void XSLStyleSheetResource::checkNotify() +{ + if (m_data.get()) { + m_sheet = m_decoder->decode(m_data->data(), encodedSize()); + m_sheet.append(m_decoder->flush()); + } + + ResourceClientWalker<StyleSheetResourceClient> w(m_clients); + while (StyleSheetResourceClient* c = w.next()) + c->setXSLStyleSheet(m_resourceRequest.url(), m_response.url(), m_sheet); +} + +} // namespace WebCore diff --git a/chromium/third_party/WebKit/Source/core/loader/cache/XSLStyleSheetResource.h b/chromium/third_party/WebKit/Source/core/loader/cache/XSLStyleSheetResource.h new file mode 100644 index 00000000000..4cab515a463 --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/loader/cache/XSLStyleSheetResource.h @@ -0,0 +1,55 @@ +/* + 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. + + 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. + + 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 XSLStyleSheetResource_h +#define XSLStyleSheetResource_h + +#include "core/loader/cache/Resource.h" + +namespace WebCore { + +class ResourceFetcher; +class TextResourceDecoder; + +class XSLStyleSheetResource : public Resource { +public: + XSLStyleSheetResource(const ResourceRequest&); + + const String& sheet() const { return m_sheet; } + + virtual void didAddClient(ResourceClient*); + virtual void setEncoding(const String&); + virtual String encoding() const; + +protected: + virtual void checkNotify(); + + String m_sheet; + RefPtr<TextResourceDecoder> m_decoder; +}; + +} // namespace WebCore + +#endif |