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 WebCore { 47 48 49 PassRefPtr<XHRReplayData> XHRReplayData::create(ExecutionContext* executionContext, const AtomicString& method, const KURL& url, bool async, PassRefPtr<FormData> formData, bool includeCredentials) 50 { 51 return adoptRef(new XHRReplayData(executionContext, method, url, async, formData, includeCredentials)); 52 } 53 54 void XHRReplayData::addHeader(const AtomicString& key, const AtomicString& value) 55 { 56 m_headers.set(key, value); 57 } 58 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 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 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 88 static size_t contentSizeInBytes(const String& content) 89 { 90 return content.isNull() ? 0 : content.impl()->sizeInBytes(); 91 } 92 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 110 unsigned NetworkResourcesData::ResourceData::evictContent() 111 { 112 m_isContentEvicted = true; 113 return removeContent(); 114 } 115 116 size_t NetworkResourcesData::ResourceData::dataLength() const 117 { 118 return m_dataBuffer ? m_dataBuffer->size() : 0; 119 } 120 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 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 141 NetworkResourcesData::NetworkResourcesData() 142 : m_contentSize(0) 143 , m_maximumResourcesContentSize(maximumResourcesContentSize) 144 , m_maximumSingleResourceContentSize(maximumSingleResourceContentSize) 145 { 146 } 147 148 NetworkResourcesData::~NetworkResourcesData() 149 { 150 clear(); 151 } 152 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 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 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 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 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 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 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 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 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 263 NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId) 264 { 265 return resourceDataForRequestId(requestId); 266 } 267 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 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 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 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 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 341 void NetworkResourcesData::setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize) 342 { 343 clear(); 344 m_maximumResourcesContentSize = maximumResourcesContentSize; 345 m_maximumSingleResourceContentSize = maximumSingleResourceContentSize; 346 } 347 348 NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId) 349 { 350 if (requestId.isNull()) 351 return 0; 352 return m_requestIdToResourceDataMap.get(requestId); 353 } 354 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 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 WebCore 381 382