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 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 53 void XHRReplayData::addHeader(const AtomicString& key, const AtomicString& value) 54 { 55 m_headers.set(key, value); 56 } 57 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 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 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 86 static size_t contentSizeInBytes(const String& content) 87 { 88 return content.isNull() ? 0 : content.impl()->sizeInBytes(); 89 } 90 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 108 unsigned NetworkResourcesData::ResourceData::evictContent() 109 { 110 m_isContentEvicted = true; 111 return removeContent(); 112 } 113 114 size_t NetworkResourcesData::ResourceData::dataLength() const 115 { 116 return m_dataBuffer ? m_dataBuffer->size() : 0; 117 } 118 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 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 139 NetworkResourcesData::NetworkResourcesData() 140 : m_contentSize(0) 141 , m_maximumResourcesContentSize(maximumResourcesContentSize) 142 , m_maximumSingleResourceContentSize(maximumSingleResourceContentSize) 143 { 144 } 145 146 NetworkResourcesData::~NetworkResourcesData() 147 { 148 clear(); 149 } 150 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 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 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 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 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 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 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 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 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 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 267 NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId) 268 { 269 return resourceDataForRequestId(requestId); 270 } 271 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 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 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 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 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 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 357 void NetworkResourcesData::setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize) 358 { 359 clear(); 360 m_maximumResourcesContentSize = maximumResourcesContentSize; 361 m_maximumSingleResourceContentSize = maximumSingleResourceContentSize; 362 } 363 364 NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId) 365 { 366 if (requestId.isNull()) 367 return 0; 368 return m_requestIdToResourceDataMap.get(requestId); 369 } 370 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 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