Home | History | Annotate | Download | only in inspector
      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