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 #include "core/inspector/NetworkResourcesData.h"
31
32 #include "core/dom/DOMImplementation.h"
33 #include "core/fetch/Resource.h"
34 #include "platform/SharedBuffer.h"
35 #include "platform/network/ResourceResponse.h"
36
37 namespace {
38 // 100MB
39 static size_t maximumResourcesContentSize = 100 * 1000 * 1000;
40
41 // 10MB
42 static size_t maximumSingleResourceContentSize = 10 * 1000 * 1000;
43 }
44
45 namespace WebCore {
46
47
create(const AtomicString & method,const KURL & url,bool async,PassRefPtr<FormData> formData,bool includeCredentials)48 PassRefPtr<XHRReplayData> XHRReplayData::create(const AtomicString& method, const KURL& url, bool async, PassRefPtr<FormData> formData, bool includeCredentials)
49 {
50 return adoptRef(new XHRReplayData(method, url, async, formData, includeCredentials));
51 }
52
addHeader(const AtomicString & key,const AtomicString & value)53 void XHRReplayData::addHeader(const AtomicString& key, const AtomicString& value)
54 {
55 m_headers.set(key, value);
56 }
57
XHRReplayData(const AtomicString & method,const KURL & url,bool async,PassRefPtr<FormData> formData,bool includeCredentials)58 XHRReplayData::XHRReplayData(const AtomicString& method, const KURL& url, bool async, PassRefPtr<FormData> formData, bool includeCredentials)
59 : m_method(method)
60 , m_url(url)
61 , m_async(async)
62 , m_formData(formData)
63 , m_includeCredentials(includeCredentials)
64 {
65 }
66
67 // ResourceData
ResourceData(const String & requestId,const String & loaderId)68 NetworkResourcesData::ResourceData::ResourceData(const String& requestId, const String& loaderId)
69 : m_requestId(requestId)
70 , m_loaderId(loaderId)
71 , m_base64Encoded(false)
72 , m_isContentEvicted(false)
73 , m_type(InspectorPageAgent::OtherResource)
74 , m_cachedResource(0)
75 {
76 }
77
setContent(const String & content,bool base64Encoded)78 void NetworkResourcesData::ResourceData::setContent(const String& content, bool base64Encoded)
79 {
80 ASSERT(!hasData());
81 ASSERT(!hasContent());
82 m_content = content;
83 m_base64Encoded = base64Encoded;
84 }
85
contentSizeInBytes(const String & content)86 static size_t contentSizeInBytes(const String& content)
87 {
88 return content.isNull() ? 0 : content.impl()->sizeInBytes();
89 }
90
removeContent()91 unsigned NetworkResourcesData::ResourceData::removeContent()
92 {
93 unsigned result = 0;
94 if (hasData()) {
95 ASSERT(!hasContent());
96 result = m_dataBuffer->size();
97 m_dataBuffer = nullptr;
98 }
99
100 if (hasContent()) {
101 ASSERT(!hasData());
102 result = contentSizeInBytes(m_content);
103 m_content = String();
104 }
105 return result;
106 }
107
evictContent()108 unsigned NetworkResourcesData::ResourceData::evictContent()
109 {
110 m_isContentEvicted = true;
111 return removeContent();
112 }
113
dataLength() const114 size_t NetworkResourcesData::ResourceData::dataLength() const
115 {
116 return m_dataBuffer ? m_dataBuffer->size() : 0;
117 }
118
appendData(const char * data,size_t dataLength)119 void NetworkResourcesData::ResourceData::appendData(const char* data, size_t dataLength)
120 {
121 ASSERT(!hasContent());
122 if (!m_dataBuffer)
123 m_dataBuffer = SharedBuffer::create(data, dataLength);
124 else
125 m_dataBuffer->append(data, dataLength);
126 }
127
decodeDataToContent()128 size_t NetworkResourcesData::ResourceData::decodeDataToContent()
129 {
130 ASSERT(!hasContent());
131 size_t dataLength = m_dataBuffer->size();
132 m_content = m_decoder->decode(m_dataBuffer->data(), m_dataBuffer->size());
133 m_content.append(m_decoder->flush());
134 m_dataBuffer = nullptr;
135 return contentSizeInBytes(m_content) - dataLength;
136 }
137
138 // NetworkResourcesData
NetworkResourcesData()139 NetworkResourcesData::NetworkResourcesData()
140 : m_contentSize(0)
141 , m_maximumResourcesContentSize(maximumResourcesContentSize)
142 , m_maximumSingleResourceContentSize(maximumSingleResourceContentSize)
143 {
144 }
145
~NetworkResourcesData()146 NetworkResourcesData::~NetworkResourcesData()
147 {
148 clear();
149 }
150
resourceCreated(const String & requestId,const String & loaderId)151 void NetworkResourcesData::resourceCreated(const String& requestId, const String& loaderId)
152 {
153 ensureNoDataForRequestId(requestId);
154 m_requestIdToResourceDataMap.set(requestId, new ResourceData(requestId, loaderId));
155 }
156
createOtherResourceTextDecoder(const String & mimeType,const String & textEncodingName)157 static PassOwnPtr<TextResourceDecoder> createOtherResourceTextDecoder(const String& mimeType, const String& textEncodingName)
158 {
159 OwnPtr<TextResourceDecoder> decoder;
160 if (!textEncodingName.isEmpty())
161 decoder = TextResourceDecoder::create("text/plain", textEncodingName);
162 else if (DOMImplementation::isXMLMIMEType(mimeType.lower())) {
163 decoder = TextResourceDecoder::create("application/xml");
164 decoder->useLenientXMLDecoding();
165 } else if (equalIgnoringCase(mimeType, "text/html"))
166 decoder = TextResourceDecoder::create("text/html", "UTF-8");
167 else if (mimeType == "text/plain")
168 decoder = TextResourceDecoder::create("text/plain", "ISO-8859-1");
169 return decoder.release();
170 }
171
responseReceived(const String & requestId,const String & frameId,const ResourceResponse & response)172 void NetworkResourcesData::responseReceived(const String& requestId, const String& frameId, const ResourceResponse& response)
173 {
174 ResourceData* resourceData = resourceDataForRequestId(requestId);
175 if (!resourceData)
176 return;
177 resourceData->setFrameId(frameId);
178 resourceData->setUrl(response.url());
179 resourceData->setDecoder(createOtherResourceTextDecoder(response.mimeType(), response.textEncodingName()));
180 resourceData->setHTTPStatusCode(response.httpStatusCode());
181 }
182
setResourceType(const String & requestId,InspectorPageAgent::ResourceType type)183 void NetworkResourcesData::setResourceType(const String& requestId, InspectorPageAgent::ResourceType type)
184 {
185 ResourceData* resourceData = resourceDataForRequestId(requestId);
186 if (!resourceData)
187 return;
188 resourceData->setType(type);
189 }
190
resourceType(const String & requestId)191 InspectorPageAgent::ResourceType NetworkResourcesData::resourceType(const String& requestId)
192 {
193 ResourceData* resourceData = resourceDataForRequestId(requestId);
194 if (!resourceData)
195 return InspectorPageAgent::OtherResource;
196 return resourceData->type();
197 }
198
setResourceContent(const String & requestId,const String & content,bool base64Encoded)199 void NetworkResourcesData::setResourceContent(const String& requestId, const String& content, bool base64Encoded)
200 {
201 ResourceData* resourceData = resourceDataForRequestId(requestId);
202 if (!resourceData)
203 return;
204 size_t dataLength = contentSizeInBytes(content);
205 if (dataLength > m_maximumSingleResourceContentSize)
206 return;
207 if (resourceData->isContentEvicted())
208 return;
209 if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
210 // We can not be sure that we didn't try to save this request data while it was loading, so remove it, if any.
211 if (resourceData->hasContent())
212 m_contentSize -= resourceData->removeContent();
213 m_requestIdsDeque.append(requestId);
214 resourceData->setContent(content, base64Encoded);
215 m_contentSize += dataLength;
216 }
217 }
218
maybeAddResourceData(const String & requestId,const char * data,size_t dataLength)219 void NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, size_t dataLength)
220 {
221 ResourceData* resourceData = resourceDataForRequestId(requestId);
222 if (!resourceData)
223 return;
224 if (!resourceData->decoder())
225 return;
226 if (resourceData->dataLength() + dataLength > m_maximumSingleResourceContentSize)
227 m_contentSize -= resourceData->evictContent();
228 if (resourceData->isContentEvicted())
229 return;
230 if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
231 m_requestIdsDeque.append(requestId);
232 resourceData->appendData(data, dataLength);
233 m_contentSize += dataLength;
234 }
235 }
236
maybeDecodeDataToContent(const String & requestId)237 void NetworkResourcesData::maybeDecodeDataToContent(const String& requestId)
238 {
239 ResourceData* resourceData = resourceDataForRequestId(requestId);
240 if (!resourceData)
241 return;
242 if (!resourceData->hasData())
243 return;
244 m_contentSize += resourceData->decodeDataToContent();
245 size_t dataLength = contentSizeInBytes(resourceData->content());
246 if (dataLength > m_maximumSingleResourceContentSize)
247 m_contentSize -= resourceData->evictContent();
248 }
249
addResource(const String & requestId,Resource * cachedResource)250 void NetworkResourcesData::addResource(const String& requestId, Resource* cachedResource)
251 {
252 ResourceData* resourceData = resourceDataForRequestId(requestId);
253 if (!resourceData)
254 return;
255 resourceData->setResource(cachedResource);
256 }
257
addResourceSharedBuffer(const String & requestId,PassRefPtr<SharedBuffer> buffer,const String & textEncodingName)258 void NetworkResourcesData::addResourceSharedBuffer(const String& requestId, PassRefPtr<SharedBuffer> buffer, const String& textEncodingName)
259 {
260 ResourceData* resourceData = resourceDataForRequestId(requestId);
261 if (!resourceData)
262 return;
263 resourceData->setBuffer(buffer);
264 resourceData->setTextEncodingName(textEncodingName);
265 }
266
data(const String & requestId)267 NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId)
268 {
269 return resourceDataForRequestId(requestId);
270 }
271
xhrReplayData(const String & requestId)272 XHRReplayData* NetworkResourcesData::xhrReplayData(const String& requestId)
273 {
274 if (m_reusedXHRReplayDataRequestIds.contains(requestId))
275 return xhrReplayData(m_reusedXHRReplayDataRequestIds.get(requestId));
276
277 ResourceData* resourceData = resourceDataForRequestId(requestId);
278 if (!resourceData)
279 return 0;
280 return resourceData->xhrReplayData();
281 }
282
setXHRReplayData(const String & requestId,XHRReplayData * xhrReplayData)283 void NetworkResourcesData::setXHRReplayData(const String& requestId, XHRReplayData* xhrReplayData)
284 {
285 ResourceData* resourceData = resourceDataForRequestId(requestId);
286 if (!resourceData) {
287 Vector<String> result;
288 ReusedRequestIds::iterator it;
289 ReusedRequestIds::iterator end = m_reusedXHRReplayDataRequestIds.end();
290 for (it = m_reusedXHRReplayDataRequestIds.begin(); it != end; ++it) {
291 if (it->value == requestId)
292 setXHRReplayData(it->key, xhrReplayData);
293 }
294 return;
295 }
296
297 resourceData->setXHRReplayData(xhrReplayData);
298 }
299
reuseXHRReplayData(const String & requestId,const String & reusedRequestId)300 void NetworkResourcesData::reuseXHRReplayData(const String& requestId, const String& reusedRequestId)
301 {
302 ResourceData* reusedResourceData = resourceDataForRequestId(reusedRequestId);
303 ResourceData* resourceData = resourceDataForRequestId(requestId);
304 if (!reusedResourceData || !resourceData) {
305 m_reusedXHRReplayDataRequestIds.set(requestId, reusedRequestId);
306 return;
307 }
308
309 resourceData->setXHRReplayData(reusedResourceData->xhrReplayData());
310 }
311
resources()312 Vector<NetworkResourcesData::ResourceData*> NetworkResourcesData::resources()
313 {
314 Vector<ResourceData*> result;
315 for (ResourceDataMap::iterator it = m_requestIdToResourceDataMap.begin(); it != m_requestIdToResourceDataMap.end(); ++it)
316 result.append(it->value);
317 return result;
318 }
319
removeResource(Resource * cachedResource)320 Vector<String> NetworkResourcesData::removeResource(Resource* cachedResource)
321 {
322 Vector<String> result;
323 ResourceDataMap::iterator it;
324 ResourceDataMap::iterator end = m_requestIdToResourceDataMap.end();
325 for (it = m_requestIdToResourceDataMap.begin(); it != end; ++it) {
326 ResourceData* resourceData = it->value;
327 if (resourceData->cachedResource() == cachedResource) {
328 resourceData->setResource(0);
329 result.append(it->key);
330 }
331 }
332
333 return result;
334 }
335
clear(const String & preservedLoaderId)336 void NetworkResourcesData::clear(const String& preservedLoaderId)
337 {
338 m_requestIdsDeque.clear();
339 m_contentSize = 0;
340
341 ResourceDataMap preservedMap;
342
343 ResourceDataMap::iterator it;
344 ResourceDataMap::iterator end = m_requestIdToResourceDataMap.end();
345 for (it = m_requestIdToResourceDataMap.begin(); it != end; ++it) {
346 ResourceData* resourceData = it->value;
347 if (!preservedLoaderId.isNull() && resourceData->loaderId() == preservedLoaderId)
348 preservedMap.set(it->key, it->value);
349 else
350 delete resourceData;
351 }
352 m_requestIdToResourceDataMap.swap(preservedMap);
353
354 m_reusedXHRReplayDataRequestIds.clear();
355 }
356
setResourcesDataSizeLimits(size_t maximumResourcesContentSize,size_t maximumSingleResourceContentSize)357 void NetworkResourcesData::setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize)
358 {
359 clear();
360 m_maximumResourcesContentSize = maximumResourcesContentSize;
361 m_maximumSingleResourceContentSize = maximumSingleResourceContentSize;
362 }
363
resourceDataForRequestId(const String & requestId)364 NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId)
365 {
366 if (requestId.isNull())
367 return 0;
368 return m_requestIdToResourceDataMap.get(requestId);
369 }
370
ensureNoDataForRequestId(const String & requestId)371 void NetworkResourcesData::ensureNoDataForRequestId(const String& requestId)
372 {
373 ResourceData* resourceData = resourceDataForRequestId(requestId);
374 if (!resourceData)
375 return;
376 if (resourceData->hasContent() || resourceData->hasData())
377 m_contentSize -= resourceData->evictContent();
378 delete resourceData;
379 m_requestIdToResourceDataMap.remove(requestId);
380 }
381
ensureFreeSpace(size_t size)382 bool NetworkResourcesData::ensureFreeSpace(size_t size)
383 {
384 if (size > m_maximumResourcesContentSize)
385 return false;
386
387 while (size > m_maximumResourcesContentSize - m_contentSize) {
388 String requestId = m_requestIdsDeque.takeFirst();
389 ResourceData* resourceData = resourceDataForRequestId(requestId);
390 if (resourceData)
391 m_contentSize -= resourceData->evictContent();
392 }
393 return true;
394 }
395
396 } // namespace WebCore
397
398