9ef75fb14da4d2bb21d81fa4e7691a60c997d657
[WebKit-https.git] / Source / WebCore / inspector / NetworkResourcesData.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20  * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30
31 #if ENABLE(INSPECTOR)
32
33 #include "NetworkResourcesData.h"
34
35 #include "DOMImplementation.h"
36 #include "SharedBuffer.h"
37 #include "TextResourceDecoder.h"
38
39 namespace {
40 // 100MB
41 static size_t maximumResourcesContentSize = 100 * 1000 * 1000;
42
43 // 10MB
44 static size_t maximumSingleResourceContentSize = 10 * 1000 * 1000;
45 }
46
47 namespace WebCore {
48
49
50 // ResourceData
51 NetworkResourcesData::ResourceData::ResourceData(const String& requestId, const String& loaderId)
52     : m_requestId(requestId)
53     , m_loaderId(loaderId)
54     , m_base64Encoded(false)
55     , m_isContentPurged(false)
56     , m_type(InspectorPageAgent::OtherResource)
57     , m_cachedResource(0)
58 {
59 }
60
61 void NetworkResourcesData::ResourceData::setContent(const String& content, bool base64Encoded)
62 {
63     ASSERT(!hasData());
64     ASSERT(!hasContent());
65     m_content = content;
66     m_base64Encoded = base64Encoded;
67 }
68
69 unsigned NetworkResourcesData::ResourceData::removeContent()
70 {
71     unsigned result = 0;
72     if (hasData()) {
73         ASSERT(!hasContent());
74         result = m_dataBuffer->size();
75         m_dataBuffer = nullptr;
76     }
77
78     if (hasContent()) {
79         ASSERT(!hasData());
80         result = 2 * m_content.length();
81         m_content = String();
82     }
83     return result;
84 }
85
86 unsigned NetworkResourcesData::ResourceData::purgeContent()
87 {
88     m_isContentPurged = true;
89     return removeContent();
90 }
91
92 size_t NetworkResourcesData::ResourceData::dataLength() const
93 {
94     return m_dataBuffer ? m_dataBuffer->size() : 0;
95 }
96
97 void NetworkResourcesData::ResourceData::appendData(const char* data, size_t dataLength)
98 {
99     ASSERT(!hasContent());
100     if (!m_dataBuffer)
101         m_dataBuffer = SharedBuffer::create(data, dataLength);
102     else
103         m_dataBuffer->append(data, dataLength);
104 }
105
106 size_t NetworkResourcesData::ResourceData::decodeDataToContent()
107 {
108     ASSERT(!hasContent());
109     size_t dataLength = m_dataBuffer->size();
110     m_content = m_decoder->decode(m_dataBuffer->data(), m_dataBuffer->size());
111     m_content += m_decoder->flush();
112     m_dataBuffer = nullptr;
113     return 2 * m_content.length() - dataLength;
114 }
115
116 // NetworkResourcesData
117 NetworkResourcesData::NetworkResourcesData()
118     : m_contentSize(0)
119     , m_maximumResourcesContentSize(maximumResourcesContentSize)
120     , m_maximumSingleResourceContentSize(maximumSingleResourceContentSize)
121 {
122 }
123
124 NetworkResourcesData::~NetworkResourcesData()
125 {
126     clear();
127 }
128
129 void NetworkResourcesData::resourceCreated(const String& requestId, const String& loaderId)
130 {
131     ensureNoDataForRequestId(requestId);
132     m_requestIdToResourceDataMap.set(requestId, new ResourceData(requestId, loaderId));
133 }
134
135 static PassRefPtr<TextResourceDecoder> createOtherResourceTextDecoder(const String& mimeType, const String& textEncodingName)
136 {
137     RefPtr<TextResourceDecoder> decoder;
138     if (!textEncodingName.isEmpty())
139         decoder = TextResourceDecoder::create("text/plain", textEncodingName);
140     else if (DOMImplementation::isXMLMIMEType(mimeType.lower())) {
141         decoder = TextResourceDecoder::create("application/xml");
142         decoder->useLenientXMLDecoding();
143     } else if (equalIgnoringCase(mimeType, "text/html"))
144         decoder = TextResourceDecoder::create("text/html", "UTF-8");
145     else if (mimeType == "text/plain")
146         decoder = TextResourceDecoder::create("text/plain", "ISO-8859-1");
147     return decoder;
148 }
149
150 void NetworkResourcesData::responseReceived(const String& requestId, const String& frameId, const ResourceResponse& response)
151 {
152     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
153     if (!resourceData)
154         return;
155     resourceData->setFrameId(frameId);
156     resourceData->setUrl(response.url());
157     resourceData->setDecoder(createOtherResourceTextDecoder(response.mimeType(), response.textEncodingName()));
158     resourceData->setHTTPStatusCode(response.httpStatusCode());
159 }
160
161 void NetworkResourcesData::setResourceType(const String& requestId, InspectorPageAgent::ResourceType type)
162 {
163     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
164     if (!resourceData)
165         return;
166     resourceData->setType(type);
167 }
168
169 InspectorPageAgent::ResourceType NetworkResourcesData::resourceType(const String& requestId)
170 {
171     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
172     if (!resourceData)
173         return InspectorPageAgent::OtherResource;
174     return resourceData->type();
175 }
176
177 void NetworkResourcesData::setResourceContent(const String& requestId, const String& content, bool base64Encoded)
178 {
179     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
180     if (!resourceData)
181         return;
182     size_t dataLength = 2 * content.length();
183     if (dataLength > m_maximumSingleResourceContentSize)
184         return;
185     if (resourceData->isContentPurged())
186         return;
187     if (ensureFreeSpace(dataLength) && !resourceData->isContentPurged()) {
188         // We can not be sure that we didn't try to save this request data while it was loading, so remove it, if any.
189         if (resourceData->hasContent())
190             m_contentSize -= resourceData->removeContent();
191         m_requestIdsDeque.append(requestId);
192         resourceData->setContent(content, base64Encoded);
193         m_contentSize += dataLength;
194     }
195 }
196
197 void NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, size_t dataLength)
198 {
199     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
200     if (!resourceData)
201         return;
202     if (!resourceData->decoder())
203         return;
204     if (resourceData->dataLength() + dataLength > m_maximumSingleResourceContentSize)
205         m_contentSize -= resourceData->purgeContent();
206     if (resourceData->isContentPurged())
207         return;
208     if (ensureFreeSpace(dataLength) && !resourceData->isContentPurged()) {
209         m_requestIdsDeque.append(requestId);
210         resourceData->appendData(data, dataLength);
211         m_contentSize += dataLength;
212     }
213 }
214
215 void NetworkResourcesData::maybeDecodeDataToContent(const String& requestId)
216 {
217     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
218     if (!resourceData)
219         return;
220     if (!resourceData->hasData())
221         return;
222     m_contentSize += resourceData->decodeDataToContent();
223     size_t dataLength = 2 * resourceData->content().length();
224     if (dataLength > m_maximumSingleResourceContentSize)
225         m_contentSize -= resourceData->purgeContent();
226 }
227
228 void NetworkResourcesData::addCachedResource(const String& requestId, CachedResource* cachedResource)
229 {
230     if (!m_requestIdToResourceDataMap.contains(requestId))
231         return;
232     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
233
234     resourceData->setCachedResource(cachedResource);
235 }
236
237 void NetworkResourcesData::addResourceSharedBuffer(const String& requestId, PassRefPtr<SharedBuffer> buffer, const String& textEncodingName)
238 {
239     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
240     if (!resourceData)
241         return;
242     resourceData->setBuffer(buffer);
243     resourceData->setTextEncodingName(textEncodingName);
244 }
245
246 NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId)
247 {
248     return m_requestIdToResourceDataMap.get(requestId);
249 }
250
251 Vector<String> NetworkResourcesData::removeCachedResource(CachedResource* cachedResource)
252 {
253     Vector<String> result;
254     ResourceDataMap::iterator it;
255     ResourceDataMap::iterator end = m_requestIdToResourceDataMap.end();
256     for (it = m_requestIdToResourceDataMap.begin(); it != end; ++it) {
257         ResourceData* resourceData = it->second;
258         if (resourceData->cachedResource() == cachedResource) {
259             resourceData->setCachedResource(0);
260             result.append(it->first);
261         }
262     }
263
264     return result;
265 }
266
267 void NetworkResourcesData::clear(const String& preservedLoaderId)
268 {
269     m_requestIdsDeque.clear();
270     m_contentSize = 0;
271
272     ResourceDataMap preservedMap;
273
274     ResourceDataMap::iterator it;
275     ResourceDataMap::iterator end = m_requestIdToResourceDataMap.end();
276     for (it = m_requestIdToResourceDataMap.begin(); it != end; ++it) {
277         ResourceData* resourceData = it->second;
278         if (!preservedLoaderId.isNull() && resourceData->loaderId() == preservedLoaderId)
279             preservedMap.set(it->first, it->second);
280         else
281             delete resourceData;
282     }
283     m_requestIdToResourceDataMap.swap(preservedMap);
284 }
285
286 void NetworkResourcesData::setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize)
287 {
288     clear();
289     m_maximumResourcesContentSize = maximumResourcesContentSize;
290     m_maximumSingleResourceContentSize = maximumSingleResourceContentSize;
291 }
292
293
294 void NetworkResourcesData::ensureNoDataForRequestId(const String& requestId)
295 {
296     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
297     if (resourceData) {
298         if (resourceData->hasContent() || resourceData->hasData())
299             m_contentSize -= resourceData->purgeContent();
300         delete resourceData;
301         m_requestIdToResourceDataMap.remove(requestId);
302     }
303 }
304
305 bool NetworkResourcesData::ensureFreeSpace(size_t size)
306 {
307     if (size > m_maximumResourcesContentSize)
308         return false;
309
310     while (size > m_maximumResourcesContentSize - m_contentSize) {
311         String requestId = m_requestIdsDeque.takeFirst();
312         ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
313         if (resourceData)
314             m_contentSize -= resourceData->purgeContent();
315     }
316     return true;
317 }
318
319 } // namespace WebCore
320
321 #endif // ENABLE(INSPECTOR)