blob: f8afdfb894b2a86b2650997981c91732eff149aa [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/inspector/InspectorResourceContentLoader.h"
#include "core/css/CSSStyleSheet.h"
#include "core/css/StyleSheetContents.h"
#include "core/dom/Document.h"
#include "core/fetch/CSSStyleSheetResource.h"
#include "core/fetch/FetchInitiatorTypeNames.h"
#include "core/fetch/RawResource.h"
#include "core/fetch/Resource.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/fetch/StyleSheetResourceClient.h"
#include "core/frame/LocalFrame.h"
#include "core/inspector/InspectedFrames.h"
#include "core/inspector/InspectorCSSAgent.h"
#include "core/inspector/InspectorPageAgent.h"
#include "core/page/Page.h"
#include "public/platform/WebCachePolicy.h"
#include "public/platform/WebURLRequest.h"
namespace blink {
class InspectorResourceContentLoader::ResourceClient final
: public GarbageCollectedFinalized<
InspectorResourceContentLoader::ResourceClient>,
private RawResourceClient,
private StyleSheetResourceClient {
USING_GARBAGE_COLLECTED_MIXIN(ResourceClient);
public:
explicit ResourceClient(InspectorResourceContentLoader* loader)
: m_loader(loader) {}
void waitForResource(Resource* resource) {
if (resource->getType() == Resource::Raw)
resource->addClient(static_cast<RawResourceClient*>(this));
else
resource->addClient(static_cast<StyleSheetResourceClient*>(this));
}
DEFINE_INLINE_TRACE() {
visitor->trace(m_loader);
StyleSheetResourceClient::trace(visitor);
RawResourceClient::trace(visitor);
}
private:
Member<InspectorResourceContentLoader> m_loader;
void setCSSStyleSheet(const String&,
const KURL&,
const String&,
const CSSStyleSheetResource*) override;
void notifyFinished(Resource*) override;
String debugName() const override {
return "InspectorResourceContentLoader::ResourceClient";
}
void resourceFinished(Resource*);
friend class InspectorResourceContentLoader;
};
void InspectorResourceContentLoader::ResourceClient::resourceFinished(
Resource* resource) {
if (m_loader)
m_loader->resourceFinished(this);
if (resource->getType() == Resource::Raw)
resource->removeClient(static_cast<RawResourceClient*>(this));
else
resource->removeClient(static_cast<StyleSheetResourceClient*>(this));
}
void InspectorResourceContentLoader::ResourceClient::setCSSStyleSheet(
const String&,
const KURL& url,
const String&,
const CSSStyleSheetResource* resource) {
resourceFinished(const_cast<CSSStyleSheetResource*>(resource));
}
void InspectorResourceContentLoader::ResourceClient::notifyFinished(
Resource* resource) {
if (resource->getType() == Resource::CSSStyleSheet)
return;
resourceFinished(resource);
}
InspectorResourceContentLoader::InspectorResourceContentLoader(
LocalFrame* inspectedFrame)
: m_allRequestsStarted(false),
m_started(false),
m_inspectedFrame(inspectedFrame),
m_lastClientId(0) {}
void InspectorResourceContentLoader::start() {
m_started = true;
HeapVector<Member<Document>> documents;
InspectedFrames* inspectedFrames = InspectedFrames::create(m_inspectedFrame);
for (LocalFrame* frame : *inspectedFrames) {
documents.append(frame->document());
documents.appendVector(InspectorPageAgent::importsForFrame(frame));
}
for (Document* document : documents) {
HashSet<String> urlsToFetch;
ResourceRequest resourceRequest;
HistoryItem* item =
document->frame() ? document->frame()->loader().currentItem() : nullptr;
if (item) {
resourceRequest = FrameLoader::resourceRequestFromHistoryItem(
item, WebCachePolicy::ReturnCacheDataDontLoad);
} else {
resourceRequest = document->url();
resourceRequest.setCachePolicy(WebCachePolicy::ReturnCacheDataDontLoad);
}
resourceRequest.setRequestContext(WebURLRequest::RequestContextInternal);
if (!resourceRequest.url().getString().isEmpty()) {
urlsToFetch.add(resourceRequest.url().getString());
FetchRequest request(resourceRequest, FetchInitiatorTypeNames::internal);
Resource* resource = RawResource::fetch(request, document->fetcher());
if (resource) {
// Prevent garbage collection by holding a reference to this resource.
m_resources.append(resource);
ResourceClient* resourceClient = new ResourceClient(this);
m_pendingResourceClients.add(resourceClient);
resourceClient->waitForResource(resource);
}
}
HeapVector<Member<CSSStyleSheet>> styleSheets;
InspectorCSSAgent::collectAllDocumentStyleSheets(document, styleSheets);
for (CSSStyleSheet* styleSheet : styleSheets) {
if (styleSheet->isInline() || !styleSheet->contents()->loadCompleted())
continue;
String url = styleSheet->href();
if (url.isEmpty() || urlsToFetch.contains(url))
continue;
urlsToFetch.add(url);
FetchRequest request(ResourceRequest(url),
FetchInitiatorTypeNames::internal);
request.mutableResourceRequest().setRequestContext(
WebURLRequest::RequestContextInternal);
Resource* resource =
CSSStyleSheetResource::fetch(request, document->fetcher());
if (!resource)
continue;
// Prevent garbage collection by holding a reference to this resource.
m_resources.append(resource);
ResourceClient* resourceClient = new ResourceClient(this);
m_pendingResourceClients.add(resourceClient);
resourceClient->waitForResource(resource);
}
}
m_allRequestsStarted = true;
checkDone();
}
int InspectorResourceContentLoader::createClientId() {
return ++m_lastClientId;
}
void InspectorResourceContentLoader::ensureResourcesContentLoaded(
int clientId,
std::unique_ptr<WTF::Closure> callback) {
if (!m_started)
start();
m_callbacks.add(clientId, Callbacks())
.storedValue->value.append(std::move(callback));
checkDone();
}
void InspectorResourceContentLoader::cancel(int clientId) {
m_callbacks.remove(clientId);
}
InspectorResourceContentLoader::~InspectorResourceContentLoader() {
ASSERT(m_resources.isEmpty());
}
DEFINE_TRACE(InspectorResourceContentLoader) {
visitor->trace(m_inspectedFrame);
visitor->trace(m_pendingResourceClients);
visitor->trace(m_resources);
}
void InspectorResourceContentLoader::didCommitLoadForLocalFrame(
LocalFrame* frame) {
if (frame == m_inspectedFrame)
stop();
}
void InspectorResourceContentLoader::dispose() {
stop();
}
void InspectorResourceContentLoader::stop() {
HeapHashSet<Member<ResourceClient>> pendingResourceClients;
m_pendingResourceClients.swap(pendingResourceClients);
for (const auto& client : pendingResourceClients)
client->m_loader = nullptr;
m_resources.clear();
// Make sure all callbacks are called to prevent infinite waiting time.
checkDone();
m_allRequestsStarted = false;
m_started = false;
}
bool InspectorResourceContentLoader::hasFinished() {
return m_allRequestsStarted && m_pendingResourceClients.size() == 0;
}
void InspectorResourceContentLoader::checkDone() {
if (!hasFinished())
return;
HashMap<int, Callbacks> callbacks;
callbacks.swap(m_callbacks);
for (const auto& keyValue : callbacks) {
for (const auto& callback : keyValue.value)
(*callback)();
}
}
void InspectorResourceContentLoader::resourceFinished(ResourceClient* client) {
m_pendingResourceClients.remove(client);
checkDone();
}
} // namespace blink