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
72typedef blink::InspectorBackendDispatcher::NetworkCommandHandler::LoadResourceForFrontendCallback LoadResourceForFrontendCallback;
73
74namespace blink {
75
76namespace ResourceAgentState {
77static const char resourceAgentEnabled[] = "resourceAgentEnabled";
78static const char extraRequestHeaders[] = "extraRequestHeaders";
79static const char cacheDisabled[] = "cacheDisabled";
80static const char userAgentOverride[] = "userAgentOverride";
81}
82
83namespace {
84
85// Keep in sync with kDevToolsRequestInitiator defined in devtools_network_controller.cc
86const char kDevToolsRequestInitiator[] = "X-DevTools-Request-Initiator";
87const char kDevToolsEmulateNetworkConditionsClientId[] = "X-DevTools-Emulate-Network-Conditions-Client-Id";
88
89static 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
98class InspectorThreadableLoaderClient FINAL : public ThreadableLoaderClient {
99    WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
100public:
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
162private:
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
177KURL urlWithoutFragment(const KURL& url)
178{
179    KURL result = url;
180    result.removeFragmentIdentifier();
181    return result;
182}
183
184} // namespace
185
186void InspectorResourceAgent::setFrontend(InspectorFrontend* frontend)
187{
188    m_frontend = frontend->network();
189}
190
191void InspectorResourceAgent::clearFrontend()
192{
193    m_frontend = 0;
194    ErrorString error;
195    disable(&error);
196}
197
198void InspectorResourceAgent::restore()
199{
200    if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled))
201        enable();
202}
203
204static 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
225static 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
239static 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
294InspectorResourceAgent::~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
305void 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
316void 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
358void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
359{
360    m_frontend->requestServedFromCache(IdentifiersFactory::requestId(identifier));
361}
362
363bool 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
375void 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
414static bool isErrorStatusCode(int statusCode)
415{
416    return statusCode >= 400;
417}
418
419void 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
432void 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
446void 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
453void 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
460void InspectorResourceAgent::scriptImported(unsigned long identifier, const String& sourceString)
461{
462    m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
463}
464
465void InspectorResourceAgent::didReceiveScriptResponse(unsigned long identifier)
466{
467    m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
468}
469
470void 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
485void 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
495void 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
505void 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
514void 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
522void 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
537void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
538{
539    String userAgentOverride = m_state->getString(ResourceAgentState::userAgentOverride);
540    if (!userAgentOverride.isEmpty())
541        *userAgent = userAgentOverride;
542}
543
544void InspectorResourceAgent::willRecalculateStyle(Document*)
545{
546    m_isRecalculatingStyle = true;
547}
548
549void InspectorResourceAgent::didRecalculateStyle(int)
550{
551    m_isRecalculatingStyle = false;
552    m_styleRecalculationInitiator = nullptr;
553}
554
555void InspectorResourceAgent::didScheduleStyleRecalculation(Document* document)
556{
557    if (!m_styleRecalculationInitiator)
558        m_styleRecalculationInitiator = buildInitiatorObject(document, FetchInitiatorInfo());
559}
560
561PassRefPtr<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
593void InspectorResourceAgent::didCreateWebSocket(Document*, unsigned long identifier, const KURL& requestURL, const String&)
594{
595    m_frontend->webSocketCreated(IdentifiersFactory::requestId(identifier), urlWithoutFragment(requestURL).string());
596}
597
598void 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
606void 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
624void InspectorResourceAgent::didCloseWebSocket(Document*, unsigned long identifier)
625{
626    m_frontend->webSocketClosed(IdentifiersFactory::requestId(identifier), currentTime());
627}
628
629void 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
638void 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
647void 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.
653void InspectorResourceAgent::setResourcesDataSizeLimitsFromInternals(int maximumResourcesContentSize, int maximumSingleResourceContentSize)
654{
655    m_resourcesData->setResourcesDataSizeLimits(maximumResourcesContentSize, maximumSingleResourceContentSize);
656}
657
658void InspectorResourceAgent::enable(ErrorString*)
659{
660    enable();
661}
662
663void InspectorResourceAgent::enable()
664{
665    if (!m_frontend)
666        return;
667    m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
668    m_instrumentingAgents->setInspectorResourceAgent(this);
669}
670
671void 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
679void InspectorResourceAgent::setUserAgentOverride(ErrorString*, const String& userAgent)
680{
681    m_state->setString(ResourceAgentState::userAgentOverride, userAgent);
682}
683
684void InspectorResourceAgent::setExtraHTTPHeaders(ErrorString*, const RefPtr<JSONObject>& headers)
685{
686    m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
687}
688
689void 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
722void 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
751void InspectorResourceAgent::canClearBrowserCache(ErrorString*, bool* result)
752{
753    *result = true;
754}
755
756void InspectorResourceAgent::canClearBrowserCookies(ErrorString*, bool* result)
757{
758    *result = true;
759}
760
761void 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
772void InspectorResourceAgent::emulateNetworkConditions(ErrorString*, bool, double, double, double)
773{
774}
775
776void 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
824void 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
835void 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
841void InspectorResourceAgent::frameClearedScheduledNavigation(LocalFrame* frame)
842{
843    m_frameNavigationInitiatorMap.remove(m_pageAgent->frameId(frame));
844}
845
846void InspectorResourceAgent::setHostId(const String& hostId)
847{
848    m_hostId = hostId;
849}
850
851bool 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
872void InspectorResourceAgent::removeFinishedReplayXHRFired(Timer<InspectorResourceAgent>*)
873{
874    m_replayXHRsToBeDeleted.clear();
875}
876
877InspectorResourceAgent::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
887bool InspectorResourceAgent::shouldForceCORSPreflight()
888{
889    return m_state->getBoolean(ResourceAgentState::cacheDisabled);
890}
891
892} // namespace blink
893