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