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/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