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
70typedef WebCore::InspectorBackendDispatcher::NetworkCommandHandler::LoadResourceForFrontendCallback LoadResourceForFrontendCallback;
71
72namespace WebCore {
73
74namespace ResourceAgentState {
75static const char resourceAgentEnabled[] = "resourceAgentEnabled";
76static const char extraRequestHeaders[] = "extraRequestHeaders";
77static const char cacheDisabled[] = "cacheDisabled";
78static const char userAgentOverride[] = "userAgentOverride";
79}
80
81namespace {
82
83// Keep in sync with kDevToolsRequestInitiator defined in devtools_network_controller.cc
84const char kDevToolsRequestInitiator[] = "X-DevTools-Request-Initiator";
85const char kDevToolsEmulateNetworkConditionsClientId[] = "X-DevTools-Emulate-Network-Conditions-Client-Id";
86
87static 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
96class InspectorThreadableLoaderClient FINAL : public ThreadableLoaderClient {
97    WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
98public:
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
160private:
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
175KURL urlWithoutFragment(const KURL& url)
176{
177    KURL result = url;
178    result.removeFragmentIdentifier();
179    return result;
180}
181
182} // namespace
183
184void InspectorResourceAgent::setFrontend(InspectorFrontend* frontend)
185{
186    m_frontend = frontend->network();
187}
188
189void InspectorResourceAgent::clearFrontend()
190{
191    m_frontend = 0;
192    ErrorString error;
193    disable(&error);
194}
195
196void InspectorResourceAgent::restore()
197{
198    if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled))
199        enable();
200}
201
202static 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
220static 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
234static 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
288InspectorResourceAgent::~InspectorResourceAgent()
289{
290    if (m_state->getBoolean(ResourceAgentState::resourceAgentEnabled)) {
291        ErrorString error;
292        disable(&error);
293    }
294    ASSERT(!m_instrumentingAgents->inspectorResourceAgent());
295}
296
297void 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
337void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
338{
339    m_frontend->requestServedFromCache(IdentifiersFactory::requestId(identifier));
340}
341
342bool 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
354void 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
396static bool isErrorStatusCode(int statusCode)
397{
398    return statusCode >= 400;
399}
400
401void 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
414void 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
428void 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
435void 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
442void InspectorResourceAgent::scriptImported(unsigned long identifier, const String& sourceString)
443{
444    m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
445}
446
447void InspectorResourceAgent::didReceiveScriptResponse(unsigned long identifier)
448{
449    m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
450}
451
452void 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
467void 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
477void InspectorResourceAgent::didFailXHRLoading(XMLHttpRequest*, ThreadableLoaderClient* client)
478{
479    m_pendingXHRReplayData.remove(client);
480}
481
482void 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
487void 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
502void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
503{
504    String userAgentOverride = m_state->getString(ResourceAgentState::userAgentOverride);
505    if (!userAgentOverride.isEmpty())
506        *userAgent = userAgentOverride;
507}
508
509void InspectorResourceAgent::willRecalculateStyle(Document*)
510{
511    m_isRecalculatingStyle = true;
512}
513
514void InspectorResourceAgent::didRecalculateStyle(int)
515{
516    m_isRecalculatingStyle = false;
517    m_styleRecalculationInitiator = nullptr;
518}
519
520void InspectorResourceAgent::didScheduleStyleRecalculation(Document* document)
521{
522    if (!m_styleRecalculationInitiator)
523        m_styleRecalculationInitiator = buildInitiatorObject(document, FetchInitiatorInfo());
524}
525
526PassRefPtr<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
555void InspectorResourceAgent::didCreateWebSocket(Document*, unsigned long identifier, const KURL& requestURL, const String&)
556{
557    m_frontend->webSocketCreated(IdentifiersFactory::requestId(identifier), urlWithoutFragment(requestURL).string());
558}
559
560void 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
568void 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
586void InspectorResourceAgent::didCloseWebSocket(Document*, unsigned long identifier)
587{
588    m_frontend->webSocketClosed(IdentifiersFactory::requestId(identifier), currentTime());
589}
590
591void 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
600void 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
609void 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.
615void InspectorResourceAgent::setResourcesDataSizeLimitsFromInternals(int maximumResourcesContentSize, int maximumSingleResourceContentSize)
616{
617    m_resourcesData->setResourcesDataSizeLimits(maximumResourcesContentSize, maximumSingleResourceContentSize);
618}
619
620void InspectorResourceAgent::enable(ErrorString*)
621{
622    enable();
623}
624
625void InspectorResourceAgent::enable()
626{
627    if (!m_frontend)
628        return;
629    m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, true);
630    m_instrumentingAgents->setInspectorResourceAgent(this);
631}
632
633void 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
641void InspectorResourceAgent::setUserAgentOverride(ErrorString*, const String& userAgent)
642{
643    m_state->setString(ResourceAgentState::userAgentOverride, userAgent);
644}
645
646void InspectorResourceAgent::setExtraHTTPHeaders(ErrorString*, const RefPtr<JSONObject>& headers)
647{
648    m_state->setObject(ResourceAgentState::extraRequestHeaders, headers);
649}
650
651void 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
684void 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
711void InspectorResourceAgent::canClearBrowserCache(ErrorString*, bool* result)
712{
713    *result = true;
714}
715
716void InspectorResourceAgent::canClearBrowserCookies(ErrorString*, bool* result)
717{
718    *result = true;
719}
720
721void 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
732void 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
779void 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
790void 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
796void InspectorResourceAgent::frameClearedScheduledNavigation(LocalFrame* frame)
797{
798    m_frameNavigationInitiatorMap.remove(m_pageAgent->frameId(frame));
799}
800
801void InspectorResourceAgent::setHostId(const String& hostId)
802{
803    m_hostId = hostId;
804}
805
806bool 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
827InspectorResourceAgent::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