Move URL from WebCore to WTF
[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     return metrics;
268 }
269
270 static Ref<Inspector::Protocol::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request)
271 {
272     auto requestObject = Inspector::Protocol::Network::Request::create()
273         .setUrl(request.url().string())
274         .setMethod(request.httpMethod())
275         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
276         .release();
277     if (request.httpBody() && !request.httpBody()->isEmpty()) {
278         auto bytes = request.httpBody()->flatten();
279         requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size()));
280     }
281     return requestObject;
282 }
283
284 static Inspector::Protocol::Network::Response::Source responseSource(ResourceResponse::Source source)
285 {
286     switch (source) {
287     case ResourceResponse::Source::ApplicationCache:
288         // FIXME: Add support for ApplicationCache in inspector.
289     case ResourceResponse::Source::Unknown:
290         return Inspector::Protocol::Network::Response::Source::Unknown;
291     case ResourceResponse::Source::Network:
292         return Inspector::Protocol::Network::Response::Source::Network;
293     case ResourceResponse::Source::MemoryCache:
294     case ResourceResponse::Source::MemoryCacheAfterValidation:
295         return Inspector::Protocol::Network::Response::Source::MemoryCache;
296     case ResourceResponse::Source::DiskCache:
297     case ResourceResponse::Source::DiskCacheAfterValidation:
298         return Inspector::Protocol::Network::Response::Source::DiskCache;
299     case ResourceResponse::Source::ServiceWorker:
300         return Inspector::Protocol::Network::Response::Source::ServiceWorker;
301     }
302
303     ASSERT_NOT_REACHED();
304     return Inspector::Protocol::Network::Response::Source::Unknown;
305 }
306
307 RefPtr<Inspector::Protocol::Network::Response> InspectorNetworkAgent::buildObjectForResourceResponse(const ResourceResponse& response, ResourceLoader* resourceLoader)
308 {
309     if (response.isNull())
310         return nullptr;
311
312     Ref<JSON::Object> headers = buildObjectForHeaders(response.httpHeaderFields());
313
314     auto responseObject = Inspector::Protocol::Network::Response::create()
315         .setUrl(response.url().string())
316         .setStatus(response.httpStatusCode())
317         .setStatusText(response.httpStatusText())
318         .setHeaders(WTFMove(headers))
319         .setMimeType(response.mimeType())
320         .setSource(responseSource(response.source()))
321         .release();
322
323     if (resourceLoader)
324         responseObject->setTiming(buildObjectForTiming(response.deprecatedNetworkLoadMetrics(), *resourceLoader));
325
326     if (auto& certificateInfo = response.certificateInfo()) {
327         auto securityPayload = Inspector::Protocol::Security::Security::create()
328             .release();
329
330         if (auto certificateSummaryInfo = certificateInfo.value().summaryInfo()) {
331             auto certificatePayload = Inspector::Protocol::Security::Certificate::create()
332                 .release();
333
334             certificatePayload->setSubject(certificateSummaryInfo.value().subject);
335
336             if (auto validFrom = certificateSummaryInfo.value().validFrom)
337                 certificatePayload->setValidFrom(validFrom.seconds());
338
339             if (auto validUntil = certificateSummaryInfo.value().validUntil)
340                 certificatePayload->setValidUntil(validUntil.seconds());
341
342             auto dnsNamesPayload = JSON::ArrayOf<String>::create();
343             for (auto& dnsName : certificateSummaryInfo.value().dnsNames)
344                 dnsNamesPayload->addItem(dnsName);
345             if (dnsNamesPayload->length())
346                 certificatePayload->setDnsNames(WTFMove(dnsNamesPayload));
347
348             auto ipAddressesPayload = JSON::ArrayOf<String>::create();
349             for (auto& ipAddress : certificateSummaryInfo.value().ipAddresses)
350                 ipAddressesPayload->addItem(ipAddress);
351             if (ipAddressesPayload->length())
352                 certificatePayload->setDnsNames(WTFMove(ipAddressesPayload));
353
354             securityPayload->setCertificate(WTFMove(certificatePayload));
355         }
356
357         responseObject->setSecurity(WTFMove(securityPayload));
358     }
359
360     return WTFMove(responseObject);
361 }
362
363 Ref<Inspector::Protocol::Network::CachedResource> InspectorNetworkAgent::buildObjectForCachedResource(CachedResource* cachedResource)
364 {
365     auto resourceObject = Inspector::Protocol::Network::CachedResource::create()
366         .setUrl(cachedResource->url())
367         .setType(InspectorPageAgent::cachedResourceTypeJSON(*cachedResource))
368         .setBodySize(cachedResource->encodedSize())
369         .release();
370
371     auto resourceResponse = buildObjectForResourceResponse(cachedResource->response(), cachedResource->loader());
372     resourceObject->setResponse(WTFMove(resourceResponse));
373
374     String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
375     if (!sourceMappingURL.isEmpty())
376         resourceObject->setSourceMapURL(sourceMappingURL);
377
378     return resourceObject;
379 }
380
381 InspectorNetworkAgent::~InspectorNetworkAgent()
382 {
383     if (m_enabled) {
384         ErrorString unused;
385         disable(unused);
386     }
387     ASSERT(!m_instrumentingAgents.inspectorNetworkAgent());
388 }
389
390 double InspectorNetworkAgent::timestamp()
391 {
392     return m_environment.executionStopwatch()->elapsedTime().seconds();
393 }
394
395 void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse, InspectorPageAgent::ResourceType type)
396 {
397     if (request.hiddenFromInspector()) {
398         m_hiddenRequestIdentifiers.add(identifier);
399         return;
400     }
401
402     double sendTimestamp = timestamp();
403     WallTime walltime = WallTime::now();
404
405     String requestId = IdentifiersFactory::requestId(identifier);
406     String frameId = frameIdentifier(loader);
407     String loaderId = loaderIdentifier(loader);
408     String targetId = request.initiatorIdentifier();
409
410     if (type == InspectorPageAgent::OtherResource) {
411         if (m_loadingXHRSynchronously)
412             type = InspectorPageAgent::XHRResource;
413         else if (loader && equalIgnoringFragmentIdentifier(request.url(), loader->url()) && !loader->isCommitted())
414             type = InspectorPageAgent::DocumentResource;
415         else if (loader) {
416             for (auto& linkIcon : loader->linkIcons()) {
417                 if (equalIgnoringFragmentIdentifier(request.url(), linkIcon.url)) {
418                     type = InspectorPageAgent::ImageResource;
419                     break;
420                 }
421             }
422         }
423     }
424
425     m_resourcesData->resourceCreated(requestId, loaderId, type);
426
427     for (auto& entry : m_extraRequestHeaders)
428         request.setHTTPHeaderField(entry.key, entry.value);
429
430     auto protocolResourceType = InspectorPageAgent::resourceTypeJSON(type);
431
432     Document* document = loader && loader->frame() ? loader->frame()->document() : nullptr;
433     auto initiatorObject = buildInitiatorObject(document, request);
434
435     String url = loader ? loader->url().string() : request.url();
436     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);
437 }
438
439 static InspectorPageAgent::ResourceType resourceTypeForCachedResource(CachedResource* resource)
440 {
441     if (resource)
442         return InspectorPageAgent::inspectorResourceType(*resource);
443     return InspectorPageAgent::OtherResource;
444 }
445
446 static InspectorPageAgent::ResourceType resourceTypeForLoadType(InspectorInstrumentation::LoadType loadType)
447 {
448     switch (loadType) {
449     case InspectorInstrumentation::LoadType::Ping:
450         return InspectorPageAgent::PingResource;
451     case InspectorInstrumentation::LoadType::Beacon:
452         return InspectorPageAgent::BeaconResource;
453     }
454
455     ASSERT_NOT_REACHED();
456     return InspectorPageAgent::OtherResource;
457 }
458
459 void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
460 {
461     auto* cachedResource = loader ? InspectorPageAgent::cachedResource(loader->frame(), request.url()) : nullptr;
462     willSendRequest(identifier, loader, request, redirectResponse, resourceTypeForCachedResource(cachedResource));
463 }
464
465 void InspectorNetworkAgent::willSendRequestOfType(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, InspectorInstrumentation::LoadType loadType)
466 {
467     willSendRequest(identifier, loader, request, ResourceResponse(), resourceTypeForLoadType(loadType));
468 }
469
470 void InspectorNetworkAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
471 {
472     if (m_hiddenRequestIdentifiers.contains(identifier))
473         return;
474
475     String requestId = IdentifiersFactory::requestId(identifier);
476
477     std::optional<ResourceResponse> realResponse;
478     if (platformStrategies()->loaderStrategy()->havePerformedSecurityChecks(response)) {
479         callOnMainThreadAndWait([&] {
480             // We do not need to isolate response since it comes straight from IPC, but we might want to isolate it for extra safety.
481             auto response = platformStrategies()->loaderStrategy()->responseFromResourceLoadIdentifier(identifier);
482             if (!response.isNull())
483                 realResponse = WTFMove(response);
484         });
485     }
486
487     RefPtr<Inspector::Protocol::Network::Response> resourceResponse = buildObjectForResourceResponse(realResponse ? *realResponse : response, resourceLoader);
488
489     bool isNotModified = response.httpStatusCode() == 304;
490
491     CachedResource* cachedResource = nullptr;
492     if (resourceLoader && resourceLoader->isSubresourceLoader() && !isNotModified)
493         cachedResource = static_cast<SubresourceLoader*>(resourceLoader)->cachedResource();
494     if (!cachedResource && loader)
495         cachedResource = InspectorPageAgent::cachedResource(loader->frame(), response.url());
496
497     if (cachedResource) {
498         // Use mime type from cached resource in case the one in response is empty.
499         if (resourceResponse && response.mimeType().isEmpty())
500             resourceResponse->setString(Inspector::Protocol::Network::Response::MimeType, cachedResource->response().mimeType());
501         m_resourcesData->addCachedResource(requestId, cachedResource);
502     }
503
504     InspectorPageAgent::ResourceType type = m_resourcesData->resourceType(requestId);
505     InspectorPageAgent::ResourceType newType = cachedResource ? InspectorPageAgent::inspectorResourceType(*cachedResource) : type;
506
507     // FIXME: XHRResource is returned for CachedResource::Type::RawResource, it should be OtherResource unless it truly is an XHR.
508     // RawResource is used for loading worker scripts, and those should stay as ScriptResource and not change to XHRResource.
509     if (type != newType && newType != InspectorPageAgent::XHRResource && newType != InspectorPageAgent::OtherResource)
510         type = newType;
511
512     String frameId = frameIdentifier(loader);
513     String loaderId = loaderIdentifier(loader);
514
515     m_resourcesData->responseReceived(requestId, frameId, response, type, shouldForceBufferingNetworkResourceData());
516
517     m_frontendDispatcher->responseReceived(requestId, frameId, loaderId, timestamp(), InspectorPageAgent::resourceTypeJSON(type), resourceResponse);
518
519     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
520     // as there will be no calls to didReceiveData from the network stack.
521     if (isNotModified && cachedResource && cachedResource->encodedSize())
522         didReceiveData(identifier, nullptr, cachedResource->encodedSize(), 0);
523 }
524
525 void InspectorNetworkAgent::didReceiveData(unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
526 {
527     if (m_hiddenRequestIdentifiers.contains(identifier))
528         return;
529
530     String requestId = IdentifiersFactory::requestId(identifier);
531
532     if (data) {
533         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
534
535         // For a synchronous XHR, if we didn't add data then we can apply it here as base64 encoded content.
536         // Often the data is text and we would have a decoder, but for non-text we won't have a decoder.
537         // Sync XHRs may not have a cached resource, while non-sync XHRs usually transfer data over on completion.
538         if (m_loadingXHRSynchronously && resourceData && !resourceData->hasBufferedData() && !resourceData->cachedResource())
539             m_resourcesData->setResourceContent(requestId, base64Encode(data, dataLength), true);
540     }
541
542     m_frontendDispatcher->dataReceived(requestId, timestamp(), dataLength, encodedDataLength);
543 }
544
545 void InspectorNetworkAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, const NetworkLoadMetrics& networkLoadMetrics, ResourceLoader* resourceLoader)
546 {
547     if (m_hiddenRequestIdentifiers.remove(identifier))
548         return;
549
550     double elapsedFinishTime;
551     if (resourceLoader && networkLoadMetrics.isComplete()) {
552         MonotonicTime fetchStart = resourceLoader->loadTiming().fetchStart();
553         Seconds fetchStartInInspector = m_environment.executionStopwatch()->elapsedTimeSince(fetchStart);
554         elapsedFinishTime = (fetchStartInInspector + networkLoadMetrics.responseEnd).seconds();
555     } else
556         elapsedFinishTime = timestamp();
557
558     String requestId = IdentifiersFactory::requestId(identifier);
559     if (loader && m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource)
560         m_resourcesData->addResourceSharedBuffer(requestId, loader->frameLoader()->documentLoader()->mainResourceData(), loader->frame()->document()->encoding());
561
562     m_resourcesData->maybeDecodeDataToContent(requestId);
563
564     String sourceMappingURL;
565     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
566     if (resourceData && resourceData->cachedResource())
567         sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(resourceData->cachedResource());
568
569     std::optional<NetworkLoadMetrics> realMetrics;
570     if (platformStrategies()->loaderStrategy()->shouldPerformSecurityChecks() && !networkLoadMetrics.isComplete()) {
571         callOnMainThreadAndWait([&] {
572             realMetrics = platformStrategies()->loaderStrategy()->networkMetricsFromResourceLoadIdentifier(identifier).isolatedCopy();
573         });
574     }
575     RefPtr<Inspector::Protocol::Network::Metrics> metrics = buildObjectForMetrics(realMetrics ? *realMetrics : networkLoadMetrics);
576
577     m_frontendDispatcher->loadingFinished(requestId, elapsedFinishTime, !sourceMappingURL.isEmpty() ? &sourceMappingURL : nullptr, metrics);
578 }
579
580 void InspectorNetworkAgent::didFailLoading(unsigned long identifier, DocumentLoader* loader, const ResourceError& error)
581 {
582     if (m_hiddenRequestIdentifiers.remove(identifier))
583         return;
584
585     String requestId = IdentifiersFactory::requestId(identifier);
586
587     if (loader && m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource) {
588         Frame* frame = loader->frame();
589         if (frame && frame->loader().documentLoader() && frame->document()) {
590             m_resourcesData->addResourceSharedBuffer(requestId,
591                 frame->loader().documentLoader()->mainResourceData(),
592                 frame->document()->encoding());
593         }
594     }
595
596     bool canceled = error.isCancellation();
597     m_frontendDispatcher->loadingFailed(requestId, timestamp(), error.localizedDescription(), canceled ? &canceled : nullptr);
598 }
599
600 void InspectorNetworkAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, CachedResource& resource)
601 {
602     ASSERT(loader);
603     if (!loader)
604         return;
605
606     unsigned long identifier = loader->frame()->page()->progress().createUniqueIdentifier();
607     String requestId = IdentifiersFactory::requestId(identifier);
608     String loaderId = loaderIdentifier(loader);
609     String frameId = frameIdentifier(loader);
610
611     m_resourcesData->resourceCreated(requestId, loaderId, resource);
612
613     auto initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : nullptr, resource.resourceRequest());
614
615     // FIXME: It would be ideal to generate the Network.Response with the MemoryCache source
616     // instead of whatever ResourceResponse::Source the CachedResources's response has.
617     // The frontend already knows for certain that this was served from the memory cache.
618
619     m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader->url().string(), timestamp(), initiatorObject, buildObjectForCachedResource(&resource));
620 }
621
622 void InspectorNetworkAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
623 {
624     m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
625 }
626
627 void InspectorNetworkAgent::didReceiveScriptResponse(unsigned long identifier)
628 {
629     m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
630 }
631
632 void InspectorNetworkAgent::didReceiveThreadableLoaderResponse(unsigned long identifier, DocumentThreadableLoader& documentThreadableLoader)
633 {
634     String initiator = documentThreadableLoader.options().initiator;
635     if (initiator == cachedResourceRequestInitiators().fetch)
636         m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::FetchResource);
637     else if (initiator == cachedResourceRequestInitiators().xmlhttprequest)
638         m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource);
639 }
640
641 void InspectorNetworkAgent::willLoadXHRSynchronously()
642 {
643     m_loadingXHRSynchronously = true;
644 }
645
646 void InspectorNetworkAgent::didLoadXHRSynchronously()
647 {
648     m_loadingXHRSynchronously = false;
649 }
650
651 void InspectorNetworkAgent::willDestroyCachedResource(CachedResource& cachedResource)
652 {
653     Vector<String> requestIds = m_resourcesData->removeCachedResource(&cachedResource);
654     if (!requestIds.size())
655         return;
656
657     String content;
658     bool base64Encoded;
659     if (!InspectorNetworkAgent::cachedResourceContent(cachedResource, &content, &base64Encoded))
660         return;
661
662     for (auto& id : requestIds)
663         m_resourcesData->setResourceContent(id, content, base64Encoded);
664 }
665
666 void InspectorNetworkAgent::willRecalculateStyle()
667 {
668     m_isRecalculatingStyle = true;
669 }
670
671 void InspectorNetworkAgent::didRecalculateStyle()
672 {
673     m_isRecalculatingStyle = false;
674     m_styleRecalculationInitiator = nullptr;
675 }
676
677 void InspectorNetworkAgent::didScheduleStyleRecalculation(Document& document)
678 {
679     if (!m_styleRecalculationInitiator)
680         m_styleRecalculationInitiator = buildInitiatorObject(&document);
681 }
682
683 RefPtr<Inspector::Protocol::Network::Initiator> InspectorNetworkAgent::buildInitiatorObject(Document* document, std::optional<const ResourceRequest&> resourceRequest)
684 {
685     // FIXME: Worker support.
686     if (!isMainThread()) {
687         return Inspector::Protocol::Network::Initiator::create()
688             .setType(Inspector::Protocol::Network::Initiator::Type::Other)
689             .release();
690     }
691
692     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject;
693
694     Ref<ScriptCallStack> stackTrace = createScriptCallStack(JSExecState::currentState());
695     if (stackTrace->size() > 0) {
696         initiatorObject = Inspector::Protocol::Network::Initiator::create()
697             .setType(Inspector::Protocol::Network::Initiator::Type::Script)
698             .release();
699         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
700     } else if (document && document->scriptableDocumentParser()) {
701         initiatorObject = Inspector::Protocol::Network::Initiator::create()
702             .setType(Inspector::Protocol::Network::Initiator::Type::Parser)
703             .release();
704         initiatorObject->setUrl(document->url().string());
705         initiatorObject->setLineNumber(document->scriptableDocumentParser()->textPosition().m_line.oneBasedInt());
706     }
707
708     auto domAgent = m_instrumentingAgents.inspectorDOMAgent();
709     if (domAgent && resourceRequest) {
710         if (auto inspectorInitiatorNodeIdentifier = resourceRequest->inspectorInitiatorNodeIdentifier()) {
711             if (!initiatorObject) {
712                 initiatorObject = Inspector::Protocol::Network::Initiator::create()
713                     .setType(Inspector::Protocol::Network::Initiator::Type::Other)
714                     .release();
715             }
716
717             initiatorObject->setNodeId(*inspectorInitiatorNodeIdentifier);
718         }
719     }
720
721     if (initiatorObject)
722         return initiatorObject;
723
724     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
725         return m_styleRecalculationInitiator;
726
727     return Inspector::Protocol::Network::Initiator::create()
728         .setType(Inspector::Protocol::Network::Initiator::Type::Other)
729         .release();
730 }
731
732 void InspectorNetworkAgent::didCreateWebSocket(unsigned long identifier, const URL& requestURL)
733 {
734     m_frontendDispatcher->webSocketCreated(IdentifiersFactory::requestId(identifier), requestURL.string());
735 }
736
737 void InspectorNetworkAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const ResourceRequest& request)
738 {
739     auto requestObject = Inspector::Protocol::Network::WebSocketRequest::create()
740         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
741         .release();
742     m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), timestamp(), WallTime::now().secondsSinceEpoch().seconds(), WTFMove(requestObject));
743 }
744
745 void InspectorNetworkAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const ResourceResponse& response)
746 {
747     auto responseObject = Inspector::Protocol::Network::WebSocketResponse::create()
748         .setStatus(response.httpStatusCode())
749         .setStatusText(response.httpStatusText())
750         .setHeaders(buildObjectForHeaders(response.httpHeaderFields()))
751         .release();
752     m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(responseObject));
753 }
754
755 void InspectorNetworkAgent::didCloseWebSocket(unsigned long identifier)
756 {
757     m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp());
758 }
759
760 void InspectorNetworkAgent::didReceiveWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
761 {
762     auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create()
763         .setOpcode(frame.opCode)
764         .setMask(frame.masked)
765         .setPayloadData(String::fromUTF8WithLatin1Fallback(frame.payload, frame.payloadLength))
766         .setPayloadLength(frame.payloadLength)
767         .release();
768     m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject));
769 }
770
771 void InspectorNetworkAgent::didSendWebSocketFrame(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->webSocketFrameSent(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject));
780 }
781
782 void InspectorNetworkAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
783 {
784     m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage);
785 }
786
787 void InspectorNetworkAgent::enable(ErrorString&)
788 {
789     enable();
790 }
791
792 void InspectorNetworkAgent::enable()
793 {
794     m_enabled = true;
795     m_instrumentingAgents.setInspectorNetworkAgent(this);
796
797     {
798         LockHolder lock(WebSocket::allActiveWebSocketsMutex());
799
800         for (WebSocket* webSocket : activeWebSockets(lock)) {
801             ASSERT(is<WebSocketChannel>(webSocket->channel().get()));
802             WebSocketChannel* channel = downcast<WebSocketChannel>(webSocket->channel().get());
803
804             unsigned identifier = channel->identifier();
805             didCreateWebSocket(identifier, webSocket->url());
806             willSendWebSocketHandshakeRequest(identifier, channel->clientHandshakeRequest());
807
808             if (channel->handshakeMode() == WebSocketHandshake::Connected)
809                 didReceiveWebSocketHandshakeResponse(identifier, channel->serverHandshakeResponse());
810
811             if (webSocket->readyState() == WebSocket::CLOSED)
812                 didCloseWebSocket(identifier);
813         }
814     }
815 }
816
817 void InspectorNetworkAgent::disable(ErrorString&)
818 {
819     m_enabled = false;
820     m_instrumentingAgents.setInspectorNetworkAgent(nullptr);
821     m_resourcesData->clear();
822     m_extraRequestHeaders.clear();
823
824     setResourceCachingDisabled(false);
825 }
826
827 void InspectorNetworkAgent::setExtraHTTPHeaders(ErrorString&, const JSON::Object& headers)
828 {
829     for (auto& entry : headers) {
830         String stringValue;
831         if (entry.value->asString(stringValue))
832             m_extraRequestHeaders.set(entry.key, stringValue);
833     }
834 }
835
836 void InspectorNetworkAgent::getResponseBody(ErrorString& errorString, const String& requestId, String* content, bool* base64Encoded)
837 {
838     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
839     if (!resourceData) {
840         errorString = "No resource with given identifier found"_s;
841         return;
842     }
843
844     if (resourceData->hasContent()) {
845         *base64Encoded = resourceData->base64Encoded();
846         *content = resourceData->content();
847         return;
848     }
849
850     if (resourceData->isContentEvicted()) {
851         errorString = "Request content was evicted from inspector cache"_s;
852         return;
853     }
854
855     if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) {
856         *base64Encoded = false;
857         if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content))
858             return;
859     }
860
861     if (resourceData->cachedResource()) {
862         if (InspectorNetworkAgent::cachedResourceContent(*resourceData->cachedResource(), content, base64Encoded))
863             return;
864     }
865
866     errorString = "No data found for resource with given identifier"_s;
867 }
868
869 void InspectorNetworkAgent::setResourceCachingDisabled(ErrorString&, bool disabled)
870 {
871     setResourceCachingDisabled(disabled);
872 }
873
874 void InspectorNetworkAgent::loadResource(const String& frameId, const String& urlString, Ref<LoadResourceCallback>&& callback)
875 {
876     ErrorString error;
877     auto* context = scriptExecutionContext(error, frameId);
878     if (!context) {
879         callback->sendFailure(error);
880         return;
881     }
882
883     URL url = context->completeURL(urlString);
884     ResourceRequest request(url);
885     request.setHTTPMethod("GET"_s);
886     request.setHiddenFromInspector(true);
887
888     ThreadableLoaderOptions options;
889     options.sendLoadCallbacks = SendCallbackPolicy::SendCallbacks; // So we remove this from m_hiddenRequestIdentifiers on completion.
890     options.defersLoadingPolicy = DefersLoadingPolicy::DisallowDefersLoading; // So the request is never deferred.
891     options.mode = FetchOptions::Mode::NoCors;
892     options.credentials = FetchOptions::Credentials::SameOrigin;
893     options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce;
894
895     // InspectorThreadableLoaderClient deletes itself when the load completes or fails.
896     InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback.copyRef());
897     auto loader = ThreadableLoader::create(*context, *inspectorThreadableLoaderClient, WTFMove(request), options);
898     if (!loader) {
899         callback->sendFailure("Could not load requested resource."_s);
900         return;
901     }
902
903     // If the load already completed, inspectorThreadableLoaderClient will have been deleted and we will have already called the callback.
904     if (!callback->isActive())
905         return;
906
907     inspectorThreadableLoaderClient->setLoader(WTFMove(loader));
908 }
909
910 void InspectorNetworkAgent::getSerializedCertificate(ErrorString& errorString, const String& requestId, String* serializedCertificate)
911 {
912     auto* resourceData = m_resourcesData->data(requestId);
913     if (!resourceData) {
914         errorString = "No resource with given identifier found"_s;
915         return;
916     }
917
918     auto& certificate = resourceData->certificateInfo();
919     if (!certificate || certificate.value().isEmpty()) {
920         errorString = "No certificate for resource"_s;
921         return;
922     }
923
924     WTF::Persistence::Encoder encoder;
925     encoder << certificate.value();
926     *serializedCertificate = base64Encode(encoder.buffer(), encoder.bufferSize());
927 }
928
929 WebSocket* InspectorNetworkAgent::webSocketForRequestId(const String& requestId)
930 {
931     LockHolder lock(WebSocket::allActiveWebSocketsMutex());
932
933     for (WebSocket* webSocket : activeWebSockets(lock)) {
934         ASSERT(is<WebSocketChannel>(webSocket->channel().get()));
935         WebSocketChannel* channel = downcast<WebSocketChannel>(webSocket->channel().get());
936         if (IdentifiersFactory::requestId(channel->identifier()) == requestId)
937             return webSocket;
938     }
939
940     return nullptr;
941 }
942
943 static JSC::JSValue webSocketAsScriptValue(JSC::ExecState& state, WebSocket* webSocket)
944 {
945     JSC::JSLockHolder lock(&state);
946     return toJS(&state, deprecatedGlobalObjectForPrototype(&state), webSocket);
947 }
948
949 void InspectorNetworkAgent::resolveWebSocket(ErrorString& errorString, const String& requestId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
950 {
951     WebSocket* webSocket = webSocketForRequestId(requestId);
952     if (!webSocket) {
953         errorString = "WebSocket not found"_s;
954         return;
955     }
956
957     // FIXME: <https://webkit.org/b/168475> Web Inspector: Correctly display iframe's and worker's WebSockets
958     if (!is<Document>(webSocket->scriptExecutionContext()))
959         return;
960
961     auto* document = downcast<Document>(webSocket->scriptExecutionContext());
962     auto* frame = document->frame();
963     if (!frame) {
964         errorString = "WebSocket belongs to document without a frame"_s;
965         return;
966     }
967
968     auto& state = *mainWorldExecState(frame);
969     auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
970     ASSERT(!injectedScript.hasNoValue());
971
972     String objectGroupName = objectGroup ? *objectGroup : String();
973     result = injectedScript.wrapObject(webSocketAsScriptValue(state, webSocket), objectGroupName);
974 }
975
976 bool InspectorNetworkAgent::shouldTreatAsText(const String& mimeType)
977 {
978     return startsWithLettersIgnoringASCIICase(mimeType, "text/")
979         || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType)
980         || MIMETypeRegistry::isSupportedJSONMIMEType(mimeType)
981         || MIMETypeRegistry::isXMLMIMEType(mimeType);
982 }
983
984 Ref<TextResourceDecoder> InspectorNetworkAgent::createTextDecoder(const String& mimeType, const String& textEncodingName)
985 {
986     if (!textEncodingName.isEmpty())
987         return TextResourceDecoder::create("text/plain"_s, textEncodingName);
988
989     if (MIMETypeRegistry::isTextMIMEType(mimeType))
990         return TextResourceDecoder::create(mimeType, "UTF-8");
991     if (MIMETypeRegistry::isXMLMIMEType(mimeType)) {
992         auto decoder = TextResourceDecoder::create("application/xml"_s);
993         decoder->useLenientXMLDecoding();
994         return decoder;
995     }
996
997     return TextResourceDecoder::create("text/plain"_s, "UTF-8");
998 }
999
1000 std::optional<String> InspectorNetworkAgent::textContentForCachedResource(CachedResource& cachedResource)
1001 {
1002     if (!InspectorNetworkAgent::shouldTreatAsText(cachedResource.mimeType()))
1003         return std::nullopt;
1004
1005     String result;
1006     bool base64Encoded;
1007     if (InspectorNetworkAgent::cachedResourceContent(cachedResource, &result, &base64Encoded)) {
1008         ASSERT(!base64Encoded);
1009         return result;
1010     }
1011
1012     return std::nullopt;
1013 }
1014
1015 bool InspectorNetworkAgent::cachedResourceContent(CachedResource& resource, String* result, bool* base64Encoded)
1016 {
1017     ASSERT(result);
1018     ASSERT(base64Encoded);
1019
1020     if (!resource.encodedSize()) {
1021         *base64Encoded = false;
1022         *result = String();
1023         return true;
1024     }
1025
1026     switch (resource.type()) {
1027     case CachedResource::Type::CSSStyleSheet:
1028         *base64Encoded = false;
1029         *result = downcast<CachedCSSStyleSheet>(resource).sheetText();
1030         // The above can return a null String if the MIME type is invalid.
1031         return !result->isNull();
1032     case CachedResource::Type::Script:
1033         *base64Encoded = false;
1034         *result = downcast<CachedScript>(resource).script().toString();
1035         return true;
1036     default:
1037         auto* buffer = resource.resourceBuffer();
1038         if (!buffer)
1039             return false;
1040
1041         if (InspectorNetworkAgent::shouldTreatAsText(resource.mimeType())) {
1042             auto decoder = InspectorNetworkAgent::createTextDecoder(resource.mimeType(), resource.response().textEncodingName());
1043             *base64Encoded = false;
1044             *result = decoder->decodeAndFlush(buffer->data(), buffer->size());
1045             return true;
1046         }
1047
1048         *base64Encoded = true;
1049         *result = base64Encode(buffer->data(), buffer->size());
1050         return true;
1051     }
1052 }
1053
1054 static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& requestId, const String& frameId, const String& url, int matchesCount)
1055 {
1056     auto searchResult = Inspector::Protocol::Page::SearchResult::create()
1057         .setUrl(url)
1058         .setFrameId(frameId)
1059         .setMatchesCount(matchesCount)
1060         .release();
1061     searchResult->setRequestId(requestId);
1062     return searchResult;
1063 }
1064
1065 static std::optional<String> textContentForResourceData(const NetworkResourcesData::ResourceData& resourceData)
1066 {
1067     if (resourceData.hasContent() && !resourceData.base64Encoded())
1068         return resourceData.content();
1069
1070     if (resourceData.cachedResource())
1071         return InspectorNetworkAgent::textContentForCachedResource(*resourceData.cachedResource());
1072
1073     return std::nullopt;
1074 }
1075
1076 void InspectorNetworkAgent::searchOtherRequests(const JSC::Yarr::RegularExpression& regex, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::SearchResult>>& result)
1077 {
1078     Vector<NetworkResourcesData::ResourceData*> resources = m_resourcesData->resources();
1079     for (auto* resourceData : resources) {
1080         if (auto textContent = textContentForResourceData(*resourceData)) {
1081             int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, resourceData->content());
1082             if (matchesCount)
1083                 result->addItem(buildObjectForSearchResult(resourceData->requestId(), resourceData->frameId(), resourceData->url(), matchesCount));
1084         }
1085     }
1086 }
1087
1088 void InspectorNetworkAgent::searchInRequest(ErrorString& errorString, const String& requestId, const String& query, bool caseSensitive, bool isRegex, RefPtr<JSON::ArrayOf<Inspector::Protocol::GenericTypes::SearchMatch>>& results)
1089 {
1090     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
1091     if (!resourceData) {
1092         errorString = "No resource with given identifier found"_s;
1093         return;
1094     }
1095
1096     if (!resourceData->hasContent()) {
1097         errorString = "No resource content"_s;
1098         return;
1099     }
1100
1101     results = ContentSearchUtilities::searchInTextByLines(resourceData->content(), query, caseSensitive, isRegex);
1102 }
1103
1104 void InspectorNetworkAgent::mainFrameNavigated(DocumentLoader& loader)
1105 {
1106     m_resourcesData->clear(loaderIdentifier(&loader));
1107 }
1108
1109 } // namespace WebCore