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