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
90typedef Inspector::NetworkBackendDispatcherHandler::LoadResourceCallback LoadResourceCallback;
91
92namespace WebCore {
93
94using namespace Inspector;
95
96namespace {
97
98class InspectorThreadableLoaderClient final : public ThreadableLoaderClient {
99 WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient);
100public:
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
155private:
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
172InspectorNetworkAgent::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
181void InspectorNetworkAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
182{
183}
184
185void InspectorNetworkAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
186{
187 ErrorString unused;
188 disable(unused);
189}
190
191static 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
200Ref<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
224static 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
241Ref<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
281static 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
295static 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
318RefPtr<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
374Ref<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
392InspectorNetworkAgent::~InspectorNetworkAgent()
393{
394 if (m_enabled) {
395 ErrorString unused;
396 disable(unused);
397 }
398 ASSERT(!m_instrumentingAgents.inspectorNetworkAgent());
399}
400
401double InspectorNetworkAgent::timestamp()
402{
403 return m_environment.executionStopwatch()->elapsedTime().seconds();
404}
405
406void 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
450static InspectorPageAgent::ResourceType resourceTypeForCachedResource(CachedResource* resource)
451{
452 if (resource)
453 return InspectorPageAgent::inspectorResourceType(*resource);
454 return InspectorPageAgent::OtherResource;
455}
456
457static 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
470void 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
476void InspectorNetworkAgent::willSendRequestOfType(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, InspectorInstrumentation::LoadType loadType)
477{
478 willSendRequest(identifier, loader, request, ResourceResponse(), resourceTypeForLoadType(loadType));
479}
480
481void 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
536void 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
556void 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
591void 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
611void 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
633void InspectorNetworkAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
634{
635 m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString);
636}
637
638void InspectorNetworkAgent::didReceiveScriptResponse(unsigned long identifier)
639{
640 m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource);
641}
642
643void 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
652void InspectorNetworkAgent::willLoadXHRSynchronously()
653{
654 m_loadingXHRSynchronously = true;
655}
656
657void InspectorNetworkAgent::didLoadXHRSynchronously()
658{
659 m_loadingXHRSynchronously = false;
660}
661
662void 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
677void InspectorNetworkAgent::willRecalculateStyle()
678{
679 m_isRecalculatingStyle = true;
680}
681
682void InspectorNetworkAgent::didRecalculateStyle()
683{
684 m_isRecalculatingStyle = false;
685 m_styleRecalculationInitiator = nullptr;
686}
687
688void InspectorNetworkAgent::didScheduleStyleRecalculation(Document& document)
689{
690 if (!m_styleRecalculationInitiator)
691 m_styleRecalculationInitiator = buildInitiatorObject(&document);
692}
693
694RefPtr<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
743void InspectorNetworkAgent::didCreateWebSocket(unsigned long identifier, const URL& requestURL)
744{
745 m_frontendDispatcher->webSocketCreated(IdentifiersFactory::requestId(identifier), requestURL.string());
746}
747
748void 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
756void 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
766void InspectorNetworkAgent::didCloseWebSocket(unsigned long identifier)
767{
768 m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp());
769}
770
771void 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
782void 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
793void InspectorNetworkAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
794{
795 m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage);
796}
797
798void InspectorNetworkAgent::enable(ErrorString&)
799{
800 enable();
801}
802
803void 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
833void 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
843void 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
852void 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
885void InspectorNetworkAgent::setResourceCachingDisabled(ErrorString&, bool disabled)
886{
887 setResourceCachingDisabled(disabled);
888}
889
890void 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
926void 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
945WebSocket* 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
959static JSC::JSValue webSocketAsScriptValue(JSC::ExecState& state, WebSocket* webSocket)
960{
961 JSC::JSLockHolder lock(&state);
962 return toJS(&state, deprecatedGlobalObjectForPrototype(&state), webSocket);
963}
964
965void 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
992bool 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
1001Ref<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
1017Optional<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
1032bool 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
1071static 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
1082static 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
1093void 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
1105void 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
1121void InspectorNetworkAgent::mainFrameNavigated(DocumentLoader& loader)
1122{
1123 m_resourcesData->clear(loaderIdentifier(&loader));
1124}
1125
1126} // namespace WebCore
1127