diff options
Diffstat (limited to 'Source/WebCore/inspector/InspectorNetworkAgent.cpp')
-rw-r--r-- | Source/WebCore/inspector/InspectorNetworkAgent.cpp | 740 |
1 files changed, 740 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/InspectorNetworkAgent.cpp b/Source/WebCore/inspector/InspectorNetworkAgent.cpp new file mode 100644 index 000000000..569ce4a2c --- /dev/null +++ b/Source/WebCore/inspector/InspectorNetworkAgent.cpp @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorNetworkAgent.h" + +#include "CachedRawResource.h" +#include "CachedResource.h" +#include "CachedResourceLoader.h" +#include "CachedResourceRequestInitiators.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "DocumentThreadableLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTTPHeaderMap.h" +#include "HTTPHeaderNames.h" +#include "IconController.h" +#include "InspectorPageAgent.h" +#include "InspectorTimelineAgent.h" +#include "InstrumentingAgents.h" +#include "JSMainThreadExecState.h" +#include "MemoryCache.h" +#include "NetworkResourcesData.h" +#include "Page.h" +#include "ProgressTracker.h" +#include "ResourceError.h" +#include "ResourceLoader.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ScriptableDocumentParser.h" +#include "SubresourceLoader.h" +#include "ThreadableLoaderClient.h" +#include "URL.h" +#include "WebSocketFrame.h" +#include <inspector/ContentSearchUtilities.h> +#include <inspector/IdentifiersFactory.h> +#include <inspector/InspectorFrontendRouter.h> +#include <inspector/InspectorValues.h> +#include <inspector/ScriptCallStack.h> +#include <inspector/ScriptCallStackFactory.h> +#include <wtf/RefPtr.h> +#include <wtf/Stopwatch.h> +#include <wtf/text/StringBuilder.h> + +using namespace Inspector; + +typedef Inspector::NetworkBackendDispatcherHandler::LoadResourceCallback LoadResourceCallback; + +namespace WebCore { + +namespace { + +class InspectorThreadableLoaderClient final : public ThreadableLoaderClient { + WTF_MAKE_NONCOPYABLE(InspectorThreadableLoaderClient); +public: + InspectorThreadableLoaderClient(RefPtr<LoadResourceCallback>&& callback) + : m_callback(WTFMove(callback)) { } + + virtual ~InspectorThreadableLoaderClient() { } + + void didReceiveResponse(unsigned long, const ResourceResponse& response) override + { + m_mimeType = response.mimeType(); + m_statusCode = response.httpStatusCode(); + + // FIXME: This assumes text only responses. We should support non-text responses as well. + TextEncoding textEncoding(response.textEncodingName()); + bool useDetector = false; + if (!textEncoding.isValid()) { + textEncoding = UTF8Encoding(); + useDetector = true; + } + + m_decoder = TextResourceDecoder::create(ASCIILiteral("text/plain"), textEncoding, useDetector); + } + + void didReceiveData(const char* data, int dataLength) override + { + if (!dataLength) + return; + + if (dataLength == -1) + dataLength = strlen(data); + + m_responseText.append(m_decoder->decode(data, dataLength)); + } + + void didFinishLoading(unsigned long, double) override + { + if (m_decoder) + m_responseText.append(m_decoder->flush()); + + m_callback->sendSuccess(m_responseText.toString(), m_mimeType, m_statusCode); + dispose(); + } + + void didFail(const ResourceError& error) override + { + m_callback->sendFailure(error.isAccessControl() ? ASCIILiteral("Loading resource for inspector failed access control check") : ASCIILiteral("Loading resource for inspector failed")); + dispose(); + } + + void setLoader(RefPtr<ThreadableLoader>&& loader) + { + m_loader = WTFMove(loader); + } + +private: + void dispose() + { + m_loader = nullptr; + delete this; + } + + RefPtr<LoadResourceCallback> m_callback; + RefPtr<ThreadableLoader> m_loader; + RefPtr<TextResourceDecoder> m_decoder; + String m_mimeType; + StringBuilder m_responseText; + int m_statusCode; +}; + +} // namespace + +InspectorNetworkAgent::InspectorNetworkAgent(WebAgentContext& context, InspectorPageAgent* pageAgent) + : InspectorAgentBase(ASCIILiteral("Network"), context) + , m_frontendDispatcher(std::make_unique<Inspector::NetworkFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(Inspector::NetworkBackendDispatcher::create(context.backendDispatcher, this)) + , m_pageAgent(pageAgent) + , m_resourcesData(std::make_unique<NetworkResourcesData>()) +{ +} + +void InspectorNetworkAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) +{ +} + +void InspectorNetworkAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason) +{ + ErrorString unused; + disable(unused); +} + +static Ref<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers) +{ + Ref<InspectorObject> headersObject = InspectorObject::create(); + + for (const auto& header : headers) + headersObject->setString(header.key, header.value); + return headersObject; +} + +Ref<Inspector::Protocol::Network::ResourceTiming> InspectorNetworkAgent::buildObjectForTiming(const NetworkLoadTiming& timing, ResourceLoader& resourceLoader) +{ + MonotonicTime startTime = resourceLoader.loadTiming().startTime(); + double startTimeInInspector = m_environment.executionStopwatch()->elapsedTimeSince(startTime); + + return Inspector::Protocol::Network::ResourceTiming::create() + .setStartTime(startTimeInInspector) + .setDomainLookupStart(timing.domainLookupStart) + .setDomainLookupEnd(timing.domainLookupEnd) + .setConnectStart(timing.connectStart) + .setConnectEnd(timing.connectEnd) + .setSecureConnectionStart(timing.secureConnectionStart) + .setRequestStart(timing.requestStart) + .setResponseStart(timing.responseStart) + .release(); +} + +static Ref<Inspector::Protocol::Network::Request> buildObjectForResourceRequest(const ResourceRequest& request) +{ + auto requestObject = Inspector::Protocol::Network::Request::create() + .setUrl(request.url().string()) + .setMethod(request.httpMethod()) + .setHeaders(buildObjectForHeaders(request.httpHeaderFields())) + .release(); + if (request.httpBody() && !request.httpBody()->isEmpty()) { + Vector<char> bytes; + request.httpBody()->flatten(bytes); + requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size())); + } + return requestObject; +} + +RefPtr<Inspector::Protocol::Network::Response> InspectorNetworkAgent::buildObjectForResourceResponse(const ResourceResponse& response, ResourceLoader* resourceLoader) +{ + if (response.isNull()) + return nullptr; + + double status = response.httpStatusCode(); + Ref<InspectorObject> headers = buildObjectForHeaders(response.httpHeaderFields()); + + auto responseObject = Inspector::Protocol::Network::Response::create() + .setUrl(response.url().string()) + .setStatus(status) + .setStatusText(response.httpStatusText()) + .setHeaders(WTFMove(headers)) + .setMimeType(response.mimeType()) + .release(); + + responseObject->setFromDiskCache(response.source() == ResourceResponse::Source::DiskCache || response.source() == ResourceResponse::Source::DiskCacheAfterValidation); + if (resourceLoader) + responseObject->setTiming(buildObjectForTiming(response.networkLoadTiming(), *resourceLoader)); + + return WTFMove(responseObject); +} + +Ref<Inspector::Protocol::Network::CachedResource> InspectorNetworkAgent::buildObjectForCachedResource(CachedResource* cachedResource) +{ + auto resourceObject = Inspector::Protocol::Network::CachedResource::create() + .setUrl(cachedResource->url()) + .setType(InspectorPageAgent::cachedResourceTypeJson(*cachedResource)) + .setBodySize(cachedResource->encodedSize()) + .release(); + + auto resourceResponse = buildObjectForResourceResponse(cachedResource->response(), cachedResource->loader()); + resourceObject->setResponse(WTFMove(resourceResponse)); + + String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource); + if (!sourceMappingURL.isEmpty()) + resourceObject->setSourceMapURL(sourceMappingURL); + + return resourceObject; +} + +InspectorNetworkAgent::~InspectorNetworkAgent() +{ + if (m_enabled) { + ErrorString unused; + disable(unused); + } + ASSERT(!m_instrumentingAgents.inspectorNetworkAgent()); +} + +double InspectorNetworkAgent::timestamp() +{ + return m_environment.executionStopwatch()->elapsedTime(); +} + +void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLoader& loader, ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + if (request.hiddenFromInspector()) { + m_hiddenRequestIdentifiers.add(identifier); + return; + } + + String requestId = IdentifiersFactory::requestId(identifier); + m_resourcesData->resourceCreated(requestId, m_pageAgent->loaderId(&loader)); + + CachedResource* cachedResource = InspectorPageAgent::cachedResource(loader.frame(), request.url()); + InspectorPageAgent::ResourceType type = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : m_resourcesData->resourceType(requestId); + if (type == InspectorPageAgent::OtherResource) { + if (m_loadingXHRSynchronously) + type = InspectorPageAgent::XHRResource; + else if (equalIgnoringFragmentIdentifier(request.url(), loader.frameLoader()->icon().url())) + type = InspectorPageAgent::ImageResource; + else if (equalIgnoringFragmentIdentifier(request.url(), loader.url()) && !loader.isCommitted()) + type = InspectorPageAgent::DocumentResource; + } + + m_resourcesData->setResourceType(requestId, type); + + for (auto& entry : m_extraRequestHeaders) + request.setHTTPHeaderField(entry.key, entry.value); + + request.setReportLoadTiming(true); + request.setReportRawHeaders(true); + + if (m_cacheDisabled) { + request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache"); + request.setCachePolicy(ReloadIgnoringCacheData); + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache"); + } + + Inspector::Protocol::Page::ResourceType resourceType = InspectorPageAgent::resourceTypeJson(type); + + RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr); + String targetId = request.initiatorIdentifier(); + + m_frontendDispatcher->requestWillBeSent(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), loader.url().string(), buildObjectForResourceRequest(request), timestamp(), initiatorObject, buildObjectForResourceResponse(redirectResponse, nullptr), type != InspectorPageAgent::OtherResource ? &resourceType : nullptr, targetId.isEmpty() ? nullptr : &targetId); +} + +void InspectorNetworkAgent::markResourceAsCached(unsigned long identifier) +{ + if (m_hiddenRequestIdentifiers.contains(identifier)) + return; + + m_frontendDispatcher->requestServedFromCache(IdentifiersFactory::requestId(identifier)); +} + +void InspectorNetworkAgent::didReceiveResponse(unsigned long identifier, DocumentLoader& loader, const ResourceResponse& response, ResourceLoader* resourceLoader) +{ + if (m_hiddenRequestIdentifiers.contains(identifier)) + return; + + String requestId = IdentifiersFactory::requestId(identifier); + RefPtr<Inspector::Protocol::Network::Response> resourceResponse = buildObjectForResourceResponse(response, resourceLoader); + + bool isNotModified = response.httpStatusCode() == 304; + + CachedResource* cachedResource = nullptr; + if (resourceLoader && resourceLoader->isSubresourceLoader() && !isNotModified) + cachedResource = static_cast<SubresourceLoader*>(resourceLoader)->cachedResource(); + if (!cachedResource) + cachedResource = InspectorPageAgent::cachedResource(loader.frame(), response.url()); + + if (cachedResource) { + // Use mime type from cached resource in case the one in response is empty. + if (resourceResponse && response.mimeType().isEmpty()) + resourceResponse->setString(Inspector::Protocol::Network::Response::MimeType, cachedResource->response().mimeType()); + m_resourcesData->addCachedResource(requestId, cachedResource); + } + + InspectorPageAgent::ResourceType type = m_resourcesData->resourceType(requestId); + InspectorPageAgent::ResourceType newType = cachedResource ? InspectorPageAgent::cachedResourceType(*cachedResource) : type; + + // FIXME: XHRResource is returned for CachedResource::RawResource, it should be OtherResource unless it truly is an XHR. + // RawResource is used for loading worker scripts, and those should stay as ScriptResource and not change to XHRResource. + if (type != newType && newType != InspectorPageAgent::XHRResource && newType != InspectorPageAgent::OtherResource) + type = newType; + + m_resourcesData->responseReceived(requestId, m_pageAgent->frameId(loader.frame()), response); + m_resourcesData->setResourceType(requestId, type); + + m_frontendDispatcher->responseReceived(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), timestamp(), InspectorPageAgent::resourceTypeJson(type), resourceResponse); + + // If we revalidated the resource and got Not modified, send content length following didReceiveResponse + // as there will be no calls to didReceiveData from the network stack. + if (isNotModified && cachedResource && cachedResource->encodedSize()) + didReceiveData(identifier, nullptr, cachedResource->encodedSize(), 0); +} + +static bool isErrorStatusCode(int statusCode) +{ + return statusCode >= 400; +} + +void InspectorNetworkAgent::didReceiveData(unsigned long identifier, const char* data, int dataLength, int encodedDataLength) +{ + if (m_hiddenRequestIdentifiers.contains(identifier)) + return; + + String requestId = IdentifiersFactory::requestId(identifier); + + if (data) { + NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId); + if (resourceData && !m_loadingXHRSynchronously && (!resourceData->cachedResource() || resourceData->cachedResource()->dataBufferingPolicy() == DoNotBufferData || isErrorStatusCode(resourceData->httpStatusCode()))) + m_resourcesData->maybeAddResourceData(requestId, data, dataLength); + } + + m_frontendDispatcher->dataReceived(requestId, timestamp(), dataLength, encodedDataLength); +} + +void InspectorNetworkAgent::didFinishLoading(unsigned long identifier, DocumentLoader& loader, double finishTime) +{ + if (m_hiddenRequestIdentifiers.remove(identifier)) + return; + + String requestId = IdentifiersFactory::requestId(identifier); + if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource) + m_resourcesData->addResourceSharedBuffer(requestId, loader.frameLoader()->documentLoader()->mainResourceData(), loader.frame()->document()->encoding()); + + m_resourcesData->maybeDecodeDataToContent(requestId); + + double elapsedFinishTime = finishTime ? m_environment.executionStopwatch()->elapsedTimeSince(MonotonicTime::fromRawSeconds(finishTime)) : timestamp(); + + String sourceMappingURL; + NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId); + if (resourceData && resourceData->cachedResource()) + sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(resourceData->cachedResource()); + + m_frontendDispatcher->loadingFinished(requestId, elapsedFinishTime, !sourceMappingURL.isEmpty() ? &sourceMappingURL : nullptr); +} + +void InspectorNetworkAgent::didFailLoading(unsigned long identifier, DocumentLoader& loader, const ResourceError& error) +{ + if (m_hiddenRequestIdentifiers.remove(identifier)) + return; + + String requestId = IdentifiersFactory::requestId(identifier); + + if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::DocumentResource) { + Frame* frame = loader.frame(); + if (frame && frame->loader().documentLoader() && frame->document()) { + m_resourcesData->addResourceSharedBuffer(requestId, + frame->loader().documentLoader()->mainResourceData(), + frame->document()->encoding()); + } + } + + bool canceled = error.isCancellation(); + m_frontendDispatcher->loadingFailed(requestId, timestamp(), error.localizedDescription(), canceled ? &canceled : nullptr); +} + +void InspectorNetworkAgent::didLoadResourceFromMemoryCache(DocumentLoader& loader, CachedResource& resource) +{ + String loaderId = m_pageAgent->loaderId(&loader); + String frameId = m_pageAgent->frameId(loader.frame()); + unsigned long identifier = loader.frame()->page()->progress().createUniqueIdentifier(); + String requestId = IdentifiersFactory::requestId(identifier); + m_resourcesData->resourceCreated(requestId, loaderId); + m_resourcesData->addCachedResource(requestId, &resource); + + RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr); + + m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader.url().string(), timestamp(), initiatorObject, buildObjectForCachedResource(&resource)); +} + +void InspectorNetworkAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString) +{ + m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), sourceString); +} + +void InspectorNetworkAgent::didReceiveScriptResponse(unsigned long identifier) +{ + m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::ScriptResource); +} + +void InspectorNetworkAgent::didReceiveThreadableLoaderResponse(unsigned long identifier, DocumentThreadableLoader& documentThreadableLoader) +{ + String initiator = documentThreadableLoader.options().initiator; + if (initiator == cachedResourceRequestInitiators().fetch) + m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::FetchResource); + else if (initiator == cachedResourceRequestInitiators().xmlhttprequest) + m_resourcesData->setResourceType(IdentifiersFactory::requestId(identifier), InspectorPageAgent::XHRResource); +} + +void InspectorNetworkAgent::didFinishXHRLoading(unsigned long identifier, const String& decodedText) +{ + m_resourcesData->setResourceContent(IdentifiersFactory::requestId(identifier), decodedText); +} + +void InspectorNetworkAgent::willLoadXHRSynchronously() +{ + m_loadingXHRSynchronously = true; +} + +void InspectorNetworkAgent::didLoadXHRSynchronously() +{ + m_loadingXHRSynchronously = false; +} + +void InspectorNetworkAgent::willDestroyCachedResource(CachedResource& cachedResource) +{ + Vector<String> requestIds = m_resourcesData->removeCachedResource(&cachedResource); + if (!requestIds.size()) + return; + + String content; + bool base64Encoded; + if (!InspectorPageAgent::cachedResourceContent(&cachedResource, &content, &base64Encoded)) + return; + for (auto& id : requestIds) + m_resourcesData->setResourceContent(id, content, base64Encoded); +} + +void InspectorNetworkAgent::willRecalculateStyle() +{ + m_isRecalculatingStyle = true; +} + +void InspectorNetworkAgent::didRecalculateStyle() +{ + m_isRecalculatingStyle = false; + m_styleRecalculationInitiator = nullptr; +} + +void InspectorNetworkAgent::didScheduleStyleRecalculation(Document& document) +{ + if (!m_styleRecalculationInitiator) + m_styleRecalculationInitiator = buildInitiatorObject(&document); +} + +RefPtr<Inspector::Protocol::Network::Initiator> InspectorNetworkAgent::buildInitiatorObject(Document* document) +{ + Ref<ScriptCallStack> stackTrace = createScriptCallStack(JSMainThreadExecState::currentState(), ScriptCallStack::maxCallStackSizeToCapture); + if (stackTrace->size() > 0) { + auto initiatorObject = Inspector::Protocol::Network::Initiator::create() + .setType(Inspector::Protocol::Network::Initiator::Type::Script) + .release(); + initiatorObject->setStackTrace(stackTrace->buildInspectorArray()); + return WTFMove(initiatorObject); + } + + if (document && document->scriptableDocumentParser()) { + auto initiatorObject = Inspector::Protocol::Network::Initiator::create() + .setType(Inspector::Protocol::Network::Initiator::Type::Parser) + .release(); + initiatorObject->setUrl(document->url().string()); + initiatorObject->setLineNumber(document->scriptableDocumentParser()->textPosition().m_line.oneBasedInt()); + return WTFMove(initiatorObject); + } + + if (m_isRecalculatingStyle && m_styleRecalculationInitiator) + return m_styleRecalculationInitiator; + + return Inspector::Protocol::Network::Initiator::create() + .setType(Inspector::Protocol::Network::Initiator::Type::Other) + .release(); +} + +#if ENABLE(WEB_SOCKETS) + +void InspectorNetworkAgent::didCreateWebSocket(unsigned long identifier, const URL& requestURL) +{ + m_frontendDispatcher->webSocketCreated(IdentifiersFactory::requestId(identifier), requestURL.string()); +} + +void InspectorNetworkAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const ResourceRequest& request) +{ + auto requestObject = Inspector::Protocol::Network::WebSocketRequest::create() + .setHeaders(buildObjectForHeaders(request.httpHeaderFields())) + .release(); + m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(requestObject)); +} + +void InspectorNetworkAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const ResourceResponse& response) +{ + auto responseObject = Inspector::Protocol::Network::WebSocketResponse::create() + .setStatus(response.httpStatusCode()) + .setStatusText(response.httpStatusText()) + .setHeaders(buildObjectForHeaders(response.httpHeaderFields())) + .release(); + m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(responseObject)); +} + +void InspectorNetworkAgent::didCloseWebSocket(unsigned long identifier) +{ + m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp()); +} + +void InspectorNetworkAgent::didReceiveWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame) +{ + auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create() + .setOpcode(frame.opCode) + .setMask(frame.masked) + .setPayloadData(String(frame.payload, frame.payloadLength)) + .release(); + m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject)); +} + +void InspectorNetworkAgent::didSendWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame) +{ + auto frameObject = Inspector::Protocol::Network::WebSocketFrame::create() + .setOpcode(frame.opCode) + .setMask(frame.masked) + .setPayloadData(String(frame.payload, frame.payloadLength)) + .release(); + m_frontendDispatcher->webSocketFrameSent(IdentifiersFactory::requestId(identifier), timestamp(), WTFMove(frameObject)); +} + +void InspectorNetworkAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage) +{ + m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage); +} + +#endif // ENABLE(WEB_SOCKETS) + +void InspectorNetworkAgent::enable(ErrorString&) +{ + enable(); +} + +void InspectorNetworkAgent::enable() +{ + m_enabled = true; + m_instrumentingAgents.setInspectorNetworkAgent(this); +} + +void InspectorNetworkAgent::disable(ErrorString&) +{ + m_enabled = false; + m_instrumentingAgents.setInspectorNetworkAgent(nullptr); + m_resourcesData->clear(); + m_extraRequestHeaders.clear(); +} + +void InspectorNetworkAgent::setExtraHTTPHeaders(ErrorString&, const InspectorObject& headers) +{ + for (auto& entry : headers) { + String stringValue; + if (entry.value->asString(stringValue)) + m_extraRequestHeaders.set(entry.key, stringValue); + } +} + +void InspectorNetworkAgent::getResponseBody(ErrorString& errorString, const String& requestId, String* content, bool* base64Encoded) +{ + NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId); + if (!resourceData) { + errorString = ASCIILiteral("No resource with given identifier found"); + return; + } + + if (resourceData->hasContent()) { + *base64Encoded = resourceData->base64Encoded(); + *content = resourceData->content(); + return; + } + + if (resourceData->isContentEvicted()) { + errorString = ASCIILiteral("Request content was evicted from inspector cache"); + return; + } + + if (resourceData->buffer() && !resourceData->textEncodingName().isNull()) { + *base64Encoded = false; + if (InspectorPageAgent::sharedBufferContent(resourceData->buffer(), resourceData->textEncodingName(), *base64Encoded, content)) + return; + } + + if (resourceData->cachedResource()) { + if (InspectorPageAgent::cachedResourceContent(resourceData->cachedResource(), content, base64Encoded)) + return; + } + + errorString = ASCIILiteral("No data found for resource with given identifier"); +} + +void InspectorNetworkAgent::setCacheDisabled(ErrorString&, bool cacheDisabled) +{ + m_cacheDisabled = cacheDisabled; + if (cacheDisabled) + MemoryCache::singleton().evictResources(); +} + +void InspectorNetworkAgent::loadResource(ErrorString& errorString, const String& frameId, const String& urlString, Ref<LoadResourceCallback>&& callback) +{ + Frame* frame = m_pageAgent->assertFrame(errorString, frameId); + if (!frame) + return; + + Document* document = frame->document(); + if (!document) { + errorString = ASCIILiteral("No Document instance for the specified frame"); + return; + } + + URL url = document->completeURL(urlString); + ResourceRequest request(url); + request.setHTTPMethod(ASCIILiteral("GET")); + request.setHiddenFromInspector(true); + + ThreadableLoaderOptions options; + options.sendLoadCallbacks = SendCallbacks; // So we remove this from m_hiddenRequestIdentifiers on completion. + options.defersLoadingPolicy = DefersLoadingPolicy::DisallowDefersLoading; // So the request is never deferred. + options.mode = FetchOptions::Mode::NoCors; + options.credentials = FetchOptions::Credentials::SameOrigin; + options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce; + + // InspectorThreadableLoaderClient deletes itself when the load completes or fails. + InspectorThreadableLoaderClient* inspectorThreadableLoaderClient = new InspectorThreadableLoaderClient(callback.copyRef()); + auto loader = DocumentThreadableLoader::create(*document, *inspectorThreadableLoaderClient, WTFMove(request), options); + if (!loader) + return; + + // If the load already completed, inspectorThreadableLoaderClient will have been deleted and we will have already called the callback. + if (!callback->isActive()) + return; + + inspectorThreadableLoaderClient->setLoader(WTFMove(loader)); +} + +static Ref<Inspector::Protocol::Page::SearchResult> buildObjectForSearchResult(const String& requestId, const String& frameId, const String& url, int matchesCount) +{ + auto searchResult = Inspector::Protocol::Page::SearchResult::create() + .setUrl(url) + .setFrameId(frameId) + .setMatchesCount(matchesCount) + .release(); + searchResult->setRequestId(requestId); + return searchResult; +} + +void InspectorNetworkAgent::searchOtherRequests(const JSC::Yarr::RegularExpression& regex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Page::SearchResult>>& result) +{ + Vector<NetworkResourcesData::ResourceData*> resources = m_resourcesData->resources(); + for (auto* resourceData : resources) { + if (resourceData->hasContent()) { + int matchesCount = ContentSearchUtilities::countRegularExpressionMatches(regex, resourceData->content()); + if (matchesCount) + result->addItem(buildObjectForSearchResult(resourceData->requestId(), resourceData->frameId(), resourceData->url(), matchesCount)); + } + } +} + +void InspectorNetworkAgent::searchInRequest(ErrorString& errorString, const String& requestId, const String& query, bool caseSensitive, bool isRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>& results) +{ + NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId); + if (!resourceData) { + errorString = ASCIILiteral("No resource with given identifier found"); + return; + } + + if (!resourceData->hasContent()) { + errorString = ASCIILiteral("No resource content"); + return; + } + + results = ContentSearchUtilities::searchInTextByLines(resourceData->content(), query, caseSensitive, isRegex); +} + +void InspectorNetworkAgent::mainFrameNavigated(DocumentLoader& loader) +{ + if (m_cacheDisabled) + MemoryCache::singleton().evictResources(); + + m_resourcesData->clear(m_pageAgent->loaderId(&loader)); +} + +} // namespace WebCore |