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