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 38namespace { 39// 100MB 40static size_t maximumResourcesContentSize = 100 * 1000 * 1000; 41 42// 10MB 43static size_t maximumSingleResourceContentSize = 10 * 1000 * 1000; 44} 45 46namespace blink { 47 48 49PassRefPtrWillBeRawPtr<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 54void XHRReplayData::addHeader(const AtomicString& key, const AtomicString& value) 55{ 56 m_headers.set(key, value); 57} 58 59XHRReplayData::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 70NetworkResourcesData::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 80void 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 88static size_t contentSizeInBytes(const String& content) 89{ 90 return content.isNull() ? 0 : content.impl()->sizeInBytes(); 91} 92 93unsigned 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 110unsigned NetworkResourcesData::ResourceData::evictContent() 111{ 112 m_isContentEvicted = true; 113 return removeContent(); 114} 115 116size_t NetworkResourcesData::ResourceData::dataLength() const 117{ 118 return m_dataBuffer ? m_dataBuffer->size() : 0; 119} 120 121void 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 130size_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 141NetworkResourcesData::NetworkResourcesData() 142 : m_contentSize(0) 143 , m_maximumResourcesContentSize(maximumResourcesContentSize) 144 , m_maximumSingleResourceContentSize(maximumSingleResourceContentSize) 145{ 146} 147 148NetworkResourcesData::~NetworkResourcesData() 149{ 150 clear(); 151} 152 153void NetworkResourcesData::resourceCreated(const String& requestId, const String& loaderId) 154{ 155 ensureNoDataForRequestId(requestId); 156 m_requestIdToResourceDataMap.set(requestId, new ResourceData(requestId, loaderId)); 157} 158 159static 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 177void 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 188void 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 196InspectorPageAgent::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 204void 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 224void 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 242void 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 255void NetworkResourcesData::addResource(const String& requestId, Resource* cachedResource) 256{ 257 ResourceData* resourceData = resourceDataForRequestId(requestId); 258 if (!resourceData) 259 return; 260 resourceData->setResource(cachedResource); 261} 262 263NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId) 264{ 265 return resourceDataForRequestId(requestId); 266} 267 268XHRReplayData* 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 279void 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 296Vector<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 304Vector<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 320void 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 341void NetworkResourcesData::setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize) 342{ 343 clear(); 344 m_maximumResourcesContentSize = maximumResourcesContentSize; 345 m_maximumSingleResourceContentSize = maximumSingleResourceContentSize; 346} 347 348NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId) 349{ 350 if (requestId.isNull()) 351 return 0; 352 return m_requestIdToResourceDataMap.get(requestId); 353} 354 355void 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 366bool 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