f94ec79013f846823b0ee85b05e7f92acd4b1faa
[WebKit-https.git] / Source / WebCore / inspector / InspectorResourceAgent.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2015 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "InspectorResourceAgent.h"
34
35 #include "CachedRawResource.h"
36 #include "CachedResource.h"
37 #include "CachedResourceLoader.h"
38 #include "Document.h"
39 #include "DocumentLoader.h"
40 #include "DocumentThreadableLoader.h"
41 #include "ExceptionCodePlaceholder.h"
42 #include "Frame.h"
43 #include "FrameLoader.h"
44 #include "HTTPHeaderMap.h"
45 #include "HTTPHeaderNames.h"
46 #include "IconController.h"
47 #include "InspectorClient.h"
48 #include "InspectorPageAgent.h"
49 #include "InspectorTimelineAgent.h"
50 #include "InstrumentingAgents.h"
51 #include "JSMainThreadExecState.h"
52 #include "MemoryCache.h"
53 #include "NetworkResourcesData.h"
54 #include "Page.h"
55 #include "ProgressTracker.h"
56 #include "ResourceError.h"
57 #include "ResourceLoader.h"
58 #include "ResourceRequest.h"
59 #include "ResourceResponse.h"
60 #include "ScriptableDocumentParser.h"
61 #include "SubresourceLoader.h"
62 #include "ThreadableLoaderClient.h"
63 #include "URL.h"
64 #include "WebSocketFrame.h"
65 #include <inspector/IdentifiersFactory.h>
66 #include <inspector/InspectorFrontendRouter.h>
67 #include <inspector/InspectorValues.h>
68 #include <inspector/ScriptCallStack.h>
69 #include <inspector/ScriptCallStackFactory.h>
70 #include <wtf/RefPtr.h>
71 #include <wtf/text/StringBuilder.h>
72
73 using namespace Inspector;
74
75 typedef Inspector::NetworkBackendDispatcherHandler::LoadResourceCallback LoadResourceCallback;
76
77 namespace WebCore {
78
79 namespace {
80
81 class InspectorThreadableLoaderClient final : public ThreadableLoaderClient {
82     WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
83 public:
84     InspectorThreadableLoaderClient(PassRefPtr<LoadResourceCallback> callback)
85         : m_callback(callback) { }
86
87     virtual ~InspectorThreadableLoaderClient() { }
88
89     virtual void didReceiveResponse(unsigned long, const ResourceResponse& response) override
90     {
91         m_mimeType = response.mimeType();
92         m_statusCode = response.httpStatusCode();
93
94         // FIXME: This assumes text only responses. We should support non-text responses as well.
95         TextEncoding textEncoding(response.textEncodingName());
96         bool useDetector = false;
97         if (!textEncoding.isValid()) {
98             textEncoding = UTF8Encoding();
99             useDetector = true;
100         }
101
102         m_decoder = TextResourceDecoder::create(ASCIILiteral("text/plain"), textEncoding, useDetector);
103     }
104
105     virtual void didReceiveData(const char* data, int dataLength) override
106     {
107         if (!dataLength)
108             return;
109
110         if (dataLength == -1)
111             dataLength = strlen(data);
112
113         m_responseText.append(m_decoder->decode(data, dataLength));
114     }
115
116     virtual void didFinishLoading(unsigned long, double) override
117     {
118         if (m_decoder)
119             m_responseText.append(m_decoder->flush());
120
121         m_callback->sendSuccess(m_responseText.toString(), m_mimeType, m_statusCode);
122         dispose();
123     }
124
125     virtual void didFail(const ResourceError&) override
126     {
127         m_callback->sendFailure(ASCIILiteral("Loading resource for inspector failed"));
128         dispose();
129     }
130
131     virtual void didFailRedirectCheck() override
132     {
133         m_callback->sendFailure(ASCIILiteral("Loading resource for inspector failed redirect check"));
134         dispose();
135     }
136
137     void didFailLoaderCreation()
138     {
139         m_callback->sendFailure(ASCIILiteral("Could not create a loader"));
140         dispose();
141     }
142
143     void setLoader(PassRefPtr<ThreadableLoader> loader)
144     {
145         m_loader = loader;
146     }
147
148 private:
149     void dispose()
150     {
151         m_loader = nullptr;
152         delete this;
153     }
154
155     RefPtr<LoadResourceCallback> m_callback;
156     RefPtr<ThreadableLoader> m_loader;
157     RefPtr<TextResourceDecoder> m_decoder;
158     String m_mimeType;
159     StringBuilder m_responseText;
160     int m_statusCode;
161 };
162
163 } // namespace
164
165 InspectorResourceAgent::InspectorResourceAgent(WebAgentContext& context, InspectorPageAgent* pageAgent, InspectorClient* client)
166     : InspectorAgentBase(ASCIILiteral("Network"), context)
167     , m_frontendDispatcher(std::make_unique<Inspector::NetworkFrontendDispatcher>(context.frontendRouter))
168     , m_backendDispatcher(Inspector::NetworkBackendDispatcher::create(context.backendDispatcher, this))
169     , m_pageAgent(pageAgent)
170     , m_client(client)
171     , m_resourcesData(std::make_unique<NetworkResourcesData>())
172 {
173 }
174
175 void InspectorResourceAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
176 {
177 }
178
179 void InspectorResourceAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
180 {
181     ErrorString unused;
182     disable(unused);
183 }
184
185 static Ref<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
186 {
187     Ref<InspectorObject> headersObject = InspectorObject::create();
188     
189     for (const auto& header : headers)
190         headersObject->setString(header.key, header.value);
191     return WTF::move(headersObject);
192 }
193
194 static Ref<Inspector::Protocol::Network::ResourceTiming> buildObjectForTiming(const ResourceLoadTiming& timing, DocumentLoader* loader)
195 {
196     return Inspector::Protocol::Network::ResourceTiming::create()
197         .setNavigationStart(loader->timing().navigationStart())
198         .setDomainLookupStart(timing.domainLookupStart)
199         .setDomainLookupEnd(timing.domainLookupEnd)
200         .setConnectStart(timing.connectStart)
201         .setConnectEnd(timing.connectEnd)
202         .setSecureConnectionStart(timing.secureConnectionStart)
203         .setRequestStart(timing.requestStart)
204         .setResponseStart(timing.responseStart)
205         .release();
206 }
207
208 static Ref<Inspector::Protocol::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request)
209 {
210     auto requestObject = Inspector::Protocol::Network::Request::create()
211         .setUrl(request.url().string())
212         .setMethod(request.httpMethod())
213         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
214         .release();
215     if (request.httpBody() && !request.httpBody()->isEmpty())
216         requestObject->setPostData(request.httpBody()->flattenToString());
217     return WTF::move(requestObject);
218 }
219
220 static RefPtr<Inspector::Protocol::Network::Response> buildObjectForResourceResponse(const ResourceResponse& response, DocumentLoader* loader)
221 {
222     if (response.isNull())
223         return nullptr;
224
225     double status = response.httpStatusCode();
226     Ref<InspectorObject> headers = buildObjectForHeaders(response.httpHeaderFields());
227
228     auto responseObject = Inspector::Protocol::Network::Response::create()
229         .setUrl(response.url().string())
230         .setStatus(status)
231         .setStatusText(response.httpStatusText())
232         .setHeaders(WTF::move(headers))
233         .setMimeType(response.mimeType())
234         .release();
235
236     responseObject->setFromDiskCache(response.source() == ResourceResponse::Source::DiskCache || response.source() == ResourceResponse::Source::DiskCacheAfterValidation);
237     responseObject->setTiming(buildObjectForTiming(response.resourceLoadTiming(), loader));
238
239     return WTF::move(responseObject);
240 }
241
242 static Ref<Inspector::Protocol::Network::CachedResource> buildObjectForCachedResource(CachedResource* cachedResource, DocumentLoader* loader)
243 {
244     auto resourceObject = Inspector::Protocol::Network::CachedResource::create()
245         .setUrl(cachedResource->url())
246         .setType(InspectorPageAgent::cachedResourceTypeJson(*cachedResource))
247         .setBodySize(cachedResource->encodedSize())
248         .release();
249
250     auto resourceResponse = buildObjectForResourceResponse(cachedResource->response(), loader);
251     resourceObject->setResponse(WTF::move(resourceResponse));
252
253     String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
254     if (!sourceMappingURL.isEmpty())
255         resourceObject->setSourceMapURL(sourceMappingURL);
256
257     return WTF::move(resourceObject);
258 }
259
260 InspectorResourceAgent::~InspectorResourceAgent()
261 {
262     if (m_enabled) {
263         ErrorString unused;
264         disable(unused);
265     }
266     ASSERT(!m_instrumentingAgents.inspectorResourceAgent());
267 }
268
269 double InspectorResourceAgent::timestamp()
270 {
271     return m_instrumentingAgents.inspectorEnvironment().executionStopwatch()->elapsedTime();
272 }
273
274 void InspectorResourceAgent::willSendRequest(unsigned long identifier, DocumentLoader& loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
275 {
276     if (request.hiddenFromInspector()) {
277         m_hiddenRequestIdentifiers.add(identifier);
278         return;
279     }
280
281     String requestId = IdentifiersFactory::requestId(identifier);
282     m_resourcesData->resourceCreated(requestId, m_pageAgent->loaderId(&loader));
283
284     CachedResource* cachedResource = InspectorPageAgent::cachedResource(loader.frame(), request.url());
285     InspectorPageAgent::ResourceType type = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : m_resourcesData->resourceType(requestId);
286     if (type == InspectorPageAgent::OtherResource) {
287         if (m_loadingXHRSynchronously)
288             type = InspectorPageAgent::XHRResource;
289         else if (equalIgnoringFragmentIdentifier(request.url(), loader.frameLoader()->icon().url()))
290             type = InspectorPageAgent::ImageResource;
291         else if (equalIgnoringFragmentIdentifier(request.url(), loader.url()) && !loader.isCommitted())
292             type = InspectorPageAgent::DocumentResource;
293     }
294
295     m_resourcesData->setResourceType(requestId, type);
296
297     for (auto& entry : m_extraRequestHeaders)
298         request.setHTTPHeaderField(entry.key, entry.value);
299
300     request.setReportLoadTiming(true);
301     request.setReportRawHeaders(true);
302
303     if (m_cacheDisabled) {
304         request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache");
305         request.setCachePolicy(ReloadIgnoringCacheData);
306         request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache");
307     }
308
309     Inspector::Protocol::Page::ResourceType resourceType = InspectorPageAgent::resourceTypeJson(type);
310
311     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr);
312     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);
313 }
314
315 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
316 {
317     if (m_hiddenRequestIdentifiers.contains(identifier))
318         return;
319
320     m_frontendDispatcher->requestServedFromCache(IdentifiersFactory::requestId(identifier));
321 }
322
323 void InspectorResourceAgent::didReceiveResponse(unsigned long identifier, DocumentLoader& loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
324 {
325     if (m_hiddenRequestIdentifiers.contains(identifier))
326         return;
327
328     String requestId = IdentifiersFactory::requestId(identifier);
329     RefPtr<Inspector::Protocol::Network::Response> resourceResponse = buildObjectForResourceResponse(response, &loader);
330
331     bool isNotModified = response.httpStatusCode() == 304;
332
333     CachedResource* cachedResource = nullptr;
334     if (resourceLoader && resourceLoader->isSubresourceLoader() && !isNotModified)
335         cachedResource = static_cast<SubresourceLoader*>(resourceLoader)->cachedResource();
336     if (!cachedResource)
337         cachedResource = InspectorPageAgent::cachedResource(loader.frame(), response.url());
338
339     if (cachedResource) {
340         // Use mime type from cached resource in case the one in response is empty.
341         if (resourceResponse && response.mimeType().isEmpty())
342             resourceResponse->setString(Inspector::Protocol::Network::Response::MimeType, cachedResource->response().mimeType());
343         m_resourcesData->addCachedResource(requestId, cachedResource);
344     }
345
346     InspectorPageAgent::ResourceType type = m_resourcesData->resourceType(requestId);
347     InspectorPageAgent::ResourceType newType = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : type;
348
349     // FIXME: XHRResource is returned for CachedResource::RawResource, it should be OtherResource unless it truly is an XHR.
350     // RawResource is used for loading worker scripts, and those should stay as ScriptResource and not change to XHRResource.
351     if (type != newType && newType != InspectorPageAgent::XHRResource && newType != InspectorPageAgent::OtherResource)
352         type = newType;
353
354     m_resourcesData->responseReceived(requestId, m_pageAgent->frameId(loader.frame()), response);
355     m_resourcesData->setResourceType(requestId, type);
356
357     m_frontendDispatcher->responseReceived(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), timestamp(), InspectorPageAgent::resourceTypeJson(type), resourceResponse);
358
359     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
360     // as there will be no calls to didReceiveData from the network stack.
361     if (isNotModified && cachedResource && cachedResource->encodedSize())
362         didReceiveData(identifier, nullptr, cachedResource->encodedSize(), 0);
363 }
364
365 static bool isErrorStatusCode(int statusCode)
366 {
367     return statusCode >= 400;
368 }
369
370 void InspectorResourceAgent::didReceiveData(unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
371 {
372     if (m_hiddenRequestIdentifiers.contains(identifier))
373         return;
374
375     String requestId = IdentifiersFactory::requestId(identifier);
376
377     if (data) {
378         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
379         if (resourceData && !m_loadingXHRSynchronously && (!resourceData->cachedResource() || resourceData->cachedResource()->dataBufferingPolicy() == DoNotBufferData || isErrorStatusCode(resourceData->httpStatusCode())))
380             m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
381     }
382
383     m_frontendDispatcher->dataReceived(requestId, timestamp(), dataLength, encodedDataLength);
384 }
385
386 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, DocumentLoader& loader, double finishTime)
387 {
388     if (m_hiddenRequestIdentifiers.remove(identifier))
389         return;
390
391     String requestId = IdentifiersFactory::requestId(identifier);
392     if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource)
393         m_resourcesData->addResourceSharedBuffer(requestId, loader.frameLoader()->documentLoader()->mainResourceData(), loader.frame()->document()->encoding());
394
395     m_resourcesData->maybeDecodeDataToContent(requestId);
396
397     // FIXME: The finishTime that is passed in is from the NetworkProcess and is more accurate.
398     // However, all other times passed to the Inspector are generated from the web process. Mixing
399     // times from different processes can cause the finish time to be earlier than the response
400     // received time due to inter-process communication lag.
401     finishTime = timestamp();
402
403     String sourceMappingURL;
404     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
405     if (resourceData && resourceData->cachedResource())
406         sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(resourceData->cachedResource());
407
408     m_frontendDispatcher->loadingFinished(requestId, finishTime, !sourceMappingURL.isEmpty() ? &sourceMappingURL : nullptr);
409 }
410
411 void InspectorResourceAgent::didFailLoading(unsigned long identifier, DocumentLoader& loader, const ResourceError& error)
412 {
413     if (m_hiddenRequestIdentifiers.remove(identifier))
414         return;
415
416     String requestId = IdentifiersFactory::requestId(identifier);
417
418     if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource) {
419         Frame* frame = loader.frame();
420         if (frame && frame->loader().documentLoader() && frame->document()) {
421             m_resourcesData->addResourceSharedBuffer(requestId,
422                 frame->loader().documentLoader()->mainResourceData(),
423                 frame->document()->encoding());
424         }
425     }
426
427     bool canceled = error.isCancellation();
428     m_frontendDispatcher->loadingFailed(requestId, timestamp(), error.localizedDescription(), canceled ? &canceled : nullptr);
429 }
430
431 void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader& loader, CachedResource& resource)
432 {
433     String loaderId = m_pageAgent->loaderId(&loader);
434     String frameId = m_pageAgent->frameId(loader.frame());
435     unsigned long identifier = loader.frame()->page()->progress().createUniqueIdentifier();
436     String requestId = IdentifiersFactory::requestId(identifier);
437     m_resourcesData->resourceCreated(requestId, loaderId);
438     m_resourcesData->addCachedResource(requestId, &resource);
439
440     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr);
441
442     m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader.url().string(), timestamp(), initiatorObject, buildObjectForCachedResource(&resource, &loader));
443 }
444
445 void InspectorResourceAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
446 {
447     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
448 }
449
450 void InspectorResourceAgent::didReceiveScriptResponse(unsigned long identifier)
451 {
452     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
453 }
454
455 void InspectorResourceAgent::didFinishXHRLoading(ThreadableLoaderClient*, unsigned long identifier, const String& sourceString)
456 {
457     // For Asynchronous XHRs, the inspector can grab the data directly off of the CachedResource. For sync XHRs, we need to
458     // provide the data here, since no CachedResource was involved.
459     if (m_loadingXHRSynchronously)
460         m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
461 }
462
463 void InspectorResourceAgent::didReceiveXHRResponse(unsigned long identifier)
464 {
465     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource);
466 }
467
468 void InspectorResourceAgent::willLoadXHRSynchronously()
469 {
470     m_loadingXHRSynchronously = true;
471 }
472
473 void InspectorResourceAgent::didLoadXHRSynchronously()
474 {
475     m_loadingXHRSynchronously = false;
476 }
477
478 void InspectorResourceAgent::willDestroyCachedResource(CachedResource& cachedResource)
479 {
480     Vector<String> requestIds = m_resourcesData->removeCachedResource(&cachedResource);
481     if (!requestIds.size())
482         return;
483
484     String content;
485     bool base64Encoded;
486     if (!InspectorPageAgent::cachedResourceContent(&cachedResource, &content, &base64Encoded))
487         return;
488     Vector<String>::iterator end = requestIds.end();
489     for (Vector<String>::iterator it = requestIds.begin(); it != end; ++it)
490         m_resourcesData->setResourceContent(*it, content, base64Encoded);
491 }
492
493 void InspectorResourceAgent::willRecalculateStyle()
494 {
495     m_isRecalculatingStyle = true;
496 }
497
498 void InspectorResourceAgent::didRecalculateStyle()
499 {
500     m_isRecalculatingStyle = false;
501     m_styleRecalculationInitiator = nullptr;
502 }
503
504 void InspectorResourceAgent::didScheduleStyleRecalculation(Document& document)
505 {
506     if (!m_styleRecalculationInitiator)
507         m_styleRecalculationInitiator = buildInitiatorObject(&document);
508 }
509
510 RefPtr<Inspector::Protocol::Network::Initiator> InspectorResourceAgent::buildInitiatorObject(Document* document)
511 {
512     RefPtr<ScriptCallStack> stackTrace = createScriptCallStack(JSMainThreadExecState::currentState(), ScriptCallStack::maxCallStackSizeToCapture);
513     if (stackTrace && stackTrace->size() > 0) {
514         auto initiatorObject = Inspector::Protocol::Network::Initiator::create()
515             .setType(Inspector::Protocol::Network::Initiator::Type::Script)
516             .release();
517         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
518         return WTF::move(initiatorObject);
519     }
520
521     if (document && document->scriptableDocumentParser()) {
522         auto initiatorObject = Inspector::Protocol::Network::Initiator::create()
523             .setType(Inspector::Protocol::Network::Initiator::Type::Parser)
524             .release();
525         initiatorObject->setUrl(document->url().string());
526         initiatorObject->setLineNumber(document->scriptableDocumentParser()->textPosition().m_line.oneBasedInt());
527         return WTF::move(initiatorObject);
528     }
529
530     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
531         return m_styleRecalculationInitiator;
532
533     return Inspector::Protocol::Network::Initiator::create()
534         .setType(Inspector::Protocol::Network::Initiator::Type::Other)
535         .release();
536 }
537
538 #if ENABLE(WEB_SOCKETS)
539
540 void InspectorResourceAgent::didCreateWebSocket(unsigned long identifier, const URL& requestURL)
541 {
542     m_frontendDispatcher->webSocketCreated(IdentifiersFactory::requestId(identifier), requestURL.string());
543 }
544
545 void InspectorResourceAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const ResourceRequest& request)
546 {
547     auto requestObject = Inspector::Protocol::Network::WebSocketRequest::create()
548         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
549         .release();
550     m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), timestamp(), WTF::move(requestObject));
551 }
552
553 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const ResourceResponse& response)
554 {
555     auto responseObject = Inspector::Protocol::Network::WebSocketResponse::create()
556         .setStatus(response.httpStatusCode())
557         .setStatusText(response.httpStatusText())
558         .setHeaders(buildObjectForHeaders(response.httpHeaderFields()))
559         .release();
560     m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTF::move(responseObject));
561 }
562
563 void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier)
564 {
565     m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp());
566 }
567
568 void InspectorResourceAgent::didReceiveWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
569 {
570     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
571         .setOpcode(frame.opCode)
572         .setMask(frame.masked)
573         .setPayloadData(String(frame.payload, frame.payloadLength))
574         .release();
575     m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTF::move(frameObject));
576 }
577
578 void InspectorResourceAgent::didSendWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
579 {
580     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
581         .setOpcode(frame.opCode)
582         .setMask(frame.masked)
583         .setPayloadData(String(frame.payload, frame.payloadLength))
584         .release();
585     m_frontendDispatcher->webSocketFrameSent(IdentifiersFactory::requestId(identifier), timestamp(), WTF::move(frameObject));
586 }
587
588 void InspectorResourceAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
589 {
590     m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage);
591 }
592
593 #endif // ENABLE(WEB_SOCKETS)
594
595 void InspectorResourceAgent::enable(ErrorString&)
596 {
597     enable();
598 }
599
600 void InspectorResourceAgent::enable()
601 {
602     m_enabled = true;
603     m_instrumentingAgents.setInspectorResourceAgent(this);
604 }
605
606 void InspectorResourceAgent::disable(ErrorString&)
607 {
608     m_enabled = false;
609     m_instrumentingAgents.setInspectorResourceAgent(nullptr);
610     m_resourcesData->clear();
611     m_extraRequestHeaders.clear();
612 }
613
614 void InspectorResourceAgent::setExtraHTTPHeaders(ErrorString&, const InspectorObject& headers)
615 {
616     for (auto& entry : headers) {
617         String stringValue;
618         if (entry.value->asString(stringValue))
619             m_extraRequestHeaders.set(entry.key, stringValue);
620     }
621 }
622
623 void InspectorResourceAgent::getResponseBody(ErrorString& errorString, const String& requestId, String* content, bool* base64Encoded)
624 {
625     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
626     if (!resourceData) {
627         errorString = ASCIILiteral("No resource with given identifier found");
628         return;
629     }
630
631     if (resourceData->hasContent()) {
632         *base64Encoded = resourceData->base64Encoded();
633         *content = resourceData->content();
634         return;
635     }
636
637     if (resourceData->isContentEvicted()) {
638         errorString = ASCIILiteral("Request content was evicted from inspector cache");
639         return;
640     }
641
642     if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) {
643         *base64Encoded = false;
644         if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content))
645             return;
646     }
647
648     if (resourceData->cachedResource()) {
649         if (InspectorPageAgent::cachedResourceContent(resourceData->cachedResource(), content, base64Encoded))
650             return;
651     }
652
653     errorString = ASCIILiteral("No data found for resource with given identifier");
654 }
655
656 void InspectorResourceAgent::canClearBrowserCache(ErrorString&, bool* result)
657 {
658     *result = m_client->canClearBrowserCache();
659 }
660
661 void InspectorResourceAgent::clearBrowserCache(ErrorString&)
662 {
663     m_client->clearBrowserCache();
664 }
665
666 void InspectorResourceAgent::canClearBrowserCookies(ErrorString&, bool* result)
667 {
668     *result = m_client->canClearBrowserCookies();
669 }
670
671 void InspectorResourceAgent::clearBrowserCookies(ErrorString&)
672 {
673     m_client->clearBrowserCookies();
674 }
675
676 void InspectorResourceAgent::setCacheDisabled(ErrorString&, bool cacheDisabled)
677 {
678     m_cacheDisabled = cacheDisabled;
679     if (cacheDisabled)
680         MemoryCache::singleton().evictResources();
681 }
682
683 void InspectorResourceAgent::loadResource(ErrorString& errorString, const String& frameId, const String& urlString, Ref<LoadResourceCallback>&& callback)
684 {
685     Frame* frame = m_pageAgent->assertFrame(errorString, frameId);
686     if (!frame)
687         return;
688
689     Document* document = frame->document();
690     if (!document) {
691         errorString = ASCIILiteral("No Document instance for the specified frame");
692         return;
693     }
694
695     URL url = document->completeURL(urlString);
696     ResourceRequest request(url);
697     request.setHTTPMethod(ASCIILiteral("GET"));
698     request.setHiddenFromInspector(true);
699
700     ThreadableLoaderOptions options;
701     options.setSendLoadCallbacks(SendCallbacks); // So we remove this from m_hiddenRequestIdentifiers on completion.
702     options.setAllowCredentials(AllowStoredCredentials);
703     options.setDefersLoadingPolicy(DefersLoadingPolicy::DisallowDefersLoading); // So the request is never deferred.
704     options.crossOriginRequestPolicy = AllowCrossOriginRequests;
705
706     // InspectorThreadableLoaderClient deletes itself when the load completes.
707     InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback.copyRef());
708
709     RefPtr<DocumentThreadableLoader> loader = DocumentThreadableLoader::create(*document, *inspectorThreadableLoaderClient, request, options);
710     if (!loader) {
711         inspectorThreadableLoaderClient->didFailLoaderCreation();
712         return;
713     }
714
715     // If the load already completed, inspectorThreadableLoaderClient will have been deleted and we will have already called the callback.
716     if (!callback->isActive())
717         return;
718
719     inspectorThreadableLoaderClient->setLoader(loader.release());
720 }
721
722 void InspectorResourceAgent::mainFrameNavigated(DocumentLoader& loader)
723 {
724     if (m_cacheDisabled)
725         MemoryCache::singleton().evictResources();
726
727     m_resourcesData->clear(m_pageAgent->loaderId(&loader));
728 }
729
730 } // namespace WebCore