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