1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "core/inspector/InspectorResourceContentLoader.h"
7
8 #include "core/FetchInitiatorTypeNames.h"
9 #include "core/css/CSSStyleSheet.h"
10 #include "core/css/StyleSheetContents.h"
11 #include "core/fetch/CSSStyleSheetResource.h"
12 #include "core/fetch/RawResource.h"
13 #include "core/fetch/Resource.h"
14 #include "core/fetch/ResourceFetcher.h"
15 #include "core/fetch/ResourcePtr.h"
16 #include "core/fetch/StyleSheetResourceClient.h"
17 #include "core/frame/LocalFrame.h"
18 #include "core/html/VoidCallback.h"
19 #include "core/inspector/InspectorCSSAgent.h"
20 #include "core/inspector/InspectorPageAgent.h"
21 #include "core/page/Page.h"
22 #include "public/platform/WebURLRequest.h"
23
24 namespace blink {
25
26 class InspectorResourceContentLoader::ResourceClient FINAL : private RawResourceClient, private StyleSheetResourceClient {
27 public:
ResourceClient(InspectorResourceContentLoader * loader)28 ResourceClient(InspectorResourceContentLoader* loader)
29 : m_loader(loader)
30 {
31 }
32
waitForResource(Resource * resource)33 void waitForResource(Resource* resource)
34 {
35 if (resource->type() == Resource::Raw)
36 resource->addClient(static_cast<RawResourceClient*>(this));
37 else
38 resource->addClient(static_cast<StyleSheetResourceClient*>(this));
39 }
40
41 private:
42 InspectorResourceContentLoader* m_loader;
43
44 virtual void setCSSStyleSheet(const String&, const KURL&, const String&, const CSSStyleSheetResource*) OVERRIDE;
45 virtual void notifyFinished(Resource*) OVERRIDE;
46 void resourceFinished(Resource*);
47
48 friend class InspectorResourceContentLoader;
49 };
50
resourceFinished(Resource * resource)51 void InspectorResourceContentLoader::ResourceClient::resourceFinished(Resource* resource)
52 {
53 if (m_loader)
54 m_loader->resourceFinished(this);
55
56 if (resource->type() == Resource::Raw)
57 resource->removeClient(static_cast<RawResourceClient*>(this));
58 else
59 resource->removeClient(static_cast<StyleSheetResourceClient*>(this));
60
61 delete this;
62 }
63
setCSSStyleSheet(const String &,const KURL & url,const String &,const CSSStyleSheetResource * resource)64 void InspectorResourceContentLoader::ResourceClient::setCSSStyleSheet(const String&, const KURL& url, const String&, const CSSStyleSheetResource* resource)
65 {
66 resourceFinished(const_cast<CSSStyleSheetResource*>(resource));
67 }
68
notifyFinished(Resource * resource)69 void InspectorResourceContentLoader::ResourceClient::notifyFinished(Resource* resource)
70 {
71 if (resource->type() == Resource::CSSStyleSheet)
72 return;
73 resourceFinished(resource);
74 }
75
InspectorResourceContentLoader(Page * page)76 InspectorResourceContentLoader::InspectorResourceContentLoader(Page* page)
77 : m_allRequestsStarted(false)
78 , m_started(false)
79 , m_page(page)
80 {
81 }
82
start()83 void InspectorResourceContentLoader::start()
84 {
85 m_started = true;
86 Vector<Document*> documents;
87 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
88 if (!frame->isLocalFrame())
89 continue;
90 LocalFrame* localFrame = toLocalFrame(frame);
91 documents.append(localFrame->document());
92 documents.appendVector(InspectorPageAgent::importsForFrame(localFrame));
93 }
94 for (Vector<Document*>::const_iterator documentIt = documents.begin(); documentIt != documents.end(); ++documentIt) {
95 Document* document = *documentIt;
96 HashSet<String> urlsToFetch;
97
98 ResourceRequest resourceRequest;
99 HistoryItem* item = document->frame() ? document->frame()->loader().currentItem() : 0;
100 if (item) {
101 resourceRequest = FrameLoader::requestFromHistoryItem(item, ReturnCacheDataDontLoad);
102 } else {
103 resourceRequest = document->url();
104 resourceRequest.setCachePolicy(ReturnCacheDataDontLoad);
105 }
106 resourceRequest.setRequestContext(blink::WebURLRequest::RequestContextInternal);
107
108 if (!resourceRequest.url().string().isEmpty()) {
109 urlsToFetch.add(resourceRequest.url().string());
110 FetchRequest request(resourceRequest, FetchInitiatorTypeNames::internal);
111 ResourcePtr<Resource> resource = document->fetcher()->fetchRawResource(request);
112 if (resource) {
113 // Prevent garbage collection by holding a reference to this resource.
114 m_resources.append(resource.get());
115 ResourceClient* resourceClient = new ResourceClient(this);
116 m_pendingResourceClients.add(resourceClient);
117 resourceClient->waitForResource(resource.get());
118 }
119 }
120
121 WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > styleSheets;
122 InspectorCSSAgent::collectAllDocumentStyleSheets(document, styleSheets);
123 for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::const_iterator stylesheetIt = styleSheets.begin(); stylesheetIt != styleSheets.end(); ++stylesheetIt) {
124 CSSStyleSheet* styleSheet = *stylesheetIt;
125 if (styleSheet->isInline() || !styleSheet->contents()->loadCompleted())
126 continue;
127 String url = styleSheet->baseURL().string();
128 if (url.isEmpty() || urlsToFetch.contains(url))
129 continue;
130 urlsToFetch.add(url);
131 FetchRequest request(ResourceRequest(url), FetchInitiatorTypeNames::internal);
132 request.mutableResourceRequest().setRequestContext(blink::WebURLRequest::RequestContextInternal);
133 ResourcePtr<Resource> resource = document->fetcher()->fetchCSSStyleSheet(request);
134 if (!resource)
135 continue;
136 // Prevent garbage collection by holding a reference to this resource.
137 m_resources.append(resource.get());
138 ResourceClient* resourceClient = new ResourceClient(this);
139 m_pendingResourceClients.add(resourceClient);
140 resourceClient->waitForResource(resource.get());
141 }
142 }
143
144 m_allRequestsStarted = true;
145 checkDone();
146 }
147
ensureResourcesContentLoaded(VoidCallback * callback)148 void InspectorResourceContentLoader::ensureResourcesContentLoaded(VoidCallback* callback)
149 {
150 if (!m_started)
151 start();
152 m_callbacks.append(callback);
153 checkDone();
154 }
155
~InspectorResourceContentLoader()156 InspectorResourceContentLoader::~InspectorResourceContentLoader()
157 {
158 ASSERT(m_resources.isEmpty());
159 }
160
trace(Visitor * visitor)161 void InspectorResourceContentLoader::trace(Visitor* visitor)
162 {
163 visitor->trace(m_callbacks);
164 visitor->trace(m_page);
165 }
166
dispose()167 void InspectorResourceContentLoader::dispose()
168 {
169 stop();
170 }
171
stop()172 void InspectorResourceContentLoader::stop()
173 {
174 HashSet<ResourceClient*> pendingResourceClients;
175 m_pendingResourceClients.swap(pendingResourceClients);
176 for (HashSet<ResourceClient*>::const_iterator it = pendingResourceClients.begin(); it != pendingResourceClients.end(); ++it)
177 (*it)->m_loader = 0;
178 m_resources.clear();
179 // Make sure all callbacks are called to prevent infinite waiting time.
180 checkDone();
181 }
182
hasFinished()183 bool InspectorResourceContentLoader::hasFinished()
184 {
185 return m_allRequestsStarted && m_pendingResourceClients.size() == 0;
186 }
187
checkDone()188 void InspectorResourceContentLoader::checkDone()
189 {
190 if (!hasFinished())
191 return;
192 PersistentHeapVectorWillBeHeapVector<Member<VoidCallback> > callbacks;
193 callbacks.swap(m_callbacks);
194 for (PersistentHeapVectorWillBeHeapVector<Member<VoidCallback> >::const_iterator it = callbacks.begin(); it != callbacks.end(); ++it)
195 (*it)->handleEvent();
196 }
197
resourceFinished(ResourceClient * client)198 void InspectorResourceContentLoader::resourceFinished(ResourceClient* client)
199 {
200 m_pendingResourceClients.remove(client);
201 checkDone();
202 }
203
204 } // namespace blink
205