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