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