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