[Resource Timing] Gather timing information with reliable responseEnd time
[WebKit-https.git] / Source / WebCore / inspector / InspectorNetworkAgent.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 "InspectorNetworkAgent.h"
34
35 #include "CachedRawResource.h"
36 #include "CachedResource.h"
37 #include "CachedResourceLoader.h"
38 #include "CachedResourceRequestInitiators.h"
39 #include "Document.h"
40 #include "DocumentLoader.h"
41 #include "DocumentThreadableLoader.h"
42 #include "Frame.h"
43 #include "FrameLoader.h"
44 #include "HTTPHeaderMap.h"
45 #include "HTTPHeaderNames.h"
46 #include "IconController.h"
47 #include "InspectorPageAgent.h"
48 #include "InspectorTimelineAgent.h"
49 #include "InstrumentingAgents.h"
50 #include "JSMainThreadExecState.h"
51 #include "MemoryCache.h"
52 #include "NetworkResourcesData.h"
53 #include "Page.h"
54 #include "ProgressTracker.h"
55 #include "ResourceError.h"
56 #include "ResourceLoader.h"
57 #include "ResourceRequest.h"
58 #include "ResourceResponse.h"
59 #include "ScriptableDocumentParser.h"
60 #include "SubresourceLoader.h"
61 #include "ThreadableLoaderClient.h"
62 #include "URL.h"
63 #include "WebSocketFrame.h"
64 #include <inspector/ContentSearchUtilities.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/Stopwatch.h>
72 #include <wtf/text/StringBuilder.h>
73
74 using namespace Inspector;
75
76 typedef Inspector::NetworkBackendDispatcherHandler::LoadResourceCallback LoadResourceCallback;
77
78 namespace WebCore {
79
80 namespace {
81
82 class InspectorThreadableLoaderClient final : public ThreadableLoaderClient {
83     WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
84 public:
85     InspectorThreadableLoaderClient(RefPtr<LoadResourceCallback>&& callback)
86         : m_callback(WTFMove(callback)) { }
87
88     virtual ~InspectorThreadableLoaderClient() { }
89
90     void didReceiveResponse(unsigned long, const ResourceResponse& response) override
91     {
92         m_mimeType = response.mimeType();
93         m_statusCode = response.httpStatusCode();
94
95         // FIXME: This assumes text only responses. We should support non-text responses as well.
96         TextEncoding textEncoding(response.textEncodingName());
97         bool useDetector = false;
98         if (!textEncoding.isValid()) {
99             textEncoding = UTF8Encoding();
100             useDetector = true;
101         }
102
103         m_decoder = TextResourceDecoder::create(ASCIILiteral("text/plain"), textEncoding, useDetector);
104     }
105
106     void didReceiveData(const char* data, int dataLength) override
107     {
108         if (!dataLength)
109             return;
110
111         if (dataLength == -1)
112             dataLength = strlen(data);
113
114         m_responseText.append(m_decoder->decode(data, dataLength));
115     }
116
117     void didFinishLoading(unsigned long) override
118     {
119         if (m_decoder)
120             m_responseText.append(m_decoder->flush());
121
122         m_callback->sendSuccess(m_responseText.toString(), m_mimeType, m_statusCode);
123         dispose();
124     }
125
126     void didFail(const ResourceError& error) override
127     {
128         m_callback->sendFailure(error.isAccessControl() ? ASCIILiteral("Loading resource for inspector failed access control check") : ASCIILiteral("Loading resource for inspector failed"));
129         dispose();
130     }
131
132     void setLoader(RefPtr<ThreadableLoader>&& loader)
133     {
134         m_loader = WTFMove(loader);
135     }
136
137 private:
138     void dispose()
139     {
140         m_loader = nullptr;
141         delete this;
142     }
143
144     RefPtr<LoadResourceCallback> m_callback;
145     RefPtr<ThreadableLoader> m_loader;
146     RefPtr<TextResourceDecoder> m_decoder;
147     String m_mimeType;
148     StringBuilder m_responseText;
149     int m_statusCode;
150 };
151
152 } // namespace
153
154 InspectorNetworkAgent::InspectorNetworkAgent(WebAgentContext& context, InspectorPageAgent* pageAgent)
155     : InspectorAgentBase(ASCIILiteral("Network"), context)
156     , m_frontendDispatcher(std::make_unique<Inspector::NetworkFrontendDispatcher>(context.frontendRouter))
157     , m_backendDispatcher(Inspector::NetworkBackendDispatcher::create(context.backendDispatcher, this))
158     , m_pageAgent(pageAgent)
159     , m_resourcesData(std::make_unique<NetworkResourcesData>())
160 {
161 }
162
163 void InspectorNetworkAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
164 {
165 }
166
167 void InspectorNetworkAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
168 {
169     ErrorString unused;
170     disable(unused);
171 }
172
173 static Ref<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers)
174 {
175     Ref<InspectorObject> headersObject = InspectorObject::create();
176     
177     for (const auto& header : headers)
178         headersObject->setString(header.key, header.value);
179     return headersObject;
180 }
181
182 Ref<Inspector::Protocol::Network::ResourceTiming> InspectorNetworkAgent::buildObjectForTiming(const NetworkLoadMetrics& timing, ResourceLoader& resourceLoader)
183 {
184     MonotonicTime startTime = resourceLoader.loadTiming().startTime();
185     double startTimeInInspector = m_environment.executionStopwatch()->elapsedTimeSince(startTime);
186
187     return Inspector::Protocol::Network::ResourceTiming::create()
188         .setStartTime(startTimeInInspector)
189         .setDomainLookupStart(timing.domainLookupStart.milliseconds())
190         .setDomainLookupEnd(timing.domainLookupEnd.milliseconds())
191         .setConnectStart(timing.connectStart.milliseconds())
192         .setConnectEnd(timing.connectEnd.milliseconds())
193         .setSecureConnectionStart(timing.secureConnectionStart.milliseconds())
194         .setRequestStart(timing.requestStart.milliseconds())
195         .setResponseStart(timing.responseStart.milliseconds())
196         .release();
197 }
198
199 static Ref<Inspector::Protocol::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request)
200 {
201     auto requestObject = Inspector::Protocol::Network::Request::create()
202         .setUrl(request.url().string())
203         .setMethod(request.httpMethod())
204         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
205         .release();
206     if (request.httpBody() && !request.httpBody()->isEmpty()) {
207         Vector<char> bytes;
208         request.httpBody()->flatten(bytes);
209         requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size()));
210     }
211     return requestObject;
212 }
213
214 RefPtr<Inspector::Protocol::Network::Response> InspectorNetworkAgent::buildObjectForResourceResponse(const ResourceResponse& response, ResourceLoader* resourceLoader)
215 {
216     if (response.isNull())
217         return nullptr;
218
219     double status = response.httpStatusCode();
220     Ref<InspectorObject> headers = buildObjectForHeaders(response.httpHeaderFields());
221
222     auto responseObject = Inspector::Protocol::Network::Response::create()
223         .setUrl(response.url().string())
224         .setStatus(status)
225         .setStatusText(response.httpStatusText())
226         .setHeaders(WTFMove(headers))
227         .setMimeType(response.mimeType())
228         .release();
229
230     responseObject->setFromDiskCache(response.source() == ResourceResponse::Source::DiskCache || response.source() == ResourceResponse::Source::DiskCacheAfterValidation);
231     if (resourceLoader)
232         responseObject->setTiming(buildObjectForTiming(response.deprecatedNetworkLoadMetrics(), *resourceLoader));
233
234     return WTFMove(responseObject);
235 }
236
237 Ref<Inspector::Protocol::Network::CachedResource> InspectorNetworkAgent::buildObjectForCachedResource(CachedResource* cachedResource)
238 {
239     auto resourceObject = Inspector::Protocol::Network::CachedResource::create()
240         .setUrl(cachedResource->url())
241         .setType(InspectorPageAgent::cachedResourceTypeJson(*cachedResource))
242         .setBodySize(cachedResource->encodedSize())
243         .release();
244
245     auto resourceResponse = buildObjectForResourceResponse(cachedResource->response(), cachedResource->loader());
246     resourceObject->setResponse(WTFMove(resourceResponse));
247
248     String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
249     if (!sourceMappingURL.isEmpty())
250         resourceObject->setSourceMapURL(sourceMappingURL);
251
252     return resourceObject;
253 }
254
255 InspectorNetworkAgent::~InspectorNetworkAgent()
256 {
257     if (m_enabled) {
258         ErrorString unused;
259         disable(unused);
260     }
261     ASSERT(!m_instrumentingAgents.inspectorNetworkAgent());
262 }
263
264 double InspectorNetworkAgent::timestamp()
265 {
266     return m_environment.executionStopwatch()->elapsedTime();
267 }
268
269 void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLoader& loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
270 {
271     if (request.hiddenFromInspector()) {
272         m_hiddenRequestIdentifiers.add(identifier);
273         return;
274     }
275
276     String requestId = IdentifiersFactory::requestId(identifier);
277     m_resourcesData->resourceCreated(requestId, m_pageAgent->loaderId(&loader));
278
279     CachedResource* cachedResource = InspectorPageAgent::cachedResource(loader.frame(), request.url());
280     InspectorPageAgent::ResourceType type = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : m_resourcesData->resourceType(requestId);
281     if (type == InspectorPageAgent::OtherResource) {
282         if (m_loadingXHRSynchronously)
283             type = InspectorPageAgent::XHRResource;
284         else if (equalIgnoringFragmentIdentifier(request.url(), loader.frameLoader()->icon().url()))
285             type = InspectorPageAgent::ImageResource;
286         else if (equalIgnoringFragmentIdentifier(request.url(), loader.url()) && !loader.isCommitted())
287             type = InspectorPageAgent::DocumentResource;
288     }
289
290     m_resourcesData->setResourceType(requestId, type);
291
292     for (auto& entry : m_extraRequestHeaders)
293         request.setHTTPHeaderField(entry.key, entry.value);
294
295     request.setReportLoadTiming(true);
296     request.setReportRawHeaders(true);
297
298     if (m_cacheDisabled) {
299         request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache");
300         request.setCachePolicy(ReloadIgnoringCacheData);
301         request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache");
302     }
303
304     Inspector::Protocol::Page::ResourceType resourceType = InspectorPageAgent::resourceTypeJson(type);
305
306     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr);
307     String targetId = request.initiatorIdentifier();
308
309     m_frontendDispatcher->requestWillBeSent(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), loader.url().string(), buildObjectForResourceRequest(request), timestamp(), initiatorObject, buildObjectForResourceResponse(redirectResponse, nullptr), type != InspectorPageAgent::OtherResource ? &resourceType : nullptr, targetId.isEmpty() ? nullptr : &targetId);
310 }
311
312 void InspectorNetworkAgent::markResourceAsCached(unsigned long identifier)
313 {
314     if (m_hiddenRequestIdentifiers.contains(identifier))
315         return;
316
317     m_frontendDispatcher->requestServedFromCache(IdentifiersFactory::requestId(identifier));
318 }
319
320 void InspectorNetworkAgent::didReceiveResponse(unsigned long identifier, DocumentLoader& loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
321 {
322     if (m_hiddenRequestIdentifiers.contains(identifier))
323         return;
324
325     String requestId = IdentifiersFactory::requestId(identifier);
326     RefPtr<Inspector::Protocol::Network::Response> resourceResponse = buildObjectForResourceResponse(response, resourceLoader);
327
328     bool isNotModified = response.httpStatusCode() == 304;
329
330     CachedResource* cachedResource = nullptr;
331     if (resourceLoader && resourceLoader->isSubresourceLoader() && !isNotModified)
332         cachedResource = static_cast<SubresourceLoader*>(resourceLoader)->cachedResource();
333     if (!cachedResource)
334         cachedResource = InspectorPageAgent::cachedResource(loader.frame(), response.url());
335
336     if (cachedResource) {
337         // Use mime type from cached resource in case the one in response is empty.
338         if (resourceResponse && response.mimeType().isEmpty())
339             resourceResponse->setString(Inspector::Protocol::Network::Response::MimeType, cachedResource->response().mimeType());
340         m_resourcesData->addCachedResource(requestId, cachedResource);
341     }
342
343     InspectorPageAgent::ResourceType type = m_resourcesData->resourceType(requestId);
344     InspectorPageAgent::ResourceType newType = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : type;
345
346     // FIXME: XHRResource is returned for CachedResource::RawResource, it should be OtherResource unless it truly is an XHR.
347     // RawResource is used for loading worker scripts, and those should stay as ScriptResource and not change to XHRResource.
348     if (type != newType && newType != InspectorPageAgent::XHRResource && newType != InspectorPageAgent::OtherResource)
349         type = newType;
350
351     m_resourcesData->responseReceived(requestId, m_pageAgent->frameId(loader.frame()), response);
352     m_resourcesData->setResourceType(requestId, type);
353
354     m_frontendDispatcher->responseReceived(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), timestamp(), InspectorPageAgent::resourceTypeJson(type), resourceResponse);
355
356     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
357     // as there will be no calls to didReceiveData from the network stack.
358     if (isNotModified && cachedResource && cachedResource->encodedSize())
359         didReceiveData(identifier, nullptr, cachedResource->encodedSize(), 0);
360 }
361
362 static bool isErrorStatusCode(int statusCode)
363 {
364     return statusCode >= 400;
365 }
366
367 void InspectorNetworkAgent::didReceiveData(unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
368 {
369     if (m_hiddenRequestIdentifiers.contains(identifier))
370         return;
371
372     String requestId = IdentifiersFactory::requestId(identifier);
373
374     if (data) {
375         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
376         if (resourceData && !m_loadingXHRSynchronously && (!resourceData->cachedResource() || resourceData->cachedResource()->dataBufferingPolicy() == DoNotBufferData || isErrorStatusCode(resourceData->httpStatusCode())))
377             m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
378     }
379
380     m_frontendDispatcher->dataReceived(requestId, timestamp(), dataLength, encodedDataLength);
381 }
382
383 void InspectorNetworkAgent::didFinishLoading(unsigned long identifier, DocumentLoader& loader)
384 {
385     if (m_hiddenRequestIdentifiers.remove(identifier))
386         return;
387
388     // FIXME: Inspector should make use of NetworkLoadMetrics.
389     double elapsedFinishTime = timestamp();
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     String sourceMappingURL;
398     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
399     if (resourceData && resourceData->cachedResource())
400         sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(resourceData->cachedResource());
401
402     m_frontendDispatcher->loadingFinished(requestId, elapsedFinishTime, !sourceMappingURL.isEmpty() ? &sourceMappingURL : nullptr);
403 }
404
405 void InspectorNetworkAgent::didFailLoading(unsigned long identifier, DocumentLoader& loader, const ResourceError& error)
406 {
407     if (m_hiddenRequestIdentifiers.remove(identifier))
408         return;
409
410     String requestId = IdentifiersFactory::requestId(identifier);
411
412     if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource) {
413         Frame* frame = loader.frame();
414         if (frame && frame->loader().documentLoader() && frame->document()) {
415             m_resourcesData->addResourceSharedBuffer(requestId,
416                 frame->loader().documentLoader()->mainResourceData(),
417                 frame->document()->encoding());
418         }
419     }
420
421     bool canceled = error.isCancellation();
422     m_frontendDispatcher->loadingFailed(requestId, timestamp(), error.localizedDescription(), canceled ? &canceled : nullptr);
423 }
424
425 void InspectorNetworkAgent::didLoadResourceFromMemoryCache(DocumentLoader& loader, CachedResource& resource)
426 {
427     String loaderId = m_pageAgent->loaderId(&loader);
428     String frameId = m_pageAgent->frameId(loader.frame());
429     unsigned long identifier = loader.frame()->page()->progress().createUniqueIdentifier();
430     String requestId = IdentifiersFactory::requestId(identifier);
431     m_resourcesData->resourceCreated(requestId, loaderId);
432     m_resourcesData->addCachedResource(requestId, &resource);
433
434     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr);
435
436     m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader.url().string(), timestamp(), initiatorObject, buildObjectForCachedResource(&resource));
437 }
438
439 void InspectorNetworkAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
440 {
441     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
442 }
443
444 void InspectorNetworkAgent::didReceiveScriptResponse(unsigned long identifier)
445 {
446     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
447 }
448
449 void InspectorNetworkAgent::didReceiveThreadableLoaderResponse(unsigned long identifier, DocumentThreadableLoader& documentThreadableLoader)
450 {
451     String initiator = documentThreadableLoader.options().initiator;
452     if (initiator == cachedResourceRequestInitiators().fetch)
453         m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::FetchResource);
454     else if (initiator == cachedResourceRequestInitiators().xmlhttprequest)
455         m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource);
456 }
457
458 void InspectorNetworkAgent::didFinishXHRLoading(unsigned long identifier, const String& decodedText)
459 {
460     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), decodedText);
461 }
462
463 void InspectorNetworkAgent::willLoadXHRSynchronously()
464 {
465     m_loadingXHRSynchronously = true;
466 }
467
468 void InspectorNetworkAgent::didLoadXHRSynchronously()
469 {
470     m_loadingXHRSynchronously = false;
471 }
472
473 void InspectorNetworkAgent::willDestroyCachedResource(CachedResource& cachedResource)
474 {
475     Vector<String> requestIds = m_resourcesData->removeCachedResource(&cachedResource);
476     if (!requestIds.size())
477         return;
478
479     String content;
480     bool base64Encoded;
481     if (!InspectorPageAgent::cachedResourceContent(&cachedResource, &content, &base64Encoded))
482         return;
483     for (auto& id : requestIds)
484         m_resourcesData->setResourceContent(id, content, base64Encoded);
485 }
486
487 void InspectorNetworkAgent::willRecalculateStyle()
488 {
489     m_isRecalculatingStyle = true;
490 }
491
492 void InspectorNetworkAgent::didRecalculateStyle()
493 {
494     m_isRecalculatingStyle = false;
495     m_styleRecalculationInitiator = nullptr;
496 }
497
498 void InspectorNetworkAgent::didScheduleStyleRecalculation(Document& document)
499 {
500     if (!m_styleRecalculationInitiator)
501         m_styleRecalculationInitiator = buildInitiatorObject(&document);
502 }
503
504 RefPtr<Inspector::Protocol::Network::Initiator> InspectorNetworkAgent::buildInitiatorObject(Document* document)
505 {
506     Ref<ScriptCallStack> stackTrace = createScriptCallStack(JSMainThreadExecState::currentState(), ScriptCallStack::maxCallStackSizeToCapture);
507     if (stackTrace->size() > 0) {
508         auto initiatorObject = Inspector::Protocol::Network::Initiator::create()
509             .setType(Inspector::Protocol::Network::Initiator::Type::Script)
510             .release();
511         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
512         return WTFMove(initiatorObject);
513     }
514
515     if (document && document->scriptableDocumentParser()) {
516         auto initiatorObject = Inspector::Protocol::Network::Initiator::create()
517             .setType(Inspector::Protocol::Network::Initiator::Type::Parser)
518             .release();
519         initiatorObject->setUrl(document->url().string());
520         initiatorObject->setLineNumber(document->scriptableDocumentParser()->textPosition().m_line.oneBasedInt());
521         return WTFMove(initiatorObject);
522     }
523
524     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
525         return m_styleRecalculationInitiator;
526
527     return Inspector::Protocol::Network::Initiator::create()
528         .setType(Inspector::Protocol::Network::Initiator::Type::Other)
529         .release();
530 }
531
532 #if ENABLE(WEB_SOCKETS)
533
534 void InspectorNetworkAgent::didCreateWebSocket(unsigned long identifier, const URL& requestURL)
535 {
536     m_frontendDispatcher->webSocketCreated(IdentifiersFactory::requestId(identifier), requestURL.string());
537 }
538
539 void InspectorNetworkAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const ResourceRequest& request)
540 {
541     auto requestObject = Inspector::Protocol::Network::WebSocketRequest::create()
542         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
543         .release();
544     m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(requestObject));
545 }
546
547 void InspectorNetworkAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const ResourceResponse& response)
548 {
549     auto responseObject = Inspector::Protocol::Network::WebSocketResponse::create()
550         .setStatus(response.httpStatusCode())
551         .setStatusText(response.httpStatusText())
552         .setHeaders(buildObjectForHeaders(response.httpHeaderFields()))
553         .release();
554     m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(responseObject));
555 }
556
557 void InspectorNetworkAgent::didCloseWebSocket(unsigned long identifier)
558 {
559     m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp());
560 }
561
562 void InspectorNetworkAgent::didReceiveWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
563 {
564     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
565         .setOpcode(frame.opCode)
566         .setMask(frame.masked)
567         .setPayloadData(String(frame.payload, frame.payloadLength))
568         .release();
569     m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject));
570 }
571
572 void InspectorNetworkAgent::didSendWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
573 {
574     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
575         .setOpcode(frame.opCode)
576         .setMask(frame.masked)
577         .setPayloadData(String(frame.payload, frame.payloadLength))
578         .release();
579     m_frontendDispatcher->webSocketFrameSent(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject));
580 }
581
582 void InspectorNetworkAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
583 {
584     m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage);
585 }
586
587 #endif // ENABLE(WEB_SOCKETS)
588
589 void InspectorNetworkAgent::enable(ErrorString&)
590 {
591     enable();
592 }
593
594 void InspectorNetworkAgent::enable()
595 {
596     m_enabled = true;
597     m_instrumentingAgents.setInspectorNetworkAgent(this);
598 }
599
600 void InspectorNetworkAgent::disable(ErrorString&)
601 {
602     m_enabled = false;
603     m_instrumentingAgents.setInspectorNetworkAgent(nullptr);
604     m_resourcesData->clear();
605     m_extraRequestHeaders.clear();
606 }
607
608 void InspectorNetworkAgent::setExtraHTTPHeaders(ErrorString&, const InspectorObject& headers)
609 {
610     for (auto& entry : headers) {
611         String stringValue;
612         if (entry.value->asString(stringValue))
613             m_extraRequestHeaders.set(entry.key, stringValue);
614     }
615 }
616
617 void InspectorNetworkAgent::getResponseBody(ErrorString& errorString, const String& requestId, String* content, bool* base64Encoded)
618 {
619     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
620     if (!resourceData) {
621         errorString = ASCIILiteral("No resource with given identifier found");
622         return;
623     }
624
625     if (resourceData->hasContent()) {
626         *base64Encoded = resourceData->base64Encoded();
627         *content = resourceData->content();
628         return;
629     }
630
631     if (resourceData->isContentEvicted()) {
632         errorString = ASCIILiteral("Request content was evicted from inspector cache");
633         return;
634     }
635
636     if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) {
637         *base64Encoded = false;
638         if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content))
639             return;
640     }
641
642     if (resourceData->cachedResource()) {
643         if (InspectorPageAgent::cachedResourceContent(resourceData->cachedResource(), content, base64Encoded))
644             return;
645     }
646
647     errorString = ASCIILiteral("No data found for resource with given identifier");
648 }
649
650 void InspectorNetworkAgent::setCacheDisabled(ErrorString&, bool cacheDisabled)
651 {
652     m_cacheDisabled = cacheDisabled;
653     if (cacheDisabled)
654         MemoryCache::singleton().evictResources();
655 }
656
657 void InspectorNetworkAgent::loadResource(ErrorString& errorString, const String& frameId, const String& urlString, Ref<LoadResourceCallback>&& callback)
658 {
659     Frame* frame = m_pageAgent->assertFrame(errorString, frameId);
660     if (!frame)
661         return;
662
663     Document* document = frame->document();
664     if (!document) {
665         errorString = ASCIILiteral("No Document instance for the specified frame");
666         return;
667     }
668
669     URL url = document->completeURL(urlString);
670     ResourceRequest request(url);
671     request.setHTTPMethod(ASCIILiteral("GET"));
672     request.setHiddenFromInspector(true);
673
674     ThreadableLoaderOptions options;
675     options.sendLoadCallbacks = SendCallbacks; // So we remove this from m_hiddenRequestIdentifiers on completion.
676     options.defersLoadingPolicy = DefersLoadingPolicy::DisallowDefersLoading; // So the request is never deferred.
677     options.mode = FetchOptions::Mode::NoCors;
678     options.credentials = FetchOptions::Credentials::SameOrigin;
679     options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce;
680
681     // InspectorThreadableLoaderClient deletes itself when the load completes or fails.
682     InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback.copyRef());
683     auto loader = DocumentThreadableLoader::create(*document, *inspectorThreadableLoaderClient, WTFMove(request), options);
684     if (!loader)
685         return;
686
687     // If the load already completed, inspectorThreadableLoaderClient will have been deleted and we will have already called the callback.
688     if (!callback->isActive())
689         return;
690
691     inspectorThreadableLoaderClient->setLoader(WTFMove(loader));
692 }
693
694 static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& requestId, const String& frameId, const String& url, int matchesCount)
695 {
696     auto searchResult = Inspector::Protocol::Page::SearchResult::create()
697         .setUrl(url)
698         .setFrameId(frameId)
699         .setMatchesCount(matchesCount)
700         .release();
701     searchResult->setRequestId(requestId);
702     return searchResult;
703 }
704
705 void InspectorNetworkAgent::searchOtherRequests(const JSC::Yarr::RegularExpression& regex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Page::SearchResult>>& result)
706 {
707     Vector<NetworkResourcesData::ResourceData*> resources = m_resourcesData->resources();
708     for (auto* resourceData : resources) {
709         if (resourceData->hasContent()) {
710             int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, resourceData->content());
711             if (matchesCount)
712                 result->addItem(buildObjectForSearchResult(resourceData->requestId(), resourceData->frameId(), resourceData->url(), matchesCount));
713         }
714     }
715 }
716
717 void InspectorNetworkAgent::searchInRequest(ErrorString& errorString, const String& requestId, const String& query, bool caseSensitive, bool isRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>& results)
718 {
719     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
720     if (!resourceData) {
721         errorString = ASCIILiteral("No resource with given identifier found");
722         return;
723     }
724
725     if (!resourceData->hasContent()) {
726         errorString = ASCIILiteral("No resource content");
727         return;
728     }
729
730     results = ContentSearchUtilities::searchInTextByLines(resourceData->content(), query, caseSensitive, isRegex);
731 }
732
733 void InspectorNetworkAgent::mainFrameNavigated(DocumentLoader& loader)
734 {
735     if (m_cacheDisabled)
736         MemoryCache::singleton().evictResources();
737
738     m_resourcesData->clear(m_pageAgent->loaderId(&loader));
739 }
740
741 } // namespace WebCore