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  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/inspector/InspectorResourceAgent.h"
     33 
     34 #include "bindings/v8/ExceptionStatePlaceholder.h"
     35 #include "bindings/v8/ScriptCallStackFactory.h"
     36 #include "core/FetchInitiatorTypeNames.h"
     37 #include "core/dom/Document.h"
     38 #include "core/dom/ScriptableDocumentParser.h"
     39 #include "core/fetch/FetchInitiatorInfo.h"
     40 #include "core/fetch/MemoryCache.h"
     41 #include "core/fetch/Resource.h"
     42 #include "core/fetch/ResourceFetcher.h"
     43 #include "core/fetch/ResourceLoader.h"
     44 #include "core/frame/LocalFrame.h"
     45 #include "core/inspector/IdentifiersFactory.h"
     46 #include "core/inspector/InspectorOverlay.h"
     47 #include "core/inspector/InspectorPageAgent.h"
     48 #include "core/inspector/InspectorState.h"
     49 #include "core/inspector/InstrumentingAgents.h"
     50 #include "core/inspector/NetworkResourcesData.h"
     51 #include "core/inspector/ScriptCallStack.h"
     52 #include "core/loader/DocumentLoader.h"
     53 #include "core/loader/DocumentThreadableLoader.h"
     54 #include "core/loader/FrameLoader.h"
     55 #include "core/loader/ThreadableLoader.h"
     56 #include "core/loader/ThreadableLoaderClient.h"
     57 #include "core/page/Page.h"
     58 #include "core/xml/XMLHttpRequest.h"
     59 #include "platform/JSONValues.h"
     60 #include "platform/network/HTTPHeaderMap.h"
     61 #include "platform/network/ResourceError.h"
     62 #include "platform/network/ResourceRequest.h"
     63 #include "platform/network/ResourceResponse.h"
     64 #include "platform/network/WebSocketHandshakeRequest.h"
     65 #include "platform/network/WebSocketHandshakeResponse.h"
     66 #include "platform/weborigin/KURL.h"
     67 #include "wtf/CurrentTime.h"
     68 #include "wtf/RefPtr.h"
     69 
     70 typedef WebCore::InspectorBackendDispatcher::NetworkCommandHandler::LoadResourceForFrontendCallback LoadResourceForFrontendCallback;
     71 
     72 namespace WebCore {
     73 
     74 namespace ResourceAgentState {
     75 static const char resourceAgentEnabled[] = "resourceAgentEnabled";
     76 static const char extraRequestHeaders[] = "extraRequestHeaders";
     77 static const char cacheDisabled[] = "cacheDisabled";
     78 static const char userAgentOverride[] = "userAgentOverride";
     79 }
     80 
     81 namespace {
     82 
     83 // Keep in sync with kDevToolsRequestInitiator defined in devtools_network_controller.cc
     84 const char kDevToolsRequestInitiator[] = "X-DevTools-Request-Initiator";
     85 const char kDevToolsEmulateNetworkConditionsClientId[] = "X-DevTools-Emulate-Network-Conditions-Client-Id";
     86 
     87 static PassRefPtr<JSONObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
     88 {
     89     RefPtr<JSONObject> headersObject = JSONObject::create();
     90     HTTPHeaderMap::const_iterator end = headers.end();
     91     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it)
     92         headersObject->setString(it->key.string(), it->value);
     93     return headersObject;
     94 }
     95 
     96 class InspectorThreadableLoaderClient FINAL : public ThreadableLoaderClient {
     97     WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
     98 public:
     99     InspectorThreadableLoaderClient(PassRefPtr<LoadResourceForFrontendCallback> callback)
    100         : m_callback(callback)
    101         , m_statusCode(0) { }
    102 
    103     virtual ~InspectorThreadableLoaderClient() { }
    104 
    105     virtual void didReceiveResponse(unsigned long identifier, const ResourceResponse& response) OVERRIDE
    106     {
    107         WTF::TextEncoding textEncoding(response.textEncodingName());
    108         bool useDetector = false;
    109         if (!textEncoding.isValid()) {
    110             textEncoding = UTF8Encoding();
    111             useDetector = true;
    112         }
    113         m_decoder = TextResourceDecoder::create("text/plain", textEncoding, useDetector);
    114         m_statusCode = response.httpStatusCode();
    115         m_responseHeaders = response.httpHeaderFields();
    116     }
    117 
    118     virtual void didReceiveData(const char* data, int dataLength) OVERRIDE
    119     {
    120         if (!dataLength)
    121             return;
    122 
    123         if (dataLength == -1)
    124             dataLength = strlen(data);
    125 
    126         m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, dataLength));
    127     }
    128 
    129     virtual void didFinishLoading(unsigned long /*identifier*/, double /*finishTime*/) OVERRIDE
    130     {
    131         if (m_decoder)
    132             m_responseText = m_responseText.concatenateWith(m_decoder->flush());
    133         m_callback->sendSuccess(m_statusCode, buildObjectForHeaders(m_responseHeaders), m_responseText.flattenToString());
    134         dispose();
    135     }
    136 
    137     virtual void didFail(const ResourceError&) OVERRIDE
    138     {
    139         m_callback->sendFailure("Loading resource for inspector failed");
    140         dispose();
    141     }
    142 
    143     virtual void didFailRedirectCheck() OVERRIDE
    144     {
    145         m_callback->sendFailure("Loading resource for inspector failed redirect check");
    146         dispose();
    147     }
    148 
    149     void didFailLoaderCreation()
    150     {
    151         m_callback->sendFailure("Couldn't create a loader");
    152         dispose();
    153     }
    154 
    155     void setLoader(PassRefPtr<ThreadableLoader> loader)
    156     {
    157         m_loader = loader;
    158     }
    159 
    160 private:
    161     void dispose()
    162     {
    163         m_loader = nullptr;
    164         delete this;
    165     }
    166 
    167     RefPtr<LoadResourceForFrontendCallback> m_callback;
    168     RefPtr<ThreadableLoader> m_loader;
    169     OwnPtr<TextResourceDecoder> m_decoder;
    170     ScriptString m_responseText;
    171     int m_statusCode;
    172     HTTPHeaderMap m_responseHeaders;
    173 };
    174 
    175 KURL urlWithoutFragment(const KURL& url)
    176 {
    177     KURL result = url;
    178     result.removeFragmentIdentifier();
    179     return result;
    180 }
    181 
    182 } // namespace
    183 
    184 void InspectorResourceAgent::setFrontend(InspectorFrontend* frontend)
    185 {
    186     m_frontend = frontend->network();
    187 }
    188 
    189 void InspectorResourceAgent::clearFrontend()
    190 {
    191     m_frontend = 0;
    192     ErrorString error;
    193     disable(&error);
    194 }
    195 
    196 void InspectorResourceAgent::restore()
    197 {
    198     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled))
    199         enable();
    200 }
    201 
    202 static PassRefPtr<TypeBuilder::Network::ResourceTiming> buildObjectForTiming(const ResourceLoadTiming& timing, DocumentLoader* loader)
    203 {
    204     return TypeBuilder::Network::ResourceTiming::create()
    205         .setRequestTime(loader->timing()->monotonicTimeToPseudoWallTime(timing.requestTime))
    206         .setProxyStart(timing.calculateMillisecondDelta(timing.proxyStart))
    207         .setProxyEnd(timing.calculateMillisecondDelta(timing.proxyEnd))
    208         .setDnsStart(timing.calculateMillisecondDelta(timing.dnsStart))
    209         .setDnsEnd(timing.calculateMillisecondDelta(timing.dnsEnd))
    210         .setConnectStart(timing.calculateMillisecondDelta(timing.connectStart))
    211         .setConnectEnd(timing.calculateMillisecondDelta(timing.connectEnd))
    212         .setSslStart(timing.calculateMillisecondDelta(timing.sslStart))
    213         .setSslEnd(timing.calculateMillisecondDelta(timing.sslEnd))
    214         .setSendStart(timing.calculateMillisecondDelta(timing.sendStart))
    215         .setSendEnd(timing.calculateMillisecondDelta(timing.sendEnd))
    216         .setReceiveHeadersEnd(timing.calculateMillisecondDelta(timing.receiveHeadersEnd))
    217         .release();
    218 }
    219 
    220 static PassRefPtr<TypeBuilder::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request)
    221 {
    222     RefPtr<TypeBuilder::Network::Request> requestObject = TypeBuilder::Network::Request::create()
    223         .setUrl(urlWithoutFragment(request.url()).string())
    224         .setMethod(request.httpMethod())
    225         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()));
    226     if (request.httpBody() && !request.httpBody()->isEmpty()) {
    227         Vector<char> bytes;
    228         request.httpBody()->flatten(bytes);
    229         requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size()));
    230     }
    231     return requestObject;
    232 }
    233 
    234 static PassRefPtr<TypeBuilder::Network::Response> buildObjectForResourceResponse(const ResourceResponse& response, DocumentLoader* loader)
    235 {
    236     if (response.isNull())
    237         return nullptr;
    238 
    239     double status;
    240     String statusText;
    241     if (response.resourceLoadInfo() && response.resourceLoadInfo()->httpStatusCode) {
    242         status = response.resourceLoadInfo()->httpStatusCode;
    243         statusText = response.resourceLoadInfo()->httpStatusText;
    244     } else {
    245         status = response.httpStatusCode();
    246         statusText = response.httpStatusText();
    247     }
    248     RefPtr<JSONObject> headers;
    249     if (response.resourceLoadInfo() && response.resourceLoadInfo()->responseHeaders.size())
    250         headers = buildObjectForHeaders(response.resourceLoadInfo()->responseHeaders);
    251     else
    252         headers = buildObjectForHeaders(response.httpHeaderFields());
    253 
    254     int64_t encodedDataLength = response.resourceLoadInfo() ? response.resourceLoadInfo()->encodedDataLength : -1;
    255 
    256     RefPtr<TypeBuilder::Network::Response> responseObject = TypeBuilder::Network::Response::create()
    257         .setUrl(urlWithoutFragment(response.url()).string())
    258         .setStatus(status)
    259         .setStatusText(statusText)
    260         .setHeaders(headers)
    261         .setMimeType(response.mimeType())
    262         .setConnectionReused(response.connectionReused())
    263         .setConnectionId(response.connectionID())
    264         .setEncodedDataLength(encodedDataLength);
    265 
    266     responseObject->setFromDiskCache(response.wasCached());
    267     if (response.resourceLoadTiming())
    268         responseObject->setTiming(buildObjectForTiming(*response.resourceLoadTiming(), loader));
    269 
    270     if (response.resourceLoadInfo()) {
    271         if (!response.resourceLoadInfo()->responseHeadersText.isEmpty())
    272             responseObject->setHeadersText(response.resourceLoadInfo()->responseHeadersText);
    273         if (response.resourceLoadInfo()->requestHeaders.size())
    274             responseObject->setRequestHeaders(buildObjectForHeaders(response.resourceLoadInfo()->requestHeaders));
    275         if (!response.resourceLoadInfo()->requestHeadersText.isEmpty())
    276             responseObject->setRequestHeadersText(response.resourceLoadInfo()->requestHeadersText);
    277     }
    278 
    279     AtomicString remoteIPAddress = response.remoteIPAddress();
    280     if (!remoteIPAddress.isEmpty()) {
    281         responseObject->setRemoteIPAddress(remoteIPAddress);
    282         responseObject->setRemotePort(response.remotePort());
    283     }
    284 
    285     return responseObject;
    286 }
    287 
    288 InspectorResourceAgent::~InspectorResourceAgent()
    289 {
    290     if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled)) {
    291         ErrorString error;
    292         disable(&error);
    293     }
    294     ASSERT(!m_instrumentingAgents->inspectorResourceAgent());
    295 }
    296 
    297 void InspectorResourceAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo)
    298 {
    299     // Ignore the request initiated internally.
    300     if (initiatorInfo.name == FetchInitiatorTypeNames::internal)
    301         return;
    302 
    303     if (!m_hostId.isEmpty())
    304         request.addHTTPHeaderField(kDevToolsEmulateNetworkConditionsClientId, AtomicString(m_hostId));
    305 
    306     String requestId = IdentifiersFactory::requestId(identifier);
    307     m_resourcesData->resourceCreated(requestId, m_pageAgent->loaderId(loader));
    308 
    309     RefPtr<JSONObject> headers = m_state->getObject(ResourceAgentState::extraRequestHeaders);
    310 
    311     if (headers) {
    312         JSONObject::const_iterator end = headers->end();
    313         for (JSONObject::const_iterator it = headers->begin(); it != end; ++it) {
    314             String value;
    315             if (it->value->asString(&value))
    316                 request.setHTTPHeaderField(AtomicString(it->key), AtomicString(value));
    317         }
    318     }
    319 
    320     request.setReportRawHeaders(true);
    321 
    322     if (m_state->getBoolean(ResourceAgentState::cacheDisabled))
    323         request.setCachePolicy(ReloadBypassingCache);
    324 
    325     String frameId = m_pageAgent->frameId(loader->frame());
    326 
    327     RefPtr<TypeBuilder::Network::Initiator> initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : 0, initiatorInfo);
    328     if (initiatorInfo.name == FetchInitiatorTypeNames::document) {
    329         FrameNavigationInitiatorMap::iterator it = m_frameNavigationInitiatorMap.find(frameId);
    330         if (it != m_frameNavigationInitiatorMap.end())
    331             initiatorObject = it->value;
    332     }
    333 
    334     m_frontend->requestWillBeSent(requestId, frameId, m_pageAgent->loaderId(loader), urlWithoutFragment(loader->url()).string(), buildObjectForResourceRequest(request), currentTime(), initiatorObject, buildObjectForResourceResponse(redirectResponse, loader));
    335 }
    336 
    337 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
    338 {
    339     m_frontend->requestServedFromCache(IdentifiersFactory::requestId(identifier));
    340 }
    341 
    342 bool isResponseEmpty(PassRefPtr<TypeBuilder::Network::Response> response)
    343 {
    344     if (!response)
    345         return true;
    346 
    347     RefPtr<JSONValue> status = response->get("status");
    348     RefPtr<JSONValue> mimeType = response->get("mimeType");
    349     RefPtr<JSONObject> headers = response->getObject("headers");
    350 
    351     return !status && !mimeType && (!headers || !headers->size());
    352 }
    353 
    354 void InspectorResourceAgent::didReceiveResourceResponse(LocalFrame* frame, unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
    355 {
    356     if (!loader)
    357         return;
    358 
    359     String requestId = IdentifiersFactory::requestId(identifier);
    360     RefPtr<TypeBuilder::Network::Response> resourceResponse = buildObjectForResourceResponse(response, loader);
    361 
    362     bool isNotModified = response.httpStatusCode() == 304;
    363 
    364     Resource* cachedResource = 0;
    365     if (resourceLoader && !isNotModified)
    366         cachedResource = resourceLoader->cachedResource();
    367     if (!cachedResource || cachedResource->type() == Resource::MainResource)
    368         cachedResource = InspectorPageAgent::cachedResource(loader->frame(), response.url());
    369 
    370     if (cachedResource) {
    371         // Use mime type from cached resource in case the one in response is empty.
    372         if (resourceResponse && response.mimeType().isEmpty())
    373             resourceResponse->setString(TypeBuilder::Network::Response::MimeType, cachedResource->response().mimeType());
    374         m_resourcesData->addResource(requestId, cachedResource);
    375     }
    376 
    377     InspectorPageAgent::ResourceType type = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : InspectorPageAgent::OtherResource;
    378     // Workaround for worker scripts that use RawResources for loading.
    379     if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::ScriptResource)
    380         type = InspectorPageAgent::ScriptResource;
    381     // Workaround for background: url() in inline style.
    382     if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && !loader->isCommitted())
    383         type = InspectorPageAgent::DocumentResource;
    384 
    385     m_resourcesData->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), response);
    386     m_resourcesData->setResourceType(requestId, type);
    387 
    388     if (!isResponseEmpty(resourceResponse))
    389         m_frontend->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), currentTime(), InspectorPageAgent::resourceTypeJson(type), resourceResponse);
    390     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
    391     // as there will be no calls to didReceiveData from the network stack.
    392     if (isNotModified && cachedResource && cachedResource->encodedSize())
    393         didReceiveData(frame, identifier, 0, cachedResource->encodedSize(), 0);
    394 }
    395 
    396 static bool isErrorStatusCode(int statusCode)
    397 {
    398     return statusCode >= 400;
    399 }
    400 
    401 void InspectorResourceAgent::didReceiveData(LocalFrame*, unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
    402 {
    403     String requestId = IdentifiersFactory::requestId(identifier);
    404 
    405     if (data) {
    406         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
    407         if (resourceData && (!resourceData->cachedResource() || resourceData->cachedResource()->dataBufferingPolicy() == DoNotBufferData || isErrorStatusCode(resourceData->httpStatusCode())))
    408             m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
    409     }
    410 
    411     m_frontend->dataReceived(requestId, currentTime(), dataLength, encodedDataLength);
    412 }
    413 
    414 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, double monotonicFinishTime, int64_t encodedDataLength)
    415 {
    416     double finishTime = 0.0;
    417     // FIXME: Expose all of the timing details to inspector and have it calculate finishTime.
    418     if (monotonicFinishTime)
    419         finishTime = loader->timing()->monotonicTimeToPseudoWallTime(monotonicFinishTime);
    420 
    421     String requestId = IdentifiersFactory::requestId(identifier);
    422     m_resourcesData->maybeDecodeDataToContent(requestId);
    423     if (!finishTime)
    424         finishTime = currentTime();
    425     m_frontend->loadingFinished(requestId, finishTime, encodedDataLength);
    426 }
    427 
    428 void InspectorResourceAgent::didReceiveCORSRedirectResponse(LocalFrame* frame, unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
    429 {
    430     // Update the response and finish loading
    431     didReceiveResourceResponse(frame, identifier, loader, response, resourceLoader);
    432     didFinishLoading(identifier, loader, 0, blink::WebURLLoaderClient::kUnknownEncodedDataLength);
    433 }
    434 
    435 void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
    436 {
    437     String requestId = IdentifiersFactory::requestId(identifier);
    438     bool canceled = error.isCancellation();
    439     m_frontend->loadingFailed(requestId, currentTime(), InspectorPageAgent::resourceTypeJson(m_resourcesData->resourceType(requestId)), error.localizedDescription(), canceled ? &canceled : 0);
    440 }
    441 
    442 void InspectorResourceAgent::scriptImported(unsigned long identifier, const String& sourceString)
    443 {
    444     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
    445 }
    446 
    447 void InspectorResourceAgent::didReceiveScriptResponse(unsigned long identifier)
    448 {
    449     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
    450 }
    451 
    452 void InspectorResourceAgent::documentThreadableLoaderStartedLoadingForClient(unsigned long identifier, ThreadableLoaderClient* client)
    453 {
    454     if (!client)
    455         return;
    456 
    457     PendingXHRReplayDataMap::iterator it = m_pendingXHRReplayData.find(client);
    458     if (it == m_pendingXHRReplayData.end())
    459         return;
    460 
    461     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource);
    462     XHRReplayData* xhrReplayData = it->value.get();
    463     String requestId = IdentifiersFactory::requestId(identifier);
    464     m_resourcesData->setXHRReplayData(requestId, xhrReplayData);
    465 }
    466 
    467 void InspectorResourceAgent::willLoadXHR(XMLHttpRequest* xhr, ThreadableLoaderClient* client, const AtomicString& method, const KURL& url, bool async, FormData* formData, const HTTPHeaderMap& headers, bool includeCredentials)
    468 {
    469     ASSERT(xhr);
    470     RefPtr<XHRReplayData> xhrReplayData = XHRReplayData::create(xhr->executionContext(), method, urlWithoutFragment(url), async, formData, includeCredentials);
    471     HTTPHeaderMap::const_iterator end = headers.end();
    472     for (HTTPHeaderMap::const_iterator it = headers.begin(); it!= end; ++it)
    473         xhrReplayData->addHeader(it->key, it->value);
    474     m_pendingXHRReplayData.set(client, xhrReplayData);
    475 }
    476 
    477 void InspectorResourceAgent::didFailXHRLoading(XMLHttpRequest*, ThreadableLoaderClient* client)
    478 {
    479     m_pendingXHRReplayData.remove(client);
    480 }
    481 
    482 void InspectorResourceAgent::didFinishXHRLoading(XMLHttpRequest*, ThreadableLoaderClient* client, unsigned long identifier, ScriptString sourceString, const AtomicString&, const String&, const String&, unsigned)
    483 {
    484     m_pendingXHRReplayData.remove(client);
    485 }
    486 
    487 void InspectorResourceAgent::willDestroyResource(Resource* cachedResource)
    488 {
    489     Vector<String> requestIds = m_resourcesData->removeResource(cachedResource);
    490     if (!requestIds.size())
    491         return;
    492 
    493     String content;
    494     bool base64Encoded;
    495     if (!InspectorPageAgent::cachedResourceContent(cachedResource, &content, &base64Encoded))
    496         return;
    497     Vector<String>::iterator end = requestIds.end();
    498     for (Vector<String>::iterator it = requestIds.begin(); it != end; ++it)
    499         m_resourcesData->setResourceContent(*it, content, base64Encoded);
    500 }
    501 
    502 void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
    503 {
    504     String userAgentOverride = m_state->getString(ResourceAgentState::userAgentOverride);
    505     if (!userAgentOverride.isEmpty())
    506         *userAgent = userAgentOverride;
    507 }
    508 
    509 void InspectorResourceAgent::willRecalculateStyle(Document*)
    510 {
    511     m_isRecalculatingStyle = true;
    512 }
    513 
    514 void InspectorResourceAgent::didRecalculateStyle(int)
    515 {
    516     m_isRecalculatingStyle = false;
    517     m_styleRecalculationInitiator = nullptr;
    518 }
    519 
    520 void InspectorResourceAgent::didScheduleStyleRecalculation(Document* document)
    521 {
    522     if (!m_styleRecalculationInitiator)
    523         m_styleRecalculationInitiator = buildInitiatorObject(document, FetchInitiatorInfo());
    524 }
    525 
    526 PassRefPtr<TypeBuilder::Network::Initiator> InspectorResourceAgent::buildInitiatorObject(Document* document, const FetchInitiatorInfo& initiatorInfo)
    527 {
    528     RefPtrWillBeRawPtr<ScriptCallStack> stackTrace = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true);
    529     if (stackTrace && stackTrace->size() > 0) {
    530         RefPtr<TypeBuilder::Network::Initiator> initiatorObject = TypeBuilder::Network::Initiator::create()
    531             .setType(TypeBuilder::Network::Initiator::Type::Script);
    532         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
    533         return initiatorObject;
    534     }
    535 
    536     if (document && document->scriptableDocumentParser()) {
    537         RefPtr<TypeBuilder::Network::Initiator> initiatorObject = TypeBuilder::Network::Initiator::create()
    538             .setType(TypeBuilder::Network::Initiator::Type::Parser);
    539         initiatorObject->setUrl(urlWithoutFragment(document->url()).string());
    540         if (TextPosition::belowRangePosition() != initiatorInfo.position)
    541             initiatorObject->setLineNumber(initiatorInfo.position.m_line.oneBasedInt());
    542         else
    543             initiatorObject->setLineNumber(document->scriptableDocumentParser()->lineNumber().oneBasedInt());
    544         return initiatorObject;
    545     }
    546 
    547     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
    548         return m_styleRecalculationInitiator;
    549 
    550     return TypeBuilder::Network::Initiator::create()
    551         .setType(TypeBuilder::Network::Initiator::Type::Other)
    552         .release();
    553 }
    554 
    555 void InspectorResourceAgent::didCreateWebSocket(Document*, unsigned long identifier, const KURL& requestURL, const String&)
    556 {
    557     m_frontend->webSocketCreated(IdentifiersFactory::requestId(identifier), urlWithoutFragment(requestURL).string());
    558 }
    559 
    560 void InspectorResourceAgent::willSendWebSocketHandshakeRequest(Document*, unsigned long identifier, const WebSocketHandshakeRequest* request)
    561 {
    562     ASSERT(request);
    563     RefPtr<TypeBuilder::Network::WebSocketRequest> requestObject = TypeBuilder::Network::WebSocketRequest::create()
    564         .setHeaders(buildObjectForHeaders(request->headerFields()));
    565     m_frontend->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), currentTime(), requestObject);
    566 }
    567 
    568 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(Document*, unsigned long identifier, const WebSocketHandshakeRequest* request, const WebSocketHandshakeResponse* response)
    569 {
    570     ASSERT(response);
    571     RefPtr<TypeBuilder::Network::WebSocketResponse> responseObject = TypeBuilder::Network::WebSocketResponse::create()
    572         .setStatus(response->statusCode())
    573         .setStatusText(response->statusText())
    574         .setHeaders(buildObjectForHeaders(response->headerFields()));
    575 
    576     if (!response->headersText().isEmpty())
    577         responseObject->setHeadersText(response->headersText());
    578     if (request) {
    579         responseObject->setRequestHeaders(buildObjectForHeaders(request->headerFields()));
    580         if (!request->headersText().isEmpty())
    581             responseObject->setRequestHeadersText(request->headersText());
    582     }
    583     m_frontend->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), currentTime(), responseObject);
    584 }
    585 
    586 void InspectorResourceAgent::didCloseWebSocket(Document*, unsigned long identifier)
    587 {
    588     m_frontend->webSocketClosed(IdentifiersFactory::requestId(identifier), currentTime());
    589 }
    590 
    591 void InspectorResourceAgent::didReceiveWebSocketFrame(unsigned long identifier, int opCode, bool masked, const char* payload, size_t payloadLength)
    592 {
    593     RefPtr<TypeBuilder::Network::WebSocketFrame> frameObject = TypeBuilder::Network::WebSocketFrame::create()
    594         .setOpcode(opCode)
    595         .setMask(masked)
    596         .setPayloadData(String(payload, payloadLength));
    597     m_frontend->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), currentTime(), frameObject);
    598 }
    599 
    600 void InspectorResourceAgent::didSendWebSocketFrame(unsigned long identifier, int opCode, bool masked, const char* payload, size_t payloadLength)
    601 {
    602     RefPtr<TypeBuilder::Network::WebSocketFrame> frameObject = TypeBuilder::Network::WebSocketFrame::create()
    603         .setOpcode(opCode)
    604         .setMask(masked)
    605         .setPayloadData(String(payload, payloadLength));
    606     m_frontend->webSocketFrameSent(IdentifiersFactory::requestId(identifier), currentTime(), frameObject);
    607 }
    608 
    609 void InspectorResourceAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
    610 {
    611     m_frontend->webSocketFrameError(IdentifiersFactory::requestId(identifier), currentTime(), errorMessage);
    612 }
    613 
    614 // called from Internals for layout test purposes.
    615 void InspectorResourceAgent::setResourcesDataSizeLimitsFromInternals(int maximumResourcesContentSize, int maximumSingleResourceContentSize)
    616 {
    617     m_resourcesData->setResourcesDataSizeLimits(maximumResourcesContentSize, maximumSingleResourceContentSize);
    618 }
    619 
    620 void InspectorResourceAgent::enable(ErrorString*)
    621 {
    622     enable();
    623 }
    624 
    625 void InspectorResourceAgent::enable()
    626 {
    627     if (!m_frontend)
    628         return;
    629     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
    630     m_instrumentingAgents->setInspectorResourceAgent(this);
    631 }
    632 
    633 void InspectorResourceAgent::disable(ErrorString*)
    634 {
    635     m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, false);
    636     m_state->setString(ResourceAgentState::userAgentOverride, "");
    637     m_instrumentingAgents->setInspectorResourceAgent(0);
    638     m_resourcesData->clear();
    639 }
    640 
    641 void InspectorResourceAgent::setUserAgentOverride(ErrorString*, const String& userAgent)
    642 {
    643     m_state->setString(ResourceAgentState::userAgentOverride, userAgent);
    644 }
    645 
    646 void InspectorResourceAgent::setExtraHTTPHeaders(ErrorString*, const RefPtr<JSONObject>& headers)
    647 {
    648     m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
    649 }
    650 
    651 void InspectorResourceAgent::getResponseBody(ErrorString* errorString, const String& requestId, String* content, bool* base64Encoded)
    652 {
    653     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
    654     if (!resourceData) {
    655         *errorString = "No resource with given identifier found";
    656         return;
    657     }
    658 
    659     if (resourceData->hasContent()) {
    660         *base64Encoded = resourceData->base64Encoded();
    661         *content = resourceData->content();
    662         return;
    663     }
    664 
    665     if (resourceData->isContentEvicted()) {
    666         *errorString = "Request content was evicted from inspector cache";
    667         return;
    668     }
    669 
    670     if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) {
    671         *base64Encoded = false;
    672         if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content))
    673             return;
    674     }
    675 
    676     if (resourceData->cachedResource()) {
    677         if (InspectorPageAgent::cachedResourceContent(resourceData->cachedResource(), content, base64Encoded))
    678             return;
    679     }
    680 
    681     *errorString = "No data found for resource with given identifier";
    682 }
    683 
    684 void InspectorResourceAgent::replayXHR(ErrorString*, const String& requestId)
    685 {
    686     String actualRequestId = requestId;
    687 
    688     XHRReplayData* xhrReplayData = m_resourcesData->xhrReplayData(requestId);
    689     if (!xhrReplayData)
    690         return;
    691 
    692     ExecutionContext* executionContext = xhrReplayData->executionContext();
    693     if (!executionContext) {
    694         m_resourcesData->setXHRReplayData(requestId, 0);
    695         return;
    696     }
    697 
    698     RefPtrWillBeRawPtr<XMLHttpRequest> xhr = XMLHttpRequest::create(executionContext);
    699 
    700     Resource* cachedResource = memoryCache()->resourceForURL(xhrReplayData->url());
    701     if (cachedResource)
    702         memoryCache()->remove(cachedResource);
    703 
    704     xhr->open(xhrReplayData->method(), xhrReplayData->url(), xhrReplayData->async(), IGNORE_EXCEPTION);
    705     HTTPHeaderMap::const_iterator end = xhrReplayData->headers().end();
    706     for (HTTPHeaderMap::const_iterator it = xhrReplayData->headers().begin(); it!= end; ++it)
    707         xhr->setRequestHeader(it->key, it->value, IGNORE_EXCEPTION);
    708     xhr->sendForInspectorXHRReplay(xhrReplayData->formData(), IGNORE_EXCEPTION);
    709 }
    710 
    711 void InspectorResourceAgent::canClearBrowserCache(ErrorString*, bool* result)
    712 {
    713     *result = true;
    714 }
    715 
    716 void InspectorResourceAgent::canClearBrowserCookies(ErrorString*, bool* result)
    717 {
    718     *result = true;
    719 }
    720 
    721 void InspectorResourceAgent::setCacheDisabled(ErrorString*, bool cacheDisabled)
    722 {
    723     m_state->setBoolean(ResourceAgentState::cacheDisabled, cacheDisabled);
    724     if (cacheDisabled)
    725         memoryCache()->evictResources();
    726     for (Frame* frame = m_pageAgent->mainFrame(); frame; frame = frame->tree().traverseNext()) {
    727         if (frame->isLocalFrame())
    728             toLocalFrame(frame)->document()->fetcher()->garbageCollectDocumentResources();
    729     }
    730 }
    731 
    732 void InspectorResourceAgent::loadResourceForFrontend(ErrorString* errorString, const String& frameId, const String& url, const RefPtr<JSONObject>* requestHeaders, PassRefPtr<LoadResourceForFrontendCallback> prpCallback)
    733 {
    734     RefPtr<LoadResourceForFrontendCallback> callback = prpCallback;
    735     LocalFrame* frame = m_pageAgent->assertFrame(errorString, frameId);
    736     if (!frame)
    737         return;
    738 
    739     Document* document = frame->document();
    740     if (!document) {
    741         *errorString = "No Document instance for the specified frame";
    742         return;
    743     }
    744 
    745     ResourceRequest request(url);
    746     request.setHTTPMethod("GET");
    747     request.setCachePolicy(ReloadIgnoringCacheData);
    748     if (requestHeaders) {
    749         for (JSONObject::iterator it = (*requestHeaders)->begin(); it != (*requestHeaders)->end(); ++it) {
    750             String value;
    751             bool success = it->value->asString(&value);
    752             if (!success) {
    753                 *errorString = "Request header \"" + it->key + "\" value is not a string";
    754                 return;
    755             }
    756             request.addHTTPHeaderField(AtomicString(it->key), AtomicString(value));
    757         }
    758     }
    759     request.addHTTPHeaderField(kDevToolsRequestInitiator, "frontend");
    760 
    761     ThreadableLoaderOptions options;
    762     options.crossOriginRequestPolicy = AllowCrossOriginRequests;
    763 
    764     ResourceLoaderOptions resourceLoaderOptions;
    765     resourceLoaderOptions.allowCredentials = AllowStoredCredentials;
    766 
    767     InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback);
    768     RefPtr<DocumentThreadableLoader> loader = DocumentThreadableLoader::create(*document, inspectorThreadableLoaderClient, request, options, resourceLoaderOptions);
    769     if (!loader) {
    770         inspectorThreadableLoaderClient->didFailLoaderCreation();
    771         return;
    772     }
    773     loader->setDefersLoading(false);
    774     if (!callback->isActive())
    775         return;
    776     inspectorThreadableLoaderClient->setLoader(loader.release());
    777 }
    778 
    779 void InspectorResourceAgent::didCommitLoad(LocalFrame* frame, DocumentLoader* loader)
    780 {
    781     if (loader->frame() != frame->page()->mainFrame())
    782         return;
    783 
    784     if (m_state->getBoolean(ResourceAgentState::cacheDisabled))
    785         memoryCache()->evictResources();
    786 
    787     m_resourcesData->clear(m_pageAgent->loaderId(loader));
    788 }
    789 
    790 void InspectorResourceAgent::frameScheduledNavigation(LocalFrame* frame, double)
    791 {
    792     RefPtr<TypeBuilder::Network::Initiator> initiator = buildInitiatorObject(frame->document(), FetchInitiatorInfo());
    793     m_frameNavigationInitiatorMap.set(m_pageAgent->frameId(frame), initiator);
    794 }
    795 
    796 void InspectorResourceAgent::frameClearedScheduledNavigation(LocalFrame* frame)
    797 {
    798     m_frameNavigationInitiatorMap.remove(m_pageAgent->frameId(frame));
    799 }
    800 
    801 void InspectorResourceAgent::setHostId(const String& hostId)
    802 {
    803     m_hostId = hostId;
    804 }
    805 
    806 bool InspectorResourceAgent::fetchResourceContent(LocalFrame* frame, const KURL& url, String* content, bool* base64Encoded)
    807 {
    808     // First try to fetch content from the cached resource.
    809     Resource* cachedResource = frame->document()->fetcher()->cachedResource(url);
    810     if (!cachedResource)
    811         cachedResource = memoryCache()->resourceForURL(url);
    812     if (cachedResource && InspectorPageAgent::cachedResourceContent(cachedResource, content, base64Encoded))
    813         return true;
    814 
    815     // Then fall back to resource data.
    816     Vector<NetworkResourcesData::ResourceData*> resources = m_resourcesData->resources();
    817     for (Vector<NetworkResourcesData::ResourceData*>::iterator it = resources.begin(); it != resources.end(); ++it) {
    818         if ((*it)->url() == url) {
    819             *content = (*it)->content();
    820             *base64Encoded = (*it)->base64Encoded();
    821             return true;
    822         }
    823     }
    824     return false;
    825 }
    826 
    827 InspectorResourceAgent::InspectorResourceAgent(InspectorPageAgent* pageAgent)
    828     : InspectorBaseAgent<InspectorResourceAgent>("Network")
    829     , m_pageAgent(pageAgent)
    830     , m_frontend(0)
    831     , m_resourcesData(adoptPtr(new NetworkResourcesData()))
    832     , m_isRecalculatingStyle(false)
    833 {
    834 }
    835 
    836 } // namespace WebCore
    837