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