1ee3c130a72ebf8263a71b97d2d11236790c038d
[WebKit-https.git] / Source / WebCore / inspector / agents / InspectorNetworkAgent.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2015-2018 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 "CachedCSSStyleSheet.h"
36 #include "CachedRawResource.h"
37 #include "CachedResource.h"
38 #include "CachedResourceLoader.h"
39 #include "CachedResourceRequestInitiators.h"
40 #include "CachedScript.h"
41 #include "CertificateInfo.h"
42 #include "Document.h"
43 #include "DocumentLoader.h"
44 #include "DocumentThreadableLoader.h"
45 #include "Frame.h"
46 #include "FrameLoader.h"
47 #include "HTTPHeaderMap.h"
48 #include "HTTPHeaderNames.h"
49 #include "InspectorDOMAgent.h"
50 #include "InspectorTimelineAgent.h"
51 #include "InstrumentingAgents.h"
52 #include "JSExecState.h"
53 #include "JSWebSocket.h"
54 #include "LoaderStrategy.h"
55 #include "MIMETypeRegistry.h"
56 #include "MemoryCache.h"
57 #include "NetworkResourcesData.h"
58 #include "Page.h"
59 #include "PlatformStrategies.h"
60 #include "ProgressTracker.h"
61 #include "ResourceError.h"
62 #include "ResourceLoader.h"
63 #include "ResourceRequest.h"
64 #include "ResourceResponse.h"
65 #include "RuntimeEnabledFeatures.h"
66 #include "ScriptState.h"
67 #include "ScriptableDocumentParser.h"
68 #include "SubresourceLoader.h"
69 #include "TextResourceDecoder.h"
70 #include "ThreadableLoaderClient.h"
71 #include <wtf/URL.h>
72 #include "WebSocket.h"
73 #include "WebSocketChannel.h"
74 #include "WebSocketFrame.h"
75 #include <JavaScriptCore/ContentSearchUtilities.h>
76 #include <JavaScriptCore/IdentifiersFactory.h>
77 #include <JavaScriptCore/InjectedScript.h>
78 #include <JavaScriptCore/InjectedScriptManager.h>
79 #include <JavaScriptCore/JSCInlines.h>
80 #include <JavaScriptCore/ScriptCallStack.h>
81 #include <JavaScriptCore/ScriptCallStackFactory.h>
82 #include <wtf/JSONValues.h>
83 #include <wtf/Lock.h>
84 #include <wtf/RefPtr.h>
85 #include <wtf/Stopwatch.h>
86 #include <wtf/persistence/PersistentEncoder.h>
87 #include <wtf/text/Base64.h>
88 #include <wtf/text/StringBuilder.h>
89
90 typedef Inspector::NetworkBackendDispatcherHandler::LoadResourceCallback LoadResourceCallback;
91
92 namespace WebCore {
93
94 using namespace Inspector;
95
96 namespace {
97
98 class InspectorThreadableLoaderClient final : public ThreadableLoaderClient {
99     WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
100 public:
101     InspectorThreadableLoaderClient(RefPtr<LoadResourceCallback>&& callback)
102         : m_callback(WTFMove(callback))
103     {
104     }
105
106     virtual ~InspectorThreadableLoaderClient() = default;
107
108     void didReceiveResponse(unsigned long, const ResourceResponse& response) override
109     {
110         m_mimeType = response.mimeType();
111         m_statusCode = response.httpStatusCode();
112
113         // FIXME: This assumes text only responses. We should support non-text responses as well.
114         TextEncoding textEncoding(response.textEncodingName());
115         bool useDetector = false;
116         if (!textEncoding.isValid()) {
117             textEncoding = UTF8Encoding();
118             useDetector = true;
119         }
120
121         m_decoder = TextResourceDecoder::create("text/plain"_s, textEncoding, useDetector);
122     }
123
124     void didReceiveData(const char* data, int dataLength) override
125     {
126         if (!dataLength)
127             return;
128
129         if (dataLength == -1)
130             dataLength = strlen(data);
131
132         m_responseText.append(m_decoder->decode(data, dataLength));
133     }
134
135     void didFinishLoading(unsigned long) override
136     {
137         if (m_decoder)
138             m_responseText.append(m_decoder->flush());
139
140         m_callback->sendSuccess(m_responseText.toString(), m_mimeType, m_statusCode);
141         dispose();
142     }
143
144     void didFail(const ResourceError& error) override
145     {
146         m_callback->sendFailure(error.isAccessControl() ? "Loading resource for inspector failed access control check"_s : "Loading resource for inspector failed"_s);
147         dispose();
148     }
149
150     void setLoader(RefPtr<ThreadableLoader>&& loader)
151     {
152         m_loader = WTFMove(loader);
153     }
154
155 private:
156     void dispose()
157     {
158         m_loader = nullptr;
159         delete this;
160     }
161
162     RefPtr<LoadResourceCallback> m_callback;
163     RefPtr<ThreadableLoader> m_loader;
164     RefPtr<TextResourceDecoder> m_decoder;
165     String m_mimeType;
166     StringBuilder m_responseText;
167     int m_statusCode;
168 };
169
170 } // namespace
171
172 InspectorNetworkAgent::InspectorNetworkAgent(WebAgentContext& context)
173     : InspectorAgentBase("Network"_s, context)
174     , m_frontendDispatcher(std::make_unique<Inspector::NetworkFrontendDispatcher>(context.frontendRouter))
175     , m_backendDispatcher(Inspector::NetworkBackendDispatcher::create(context.backendDispatcher, this))
176     , m_injectedScriptManager(context.injectedScriptManager)
177     , m_resourcesData(std::make_unique<NetworkResourcesData>())
178 {
179 }
180
181 void InspectorNetworkAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
182 {
183 }
184
185 void InspectorNetworkAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
186 {
187     ErrorString unused;
188     disable(unused);
189 }
190
191 static Ref<JSON::Object> buildObjectForHeaders(const HTTPHeaderMap& headers)
192 {
193     Ref<JSON::Object> headersObject = JSON::Object::create();
194
195     for (const auto& header : headers)
196         headersObject->setString(header.key, header.value);
197     return headersObject;
198 }
199
200 Ref<Inspector::Protocol::Network::ResourceTiming> InspectorNetworkAgent::buildObjectForTiming(const NetworkLoadMetrics& timing, ResourceLoader& resourceLoader)
201 {
202     auto& loadTiming = resourceLoader.loadTiming();
203
204     auto elapsedTimeSince = [&] (const MonotonicTime& time) {
205         return m_environment.executionStopwatch()->elapsedTimeSince(time).seconds();
206     };
207
208     return Inspector::Protocol::Network::ResourceTiming::create()
209         .setStartTime(elapsedTimeSince(loadTiming.startTime()))
210         .setRedirectStart(elapsedTimeSince(loadTiming.redirectStart()))
211         .setRedirectEnd(elapsedTimeSince(loadTiming.redirectEnd()))
212         .setFetchStart(elapsedTimeSince(loadTiming.fetchStart()))
213         .setDomainLookupStart(timing.domainLookupStart.milliseconds())
214         .setDomainLookupEnd(timing.domainLookupEnd.milliseconds())
215         .setConnectStart(timing.connectStart.milliseconds())
216         .setConnectEnd(timing.connectEnd.milliseconds())
217         .setSecureConnectionStart(timing.secureConnectionStart.milliseconds())
218         .setRequestStart(timing.requestStart.milliseconds())
219         .setResponseStart(timing.responseStart.milliseconds())
220         .setResponseEnd(timing.responseEnd.milliseconds())
221         .release();
222 }
223
224 static Inspector::Protocol::Network::Metrics::Priority toProtocol(NetworkLoadPriority priority)
225 {
226     switch (priority) {
227     case NetworkLoadPriority::Low:
228         return Inspector::Protocol::Network::Metrics::Priority::Low;
229     case NetworkLoadPriority::Medium:
230         return Inspector::Protocol::Network::Metrics::Priority::Medium;
231     case NetworkLoadPriority::High:
232         return Inspector::Protocol::Network::Metrics::Priority::High;
233     case NetworkLoadPriority::Unknown:
234         break;
235     }
236
237     ASSERT_NOT_REACHED();
238     return Inspector::Protocol::Network::Metrics::Priority::Medium;
239 }
240
241 Ref<Inspector::Protocol::Network::Metrics> InspectorNetworkAgent::buildObjectForMetrics(const NetworkLoadMetrics& networkLoadMetrics)
242 {
243     auto metrics = Inspector::Protocol::Network::Metrics::create().release();
244
245     if (!networkLoadMetrics.protocol.isNull())
246         metrics->setProtocol(networkLoadMetrics.protocol);
247     if (networkLoadMetrics.priority != NetworkLoadPriority::Unknown)
248         metrics->setPriority(toProtocol(networkLoadMetrics.priority));
249     if (!networkLoadMetrics.remoteAddress.isNull())
250         metrics->setRemoteAddress(networkLoadMetrics.remoteAddress);
251     if (!networkLoadMetrics.connectionIdentifier.isNull())
252         metrics->setConnectionIdentifier(networkLoadMetrics.connectionIdentifier);
253     if (!networkLoadMetrics.requestHeaders.isEmpty())
254         metrics->setRequestHeaders(buildObjectForHeaders(networkLoadMetrics.requestHeaders));
255
256     if (networkLoadMetrics.requestHeaderBytesSent != std::numeric_limits<uint32_t>::max())
257         metrics->setRequestHeaderBytesSent(networkLoadMetrics.requestHeaderBytesSent);
258     if (networkLoadMetrics.requestBodyBytesSent != std::numeric_limits<uint64_t>::max())
259         metrics->setRequestBodyBytesSent(networkLoadMetrics.requestBodyBytesSent);
260     if (networkLoadMetrics.responseHeaderBytesReceived != std::numeric_limits<uint32_t>::max())
261         metrics->setResponseHeaderBytesReceived(networkLoadMetrics.responseHeaderBytesReceived);
262     if (networkLoadMetrics.responseBodyBytesReceived != std::numeric_limits<uint64_t>::max())
263         metrics->setResponseBodyBytesReceived(networkLoadMetrics.responseBodyBytesReceived);
264     if (networkLoadMetrics.responseBodyDecodedSize != std::numeric_limits<uint64_t>::max())
265         metrics->setResponseBodyDecodedSize(networkLoadMetrics.responseBodyDecodedSize);
266
267     auto connectionPayload = Inspector::Protocol::Security::Connection::create()
268         .release();
269
270     if (!networkLoadMetrics.tlsProtocol.isEmpty())
271         connectionPayload->setProtocol(networkLoadMetrics.tlsProtocol);
272
273     if (!networkLoadMetrics.tlsCipher.isEmpty())
274         connectionPayload->setCipher(networkLoadMetrics.tlsCipher);
275
276     metrics->setSecurityConnection(WTFMove(connectionPayload));
277
278     return metrics;
279 }
280
281 static Ref<Inspector::Protocol::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request)
282 {
283     auto requestObject = Inspector::Protocol::Network::Request::create()
284         .setUrl(request.url().string())
285         .setMethod(request.httpMethod())
286         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
287         .release();
288     if (request.httpBody() && !request.httpBody()->isEmpty()) {
289         auto bytes = request.httpBody()->flatten();
290         requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size()));
291     }
292     return requestObject;
293 }
294
295 static Inspector::Protocol::Network::Response::Source responseSource(ResourceResponse::Source source)
296 {
297     switch (source) {
298     case ResourceResponse::Source::ApplicationCache:
299         // FIXME: Add support for ApplicationCache in inspector.
300     case ResourceResponse::Source::Unknown:
301         return Inspector::Protocol::Network::Response::Source::Unknown;
302     case ResourceResponse::Source::Network:
303         return Inspector::Protocol::Network::Response::Source::Network;
304     case ResourceResponse::Source::MemoryCache:
305     case ResourceResponse::Source::MemoryCacheAfterValidation:
306         return Inspector::Protocol::Network::Response::Source::MemoryCache;
307     case ResourceResponse::Source::DiskCache:
308     case ResourceResponse::Source::DiskCacheAfterValidation:
309         return Inspector::Protocol::Network::Response::Source::DiskCache;
310     case ResourceResponse::Source::ServiceWorker:
311         return Inspector::Protocol::Network::Response::Source::ServiceWorker;
312     }
313
314     ASSERT_NOT_REACHED();
315     return Inspector::Protocol::Network::Response::Source::Unknown;
316 }
317
318 RefPtr<Inspector::Protocol::Network::Response> InspectorNetworkAgent::buildObjectForResourceResponse(const ResourceResponse& response, ResourceLoader* resourceLoader)
319 {
320     if (response.isNull())
321         return nullptr;
322
323     Ref<JSON::Object> headers = buildObjectForHeaders(response.httpHeaderFields());
324
325     auto responseObject = Inspector::Protocol::Network::Response::create()
326         .setUrl(response.url().string())
327         .setStatus(response.httpStatusCode())
328         .setStatusText(response.httpStatusText())
329         .setHeaders(WTFMove(headers))
330         .setMimeType(response.mimeType())
331         .setSource(responseSource(response.source()))
332         .release();
333
334     if (resourceLoader)
335         responseObject->setTiming(buildObjectForTiming(response.deprecatedNetworkLoadMetrics(), *resourceLoader));
336
337     if (auto& certificateInfo = response.certificateInfo()) {
338         auto securityPayload = Inspector::Protocol::Security::Security::create()
339             .release();
340
341         if (auto certificateSummaryInfo = certificateInfo.value().summaryInfo()) {
342             auto certificatePayload = Inspector::Protocol::Security::Certificate::create()
343                 .release();
344
345             certificatePayload->setSubject(certificateSummaryInfo.value().subject);
346
347             if (auto validFrom = certificateSummaryInfo.value().validFrom)
348                 certificatePayload->setValidFrom(validFrom.seconds());
349
350             if (auto validUntil = certificateSummaryInfo.value().validUntil)
351                 certificatePayload->setValidUntil(validUntil.seconds());
352
353             auto dnsNamesPayload = JSON::ArrayOf<String>::create();
354             for (auto& dnsName : certificateSummaryInfo.value().dnsNames)
355                 dnsNamesPayload->addItem(dnsName);
356             if (dnsNamesPayload->length())
357                 certificatePayload->setDnsNames(WTFMove(dnsNamesPayload));
358
359             auto ipAddressesPayload = JSON::ArrayOf<String>::create();
360             for (auto& ipAddress : certificateSummaryInfo.value().ipAddresses)
361                 ipAddressesPayload->addItem(ipAddress);
362             if (ipAddressesPayload->length())
363                 certificatePayload->setDnsNames(WTFMove(ipAddressesPayload));
364
365             securityPayload->setCertificate(WTFMove(certificatePayload));
366         }
367
368         responseObject->setSecurity(WTFMove(securityPayload));
369     }
370
371     return responseObject;
372 }
373
374 Ref<Inspector::Protocol::Network::CachedResource> InspectorNetworkAgent::buildObjectForCachedResource(CachedResource* cachedResource)
375 {
376     auto resourceObject = Inspector::Protocol::Network::CachedResource::create()
377         .setUrl(cachedResource->url())
378         .setType(InspectorPageAgent::cachedResourceTypeJSON(*cachedResource))
379         .setBodySize(cachedResource->encodedSize())
380         .release();
381
382     auto resourceResponse = buildObjectForResourceResponse(cachedResource->response(), cachedResource->loader());
383     resourceObject->setResponse(WTFMove(resourceResponse));
384
385     String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
386     if (!sourceMappingURL.isEmpty())
387         resourceObject->setSourceMapURL(sourceMappingURL);
388
389     return resourceObject;
390 }
391
392 InspectorNetworkAgent::~InspectorNetworkAgent()
393 {
394     if (m_enabled) {
395         ErrorString unused;
396         disable(unused);
397     }
398     ASSERT(!m_instrumentingAgents.inspectorNetworkAgent());
399 }
400
401 double InspectorNetworkAgent::timestamp()
402 {
403     return m_environment.executionStopwatch()->elapsedTime().seconds();
404 }
405
406 void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse, InspectorPageAgent::ResourceType type)
407 {
408     if (request.hiddenFromInspector()) {
409         m_hiddenRequestIdentifiers.add(identifier);
410         return;
411     }
412
413     double sendTimestamp = timestamp();
414     WallTime walltime = WallTime::now();
415
416     String requestId = IdentifiersFactory::requestId(identifier);
417     String frameId = frameIdentifier(loader);
418     String loaderId = loaderIdentifier(loader);
419     String targetId = request.initiatorIdentifier();
420
421     if (type == InspectorPageAgent::OtherResource) {
422         if (m_loadingXHRSynchronously)
423             type = InspectorPageAgent::XHRResource;
424         else if (loader && equalIgnoringFragmentIdentifier(request.url(), loader->url()) && !loader->isCommitted())
425             type = InspectorPageAgent::DocumentResource;
426         else if (loader) {
427             for (auto& linkIcon : loader->linkIcons()) {
428                 if (equalIgnoringFragmentIdentifier(request.url(), linkIcon.url)) {
429                     type = InspectorPageAgent::ImageResource;
430                     break;
431                 }
432             }
433         }
434     }
435
436     m_resourcesData->resourceCreated(requestId, loaderId, type);
437
438     for (auto& entry : m_extraRequestHeaders)
439         request.setHTTPHeaderField(entry.key, entry.value);
440
441     auto protocolResourceType = InspectorPageAgent::resourceTypeJSON(type);
442
443     Document* document = loader && loader->frame() ? loader->frame()->document() : nullptr;
444     auto initiatorObject = buildInitiatorObject(document, request);
445
446     String url = loader ? loader->url().string() : request.url();
447     m_frontendDispatcher->requestWillBeSent(requestId, frameId, loaderId, url, buildObjectForResourceRequest(request), sendTimestamp, walltime.secondsSinceEpoch().seconds(), initiatorObject, buildObjectForResourceResponse(redirectResponse, nullptr), type != InspectorPageAgent::OtherResource ? &protocolResourceType : nullptr, targetId.isEmpty() ? nullptr : &targetId);
448 }
449
450 static InspectorPageAgent::ResourceType resourceTypeForCachedResource(CachedResource* resource)
451 {
452     if (resource)
453         return InspectorPageAgent::inspectorResourceType(*resource);
454     return InspectorPageAgent::OtherResource;
455 }
456
457 static InspectorPageAgent::ResourceType resourceTypeForLoadType(InspectorInstrumentation::LoadType loadType)
458 {
459     switch (loadType) {
460     case InspectorInstrumentation::LoadType::Ping:
461         return InspectorPageAgent::PingResource;
462     case InspectorInstrumentation::LoadType::Beacon:
463         return InspectorPageAgent::BeaconResource;
464     }
465
466     ASSERT_NOT_REACHED();
467     return InspectorPageAgent::OtherResource;
468 }
469
470 void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
471 {
472     auto* cachedResource = loader ? InspectorPageAgent::cachedResource(loader->frame(), request.url()) : nullptr;
473     willSendRequest(identifier, loader, request, redirectResponse, resourceTypeForCachedResource(cachedResource));
474 }
475
476 void InspectorNetworkAgent::willSendRequestOfType(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, InspectorInstrumentation::LoadType loadType)
477 {
478     willSendRequest(identifier, loader, request, ResourceResponse(), resourceTypeForLoadType(loadType));
479 }
480
481 void InspectorNetworkAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
482 {
483     if (m_hiddenRequestIdentifiers.contains(identifier))
484         return;
485
486     String requestId = IdentifiersFactory::requestId(identifier);
487
488     Optional<ResourceResponse> realResponse;
489     if (platformStrategies()->loaderStrategy()->havePerformedSecurityChecks(response)) {
490         callOnMainThreadAndWait([&] {
491             // We do not need to isolate response since it comes straight from IPC, but we might want to isolate it for extra safety.
492             auto response = platformStrategies()->loaderStrategy()->responseFromResourceLoadIdentifier(identifier);
493             if (!response.isNull())
494                 realResponse = WTFMove(response);
495         });
496     }
497
498     RefPtr<Inspector::Protocol::Network::Response> resourceResponse = buildObjectForResourceResponse(realResponse ? *realResponse : response, resourceLoader);
499
500     bool isNotModified = response.httpStatusCode() == 304;
501
502     CachedResource* cachedResource = nullptr;
503     if (is<SubresourceLoader>(resourceLoader) && !isNotModified)
504         cachedResource = downcast<SubresourceLoader>(resourceLoader)->cachedResource();
505     if (!cachedResource && loader)
506         cachedResource = InspectorPageAgent::cachedResource(loader->frame(), response.url());
507
508     if (cachedResource) {
509         // Use mime type from cached resource in case the one in response is empty.
510         if (resourceResponse && response.mimeType().isEmpty())
511             resourceResponse->setString(Inspector::Protocol::Network::Response::MimeType, cachedResource->response().mimeType());
512         m_resourcesData->addCachedResource(requestId, cachedResource);
513     }
514
515     InspectorPageAgent::ResourceType type = m_resourcesData->resourceType(requestId);
516     InspectorPageAgent::ResourceType newType = cachedResource ? InspectorPageAgent::inspectorResourceType(*cachedResource) : type;
517
518     // FIXME: XHRResource is returned for CachedResource::Type::RawResource, it should be OtherResource unless it truly is an XHR.
519     // RawResource is used for loading worker scripts, and those should stay as ScriptResource and not change to XHRResource.
520     if (type != newType && newType != InspectorPageAgent::XHRResource && newType != InspectorPageAgent::OtherResource)
521         type = newType;
522
523     String frameId = frameIdentifier(loader);
524     String loaderId = loaderIdentifier(loader);
525
526     m_resourcesData->responseReceived(requestId, frameId, response, type, shouldForceBufferingNetworkResourceData());
527
528     m_frontendDispatcher->responseReceived(requestId, frameId, loaderId, timestamp(), InspectorPageAgent::resourceTypeJSON(type), resourceResponse);
529
530     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
531     // as there will be no calls to didReceiveData from the network stack.
532     if (isNotModified && cachedResource && cachedResource->encodedSize())
533         didReceiveData(identifier, nullptr, cachedResource->encodedSize(), 0);
534 }
535
536 void InspectorNetworkAgent::didReceiveData(unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
537 {
538     if (m_hiddenRequestIdentifiers.contains(identifier))
539         return;
540
541     String requestId = IdentifiersFactory::requestId(identifier);
542
543     if (data) {
544         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
545
546         // For a synchronous XHR, if we didn't add data then we can apply it here as base64 encoded content.
547         // Often the data is text and we would have a decoder, but for non-text we won't have a decoder.
548         // Sync XHRs may not have a cached resource, while non-sync XHRs usually transfer data over on completion.
549         if (m_loadingXHRSynchronously && resourceData && !resourceData->hasBufferedData() && !resourceData->cachedResource())
550             m_resourcesData->setResourceContent(requestId, base64Encode(data, dataLength), true);
551     }
552
553     m_frontendDispatcher->dataReceived(requestId, timestamp(), dataLength, encodedDataLength);
554 }
555
556 void InspectorNetworkAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, const NetworkLoadMetrics& networkLoadMetrics, ResourceLoader* resourceLoader)
557 {
558     if (m_hiddenRequestIdentifiers.remove(identifier))
559         return;
560
561     double elapsedFinishTime;
562     if (resourceLoader && networkLoadMetrics.isComplete()) {
563         MonotonicTime fetchStart = resourceLoader->loadTiming().fetchStart();
564         Seconds fetchStartInInspector = m_environment.executionStopwatch()->elapsedTimeSince(fetchStart);
565         elapsedFinishTime = (fetchStartInInspector + networkLoadMetrics.responseEnd).seconds();
566     } else
567         elapsedFinishTime = timestamp();
568
569     String requestId = IdentifiersFactory::requestId(identifier);
570     if (loader && m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource)
571         m_resourcesData->addResourceSharedBuffer(requestId, loader->frameLoader()->documentLoader()->mainResourceData(), loader->frame()->document()->encoding());
572
573     m_resourcesData->maybeDecodeDataToContent(requestId);
574
575     String sourceMappingURL;
576     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
577     if (resourceData && resourceData->cachedResource())
578         sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(resourceData->cachedResource());
579
580     Optional<NetworkLoadMetrics> realMetrics;
581     if (platformStrategies()->loaderStrategy()->shouldPerformSecurityChecks() && !networkLoadMetrics.isComplete()) {
582         callOnMainThreadAndWait([&] {
583             realMetrics = platformStrategies()->loaderStrategy()->networkMetricsFromResourceLoadIdentifier(identifier).isolatedCopy();
584         });
585     }
586     RefPtr<Inspector::Protocol::Network::Metrics> metrics = buildObjectForMetrics(realMetrics ? *realMetrics : networkLoadMetrics);
587
588     m_frontendDispatcher->loadingFinished(requestId, elapsedFinishTime, !sourceMappingURL.isEmpty() ? &sourceMappingURL : nullptr, metrics);
589 }
590
591 void InspectorNetworkAgent::didFailLoading(unsigned long identifier, DocumentLoader* loader, const ResourceError& error)
592 {
593     if (m_hiddenRequestIdentifiers.remove(identifier))
594         return;
595
596     String requestId = IdentifiersFactory::requestId(identifier);
597
598     if (loader && m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource) {
599         Frame* frame = loader->frame();
600         if (frame && frame->loader().documentLoader() && frame->document()) {
601             m_resourcesData->addResourceSharedBuffer(requestId,
602                 frame->loader().documentLoader()->mainResourceData(),
603                 frame->document()->encoding());
604         }
605     }
606
607     bool canceled = error.isCancellation();
608     m_frontendDispatcher->loadingFailed(requestId, timestamp(), error.localizedDescription(), canceled ? &canceled : nullptr);
609 }
610
611 void InspectorNetworkAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, CachedResource& resource)
612 {
613     ASSERT(loader);
614     if (!loader)
615         return;
616
617     unsigned long identifier = loader->frame()->page()->progress().createUniqueIdentifier();
618     String requestId = IdentifiersFactory::requestId(identifier);
619     String loaderId = loaderIdentifier(loader);
620     String frameId = frameIdentifier(loader);
621
622     m_resourcesData->resourceCreated(requestId, loaderId, resource);
623
624     auto initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : nullptr, resource.resourceRequest());
625
626     // FIXME: It would be ideal to generate the Network.Response with the MemoryCache source
627     // instead of whatever ResourceResponse::Source the CachedResources's response has.
628     // The frontend already knows for certain that this was served from the memory cache.
629
630     m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader->url().string(), timestamp(), initiatorObject, buildObjectForCachedResource(&resource));
631 }
632
633 void InspectorNetworkAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
634 {
635     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
636 }
637
638 void InspectorNetworkAgent::didReceiveScriptResponse(unsigned long identifier)
639 {
640     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
641 }
642
643 void InspectorNetworkAgent::didReceiveThreadableLoaderResponse(unsigned long identifier, DocumentThreadableLoader& documentThreadableLoader)
644 {
645     String initiator = documentThreadableLoader.options().initiator;
646     if (initiator == cachedResourceRequestInitiators().fetch)
647         m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::FetchResource);
648     else if (initiator == cachedResourceRequestInitiators().xmlhttprequest)
649         m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource);
650 }
651
652 void InspectorNetworkAgent::willLoadXHRSynchronously()
653 {
654     m_loadingXHRSynchronously = true;
655 }
656
657 void InspectorNetworkAgent::didLoadXHRSynchronously()
658 {
659     m_loadingXHRSynchronously = false;
660 }
661
662 void InspectorNetworkAgent::willDestroyCachedResource(CachedResource& cachedResource)
663 {
664     Vector<String> requestIds = m_resourcesData->removeCachedResource(&cachedResource);
665     if (!requestIds.size())
666         return;
667
668     String content;
669     bool base64Encoded;
670     if (!InspectorNetworkAgent::cachedResourceContent(cachedResource, &content, &base64Encoded))
671         return;
672
673     for (auto& id : requestIds)
674         m_resourcesData->setResourceContent(id, content, base64Encoded);
675 }
676
677 void InspectorNetworkAgent::willRecalculateStyle()
678 {
679     m_isRecalculatingStyle = true;
680 }
681
682 void InspectorNetworkAgent::didRecalculateStyle()
683 {
684     m_isRecalculatingStyle = false;
685     m_styleRecalculationInitiator = nullptr;
686 }
687
688 void InspectorNetworkAgent::didScheduleStyleRecalculation(Document& document)
689 {
690     if (!m_styleRecalculationInitiator)
691         m_styleRecalculationInitiator = buildInitiatorObject(&document);
692 }
693
694 RefPtr<Inspector::Protocol::Network::Initiator> InspectorNetworkAgent::buildInitiatorObject(Document* document, Optional<const ResourceRequest&> resourceRequest)
695 {
696     // FIXME: Worker support.
697     if (!isMainThread()) {
698         return Inspector::Protocol::Network::Initiator::create()
699             .setType(Inspector::Protocol::Network::Initiator::Type::Other)
700             .release();
701     }
702
703     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject;
704
705     Ref<ScriptCallStack> stackTrace = createScriptCallStack(JSExecState::currentState());
706     if (stackTrace->size() > 0) {
707         initiatorObject = Inspector::Protocol::Network::Initiator::create()
708             .setType(Inspector::Protocol::Network::Initiator::Type::Script)
709             .release();
710         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
711     } else if (document && document->scriptableDocumentParser()) {
712         initiatorObject = Inspector::Protocol::Network::Initiator::create()
713             .setType(Inspector::Protocol::Network::Initiator::Type::Parser)
714             .release();
715         initiatorObject->setUrl(document->url().string());
716         initiatorObject->setLineNumber(document->scriptableDocumentParser()->textPosition().m_line.oneBasedInt());
717     }
718
719     auto domAgent = m_instrumentingAgents.inspectorDOMAgent();
720     if (domAgent && resourceRequest) {
721         if (auto inspectorInitiatorNodeIdentifier = resourceRequest->inspectorInitiatorNodeIdentifier()) {
722             if (!initiatorObject) {
723                 initiatorObject = Inspector::Protocol::Network::Initiator::create()
724                     .setType(Inspector::Protocol::Network::Initiator::Type::Other)
725                     .release();
726             }
727
728             initiatorObject->setNodeId(*inspectorInitiatorNodeIdentifier);
729         }
730     }
731
732     if (initiatorObject)
733         return initiatorObject;
734
735     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
736         return m_styleRecalculationInitiator;
737
738     return Inspector::Protocol::Network::Initiator::create()
739         .setType(Inspector::Protocol::Network::Initiator::Type::Other)
740         .release();
741 }
742
743 void InspectorNetworkAgent::didCreateWebSocket(unsigned long identifier, const URL& requestURL)
744 {
745     m_frontendDispatcher->webSocketCreated(IdentifiersFactory::requestId(identifier), requestURL.string());
746 }
747
748 void InspectorNetworkAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const ResourceRequest& request)
749 {
750     auto requestObject = Inspector::Protocol::Network::WebSocketRequest::create()
751         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
752         .release();
753     m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), timestamp(), WallTime::now().secondsSinceEpoch().seconds(), WTFMove(requestObject));
754 }
755
756 void InspectorNetworkAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const ResourceResponse& response)
757 {
758     auto responseObject = Inspector::Protocol::Network::WebSocketResponse::create()
759         .setStatus(response.httpStatusCode())
760         .setStatusText(response.httpStatusText())
761         .setHeaders(buildObjectForHeaders(response.httpHeaderFields()))
762         .release();
763     m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(responseObject));
764 }
765
766 void InspectorNetworkAgent::didCloseWebSocket(unsigned long identifier)
767 {
768     m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp());
769 }
770
771 void InspectorNetworkAgent::didReceiveWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
772 {
773     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
774         .setOpcode(frame.opCode)
775         .setMask(frame.masked)
776         .setPayloadData(String::fromUTF8WithLatin1Fallback(frame.payload, frame.payloadLength))
777         .setPayloadLength(frame.payloadLength)
778         .release();
779     m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject));
780 }
781
782 void InspectorNetworkAgent::didSendWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
783 {
784     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
785         .setOpcode(frame.opCode)
786         .setMask(frame.masked)
787         .setPayloadData(String::fromUTF8WithLatin1Fallback(frame.payload, frame.payloadLength))
788         .setPayloadLength(frame.payloadLength)
789         .release();
790     m_frontendDispatcher->webSocketFrameSent(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject));
791 }
792
793 void InspectorNetworkAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
794 {
795     m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage);
796 }
797
798 void InspectorNetworkAgent::enable(ErrorString&)
799 {
800     enable();
801 }
802
803 void InspectorNetworkAgent::enable()
804 {
805     m_enabled = true;
806     m_instrumentingAgents.setInspectorNetworkAgent(this);
807
808     {
809         LockHolder lock(WebSocket::allActiveWebSocketsMutex());
810
811         for (WebSocket* webSocket : activeWebSockets(lock)) {
812             ASSERT(is<WebSocketChannel>(webSocket->channel().get()));
813             WebSocketChannel* channel = downcast<WebSocketChannel>(webSocket->channel().get());
814
815             unsigned identifier = channel->identifier();
816             didCreateWebSocket(identifier, webSocket->url());
817             auto cookieRequestHeaderFieldValue = [document = makeWeakPtr(channel->document())] (const URL& url) -> String {
818                 if (!document || !document->page())
819                     return { };
820                 return document->page()->cookieJar().cookieRequestHeaderFieldValue(*document, url);
821             };
822             willSendWebSocketHandshakeRequest(identifier, channel->clientHandshakeRequest(WTFMove(cookieRequestHeaderFieldValue)));
823
824             if (channel->handshakeMode() == WebSocketHandshake::Connected)
825                 didReceiveWebSocketHandshakeResponse(identifier, channel->serverHandshakeResponse());
826
827             if (webSocket->readyState() == WebSocket::CLOSED)
828                 didCloseWebSocket(identifier);
829         }
830     }
831 }
832
833 void InspectorNetworkAgent::disable(ErrorString&)
834 {
835     m_enabled = false;
836     m_instrumentingAgents.setInspectorNetworkAgent(nullptr);
837     m_resourcesData->clear();
838     m_extraRequestHeaders.clear();
839
840     setResourceCachingDisabled(false);
841 }
842
843 void InspectorNetworkAgent::setExtraHTTPHeaders(ErrorString&, const JSON::Object& headers)
844 {
845     for (auto& entry : headers) {
846         String stringValue;
847         if (entry.value->asString(stringValue))
848             m_extraRequestHeaders.set(entry.key, stringValue);
849     }
850 }
851
852 void InspectorNetworkAgent::getResponseBody(ErrorString& errorString, const String& requestId, String* content, bool* base64Encoded)
853 {
854     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
855     if (!resourceData) {
856         errorString = "No resource with given identifier found"_s;
857         return;
858     }
859
860     if (resourceData->hasContent()) {
861         *base64Encoded = resourceData->base64Encoded();
862         *content = resourceData->content();
863         return;
864     }
865
866     if (resourceData->isContentEvicted()) {
867         errorString = "Request content was evicted from inspector cache"_s;
868         return;
869     }
870
871     if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) {
872         *base64Encoded = false;
873         if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content))
874             return;
875     }
876
877     if (resourceData->cachedResource()) {
878         if (InspectorNetworkAgent::cachedResourceContent(*resourceData->cachedResource(), content, base64Encoded))
879             return;
880     }
881
882     errorString = "No data found for resource with given identifier"_s;
883 }
884
885 void InspectorNetworkAgent::setResourceCachingDisabled(ErrorString&, bool disabled)
886 {
887     setResourceCachingDisabled(disabled);
888 }
889
890 void InspectorNetworkAgent::loadResource(const String& frameId, const String& urlString, Ref<LoadResourceCallback>&& callback)
891 {
892     ErrorString error;
893     auto* context = scriptExecutionContext(error, frameId);
894     if (!context) {
895         callback->sendFailure(error);
896         return;
897     }
898
899     URL url = context->completeURL(urlString);
900     ResourceRequest request(url);
901     request.setHTTPMethod("GET"_s);
902     request.setHiddenFromInspector(true);
903
904     ThreadableLoaderOptions options;
905     options.sendLoadCallbacks = SendCallbackPolicy::SendCallbacks; // So we remove this from m_hiddenRequestIdentifiers on completion.
906     options.defersLoadingPolicy = DefersLoadingPolicy::DisallowDefersLoading; // So the request is never deferred.
907     options.mode = FetchOptions::Mode::NoCors;
908     options.credentials = FetchOptions::Credentials::SameOrigin;
909     options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce;
910
911     // InspectorThreadableLoaderClient deletes itself when the load completes or fails.
912     InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback.copyRef());
913     auto loader = ThreadableLoader::create(*context, *inspectorThreadableLoaderClient, WTFMove(request), options);
914     if (!loader) {
915         callback->sendFailure("Could not load requested resource."_s);
916         return;
917     }
918
919     // If the load already completed, inspectorThreadableLoaderClient will have been deleted and we will have already called the callback.
920     if (!callback->isActive())
921         return;
922
923     inspectorThreadableLoaderClient->setLoader(WTFMove(loader));
924 }
925
926 void InspectorNetworkAgent::getSerializedCertificate(ErrorString& errorString, const String& requestId, String* serializedCertificate)
927 {
928     auto* resourceData = m_resourcesData->data(requestId);
929     if (!resourceData) {
930         errorString = "No resource with given identifier found"_s;
931         return;
932     }
933
934     auto& certificate = resourceData->certificateInfo();
935     if (!certificate || certificate.value().isEmpty()) {
936         errorString = "No certificate for resource"_s;
937         return;
938     }
939
940     WTF::Persistence::Encoder encoder;
941     encoder << certificate.value();
942     *serializedCertificate = base64Encode(encoder.buffer(), encoder.bufferSize());
943 }
944
945 WebSocket* InspectorNetworkAgent::webSocketForRequestId(const String& requestId)
946 {
947     LockHolder lock(WebSocket::allActiveWebSocketsMutex());
948
949     for (WebSocket* webSocket : activeWebSockets(lock)) {
950         ASSERT(is<WebSocketChannel>(webSocket->channel().get()));
951         WebSocketChannel* channel = downcast<WebSocketChannel>(webSocket->channel().get());
952         if (IdentifiersFactory::requestId(channel->identifier()) == requestId)
953             return webSocket;
954     }
955
956     return nullptr;
957 }
958
959 static JSC::JSValue webSocketAsScriptValue(JSC::ExecState& state, WebSocket* webSocket)
960 {
961     JSC::JSLockHolder lock(&state);
962     return toJS(&state, deprecatedGlobalObjectForPrototype(&state), webSocket);
963 }
964
965 void InspectorNetworkAgent::resolveWebSocket(ErrorString& errorString, const String& requestId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
966 {
967     WebSocket* webSocket = webSocketForRequestId(requestId);
968     if (!webSocket) {
969         errorString = "WebSocket not found"_s;
970         return;
971     }
972
973     // FIXME: <https://webkit.org/b/168475> Web Inspector: Correctly display iframe's and worker's WebSockets
974     if (!is<Document>(webSocket->scriptExecutionContext()))
975         return;
976
977     auto* document = downcast<Document>(webSocket->scriptExecutionContext());
978     auto* frame = document->frame();
979     if (!frame) {
980         errorString = "WebSocket belongs to document without a frame"_s;
981         return;
982     }
983
984     auto& state = *mainWorldExecState(frame);
985     auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
986     ASSERT(!injectedScript.hasNoValue());
987
988     String objectGroupName = objectGroup ? *objectGroup : String();
989     result = injectedScript.wrapObject(webSocketAsScriptValue(state, webSocket), objectGroupName);
990 }
991
992 bool InspectorNetworkAgent::shouldTreatAsText(const String& mimeType)
993 {
994     return startsWithLettersIgnoringASCIICase(mimeType, "text/")
995         || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType)
996         || MIMETypeRegistry::isSupportedJSONMIMEType(mimeType)
997         || MIMETypeRegistry::isXMLMIMEType(mimeType)
998         || MIMETypeRegistry::isTextMediaPlaylistMIMEType(mimeType);
999 }
1000
1001 Ref<TextResourceDecoder> InspectorNetworkAgent::createTextDecoder(const String& mimeType, const String& textEncodingName)
1002 {
1003     if (!textEncodingName.isEmpty())
1004         return TextResourceDecoder::create("text/plain"_s, textEncodingName);
1005
1006     if (MIMETypeRegistry::isTextMIMEType(mimeType))
1007         return TextResourceDecoder::create(mimeType, "UTF-8");
1008     if (MIMETypeRegistry::isXMLMIMEType(mimeType)) {
1009         auto decoder = TextResourceDecoder::create("application/xml"_s);
1010         decoder->useLenientXMLDecoding();
1011         return decoder;
1012     }
1013
1014     return TextResourceDecoder::create("text/plain"_s, "UTF-8");
1015 }
1016
1017 Optional<String> InspectorNetworkAgent::textContentForCachedResource(CachedResource& cachedResource)
1018 {
1019     if (!InspectorNetworkAgent::shouldTreatAsText(cachedResource.mimeType()))
1020         return WTF::nullopt;
1021
1022     String result;
1023     bool base64Encoded;
1024     if (InspectorNetworkAgent::cachedResourceContent(cachedResource, &result, &base64Encoded)) {
1025         ASSERT(!base64Encoded);
1026         return result;
1027     }
1028
1029     return WTF::nullopt;
1030 }
1031
1032 bool InspectorNetworkAgent::cachedResourceContent(CachedResource& resource, String* result, bool* base64Encoded)
1033 {
1034     ASSERT(result);
1035     ASSERT(base64Encoded);
1036
1037     if (!resource.encodedSize()) {
1038         *base64Encoded = false;
1039         *result = String();
1040         return true;
1041     }
1042
1043     switch (resource.type()) {
1044     case CachedResource::Type::CSSStyleSheet:
1045         *base64Encoded = false;
1046         *result = downcast<CachedCSSStyleSheet>(resource).sheetText();
1047         // The above can return a null String if the MIME type is invalid.
1048         return !result->isNull();
1049     case CachedResource::Type::Script:
1050         *base64Encoded = false;
1051         *result = downcast<CachedScript>(resource).script().toString();
1052         return true;
1053     default:
1054         auto* buffer = resource.resourceBuffer();
1055         if (!buffer)
1056             return false;
1057
1058         if (InspectorNetworkAgent::shouldTreatAsText(resource.mimeType())) {
1059             auto decoder = InspectorNetworkAgent::createTextDecoder(resource.mimeType(), resource.response().textEncodingName());
1060             *base64Encoded = false;
1061             *result = decoder->decodeAndFlush(buffer->data(), buffer->size());
1062             return true;
1063         }
1064
1065         *base64Encoded = true;
1066         *result = base64Encode(buffer->data(), buffer->size());
1067         return true;
1068     }
1069 }
1070
1071 static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& requestId, const String& frameId, const String& url, int matchesCount)
1072 {
1073     auto searchResult = Inspector::Protocol::Page::SearchResult::create()
1074         .setUrl(url)
1075         .setFrameId(frameId)
1076         .setMatchesCount(matchesCount)
1077         .release();
1078     searchResult->setRequestId(requestId);
1079     return searchResult;
1080 }
1081
1082 static Optional<String> textContentForResourceData(const NetworkResourcesData::ResourceData& resourceData)
1083 {
1084     if (resourceData.hasContent() && !resourceData.base64Encoded())
1085         return resourceData.content();
1086
1087     if (resourceData.cachedResource())
1088         return InspectorNetworkAgent::textContentForCachedResource(*resourceData.cachedResource());
1089
1090     return WTF::nullopt;
1091 }
1092
1093 void InspectorNetworkAgent::searchOtherRequests(const JSC::Yarr::RegularExpression& regex, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::SearchResult>>& result)
1094 {
1095     Vector<NetworkResourcesData::ResourceData*> resources = m_resourcesData->resources();
1096     for (auto* resourceData : resources) {
1097         if (auto textContent = textContentForResourceData(*resourceData)) {
1098             int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, resourceData->content());
1099             if (matchesCount)
1100                 result->addItem(buildObjectForSearchResult(resourceData->requestId(), resourceData->frameId(), resourceData->url(), matchesCount));
1101         }
1102     }
1103 }
1104
1105 void InspectorNetworkAgent::searchInRequest(ErrorString& errorString, const String& requestId, const String& query, bool caseSensitive, bool isRegex, RefPtr<JSON::ArrayOf<Inspector::Protocol::GenericTypes::SearchMatch>>& results)
1106 {
1107     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
1108     if (!resourceData) {
1109         errorString = "No resource with given identifier found"_s;
1110         return;
1111     }
1112
1113     if (!resourceData->hasContent()) {
1114         errorString = "No resource content"_s;
1115         return;
1116     }
1117
1118     results = ContentSearchUtilities::searchInTextByLines(resourceData->content(), query, caseSensitive, isRegex);
1119 }
1120
1121 void InspectorNetworkAgent::mainFrameNavigated(DocumentLoader& loader)
1122 {
1123     m_resourcesData->clear(loaderIdentifier(&loader));
1124 }
1125
1126 } // namespace WebCore