af40cc204ad047f85c23417cbce7f197e3be6241
[WebKit-https.git] / Source / WebCore / loader / cache / CachedCSSStyleSheet.cpp
1 /*
2     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5     Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6     Copyright (C) 2004-2017 Apple Inc. All rights reserved.
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     along with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "CachedCSSStyleSheet.h"
26
27 #include "CSSStyleSheet.h"
28 #include "CachedResourceClientWalker.h"
29 #include "CachedResourceRequest.h"
30 #include "CachedStyleSheetClient.h"
31 #include "HTTPHeaderNames.h"
32 #include "HTTPParsers.h"
33 #include "MemoryCache.h"
34 #include "SharedBuffer.h"
35 #include "StyleSheetContents.h"
36 #include "TextResourceDecoder.h"
37 #include <wtf/CurrentTime.h>
38
39 namespace WebCore {
40
41 CachedCSSStyleSheet::CachedCSSStyleSheet(CachedResourceRequest&& request, SessionID sessionID)
42     : CachedResource(WTFMove(request), CSSStyleSheet, sessionID)
43     , m_decoder(TextResourceDecoder::create("text/css", request.charset()))
44 {
45 }
46
47 CachedCSSStyleSheet::~CachedCSSStyleSheet()
48 {
49     if (m_parsedStyleSheetCache)
50         m_parsedStyleSheetCache->removedFromMemoryCache();
51 }
52
53 void CachedCSSStyleSheet::didAddClient(CachedResourceClient& client)
54 {
55     ASSERT(client.resourceClientType() == CachedStyleSheetClient::expectedType());
56     // CachedResource::didAddClient() must be before setCSSStyleSheet(),
57     // because setCSSStyleSheet() may cause scripts to be executed, which could destroy 'c' if it is an instance of HTMLLinkElement.
58     // see the comment of HTMLLinkElement::setCSSStyleSheet.
59     CachedResource::didAddClient(client);
60
61     if (!isLoading())
62         static_cast<CachedStyleSheetClient&>(client).setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this);
63 }
64
65 void CachedCSSStyleSheet::setEncoding(const String& chs)
66 {
67     m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader);
68 }
69
70 String CachedCSSStyleSheet::encoding() const
71 {
72     return m_decoder->encoding().name();
73 }
74
75 const String CachedCSSStyleSheet::sheetText(MIMETypeCheck mimeTypeCheck, bool* hasValidMIMEType) const
76 {
77     if (!m_data || m_data->isEmpty() || !canUseSheet(mimeTypeCheck, hasValidMIMEType))
78         return String();
79
80     if (!m_decodedSheetText.isNull())
81         return m_decodedSheetText;
82
83     // Don't cache the decoded text, regenerating is cheap and it can use quite a bit of memory
84     return m_decoder->decodeAndFlush(m_data->data(), m_data->size());
85 }
86
87 void CachedCSSStyleSheet::setBodyDataFrom(const CachedResource& resource)
88 {
89     ASSERT(resource.type() == type());
90     const CachedCSSStyleSheet& sheet = static_cast<const CachedCSSStyleSheet&>(resource);
91
92     CachedResource::setBodyDataFrom(resource);
93
94     m_decoder = sheet.m_decoder;
95     m_decodedSheetText = sheet.m_decodedSheetText;
96     if (sheet.m_parsedStyleSheetCache)
97         saveParsedStyleSheet(*sheet.m_parsedStyleSheetCache);
98 }
99
100 void CachedCSSStyleSheet::finishLoading(SharedBuffer* data)
101 {
102     m_data = data;
103     setEncodedSize(data ? data->size() : 0);
104     // Decode the data to find out the encoding and keep the sheet text around during checkNotify()
105     if (data)
106         m_decodedSheetText = m_decoder->decodeAndFlush(data->data(), data->size());
107     setLoading(false);
108     checkNotify();
109     // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate.
110     m_decodedSheetText = String();
111 }
112
113 void CachedCSSStyleSheet::checkNotify()
114 {
115     if (isLoading())
116         return;
117
118     CachedResourceClientWalker<CachedStyleSheetClient> w(m_clients);
119     while (CachedStyleSheetClient* c = w.next())
120         c->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this);
121 }
122
123 bool CachedCSSStyleSheet::canUseSheet(MIMETypeCheck mimeTypeCheck, bool* hasValidMIMEType) const
124 {
125     if (errorOccurred())
126         return false;
127
128     if (mimeTypeCheck == MIMETypeCheck::Lax)
129         return true;
130
131     // This check exactly matches Firefox.  Note that we grab the Content-Type
132     // header directly because we want to see what the value is BEFORE content
133     // sniffing.  Firefox does this by setting a "type hint" on the channel.
134     // This implementation should be observationally equivalent.
135     //
136     // This code defaults to allowing the stylesheet for non-HTTP protocols so
137     // folks can use standards mode for local HTML documents.
138     String mimeType = extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType));
139     bool typeOK = mimeType.isEmpty() || equalLettersIgnoringASCIICase(mimeType, "text/css") || equalLettersIgnoringASCIICase(mimeType, "application/x-unknown-content-type");
140     if (hasValidMIMEType)
141         *hasValidMIMEType = typeOK;
142     return typeOK;
143 }
144
145 void CachedCSSStyleSheet::destroyDecodedData()
146 {
147     if (!m_parsedStyleSheetCache)
148         return;
149
150     m_parsedStyleSheetCache->removedFromMemoryCache();
151     m_parsedStyleSheetCache = nullptr;
152
153     setDecodedSize(0);
154 }
155
156 RefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context, CachePolicy cachePolicy)
157 {
158     if (!m_parsedStyleSheetCache)
159         return nullptr;
160     if (!m_parsedStyleSheetCache->subresourcesAllowReuse(cachePolicy)) {
161         m_parsedStyleSheetCache->removedFromMemoryCache();
162         m_parsedStyleSheetCache = nullptr;
163         return nullptr;
164     }
165
166     ASSERT(m_parsedStyleSheetCache->isCacheable());
167     ASSERT(m_parsedStyleSheetCache->isInMemoryCache());
168
169     // Contexts must be identical so we know we would get the same exact result if we parsed again.
170     if (m_parsedStyleSheetCache->parserContext() != context)
171         return nullptr;
172
173     didAccessDecodedData(monotonicallyIncreasingTime());
174
175     return m_parsedStyleSheetCache;
176 }
177
178 void CachedCSSStyleSheet::saveParsedStyleSheet(Ref<StyleSheetContents>&& sheet)
179 {
180     ASSERT(sheet->isCacheable());
181
182     if (m_parsedStyleSheetCache)
183         m_parsedStyleSheetCache->removedFromMemoryCache();
184     m_parsedStyleSheetCache = WTFMove(sheet);
185     m_parsedStyleSheetCache->addedToMemoryCache();
186
187     setDecodedSize(m_parsedStyleSheetCache->estimatedSizeInBytes());
188 }
189
190 }