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