OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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 "chrome/browser/ui/webui/inspect_ui.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/json/json_writer.h" | |
12 #include "base/memory/ref_counted_memory.h" | |
13 #include "base/string_number_conversions.h" | |
14 #include "base/string_util.h" | |
15 #include "base/utf_string_conversions.h" | |
16 #include "base/values.h" | |
17 #include "chrome/browser/debugger/devtools_window.h" | |
18 #include "chrome/browser/extensions/extension_service.h" | |
19 #include "chrome/browser/profiles/profile.h" | |
20 #include "chrome/browser/ui/browser_list.h" | |
21 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
22 #include "chrome/browser/ui/webui/chrome_url_data_manager_backend.h" | |
23 #include "chrome/browser/ui/webui/chrome_web_ui_data_source.h" | |
24 #include "chrome/common/url_constants.h" | |
25 #include "content/public/browser/child_process_data.h" | |
26 #include "content/public/browser/devtools_agent_host_registry.h" | |
27 #include "content/public/browser/devtools_client_host.h" | |
28 #include "content/public/browser/devtools_manager.h" | |
29 #include "content/public/browser/browser_thread.h" | |
30 #include "content/public/browser/favicon_status.h" | |
31 #include "content/public/browser/navigation_entry.h" | |
32 #include "content/public/browser/notification_service.h" | |
33 #include "content/public/browser/notification_source.h" | |
34 #include "content/public/browser/notification_types.h" | |
35 #include "content/public/browser/render_process_host.h" | |
36 #include "content/public/browser/render_view_host.h" | |
37 #include "content/public/browser/render_view_host_delegate.h" | |
38 #include "content/public/browser/render_widget_host.h" | |
39 #include "content/public/browser/web_contents.h" | |
40 #include "content/public/browser/web_ui.h" | |
41 #include "content/public/browser/worker_service.h" | |
42 #include "content/public/browser/worker_service_observer.h" | |
43 #include "content/public/browser/web_contents.h" | |
44 #include "content/public/browser/web_ui_message_handler.h" | |
45 #include "content/public/common/process_type.h" | |
46 #include "grit/browser_resources.h" | |
47 #include "grit/generated_resources.h" | |
48 #include "net/base/escape.h" | |
49 #include "ui/base/resource/resource_bundle.h" | |
50 | |
51 using content::BrowserThread; | |
52 using content::ChildProcessData; | |
53 using content::DevToolsAgentHost; | |
54 using content::DevToolsAgentHostRegistry; | |
55 using content::DevToolsClientHost; | |
56 using content::DevToolsManager; | |
57 using content::RenderProcessHost; | |
58 using content::RenderViewHost; | |
59 using content::RenderViewHostDelegate; | |
60 using content::RenderWidgetHost; | |
61 using content::WebContents; | |
62 using content::WebUIMessageHandler; | |
63 using content::WorkerService; | |
64 using content::WorkerServiceObserver; | |
65 | |
66 static const char kDataFile[] = "targets-data.json"; | |
67 | |
68 static const char kExtensionTargetType[] = "extension"; | |
69 static const char kPageTargetType[] = "page"; | |
70 static const char kWorkerTargetType[] = "worker"; | |
71 | |
72 static const char kInspectCommand[] = "inspect"; | |
73 static const char kTerminateCommand[] = "terminate"; | |
74 | |
75 static const char kTargetTypeField[] = "type"; | |
76 static const char kAttachedField[] = "attached"; | |
77 static const char kProcessIdField[] = "processId"; | |
78 static const char kRouteIdField[] = "routeId"; | |
79 static const char kUrlField[] = "url"; | |
80 static const char kNameField[] = "name"; | |
81 static const char kFaviconUrlField[] = "favicon_url"; | |
82 static const char kPidField[] = "pid"; | |
83 | |
84 namespace { | |
85 | |
86 DictionaryValue* BuildTargetDescriptor( | |
87 const std::string& target_type, | |
88 bool attached, | |
89 const GURL& url, | |
90 const std::string& name, | |
91 const GURL& favicon_url, | |
92 int process_id, | |
93 int route_id, | |
94 base::ProcessHandle handle = base::kNullProcessHandle) { | |
95 DictionaryValue* target_data = new DictionaryValue(); | |
96 target_data->SetString(kTargetTypeField, target_type); | |
97 target_data->SetBoolean(kAttachedField, attached); | |
98 target_data->SetInteger(kProcessIdField, process_id); | |
99 target_data->SetInteger(kRouteIdField, route_id); | |
100 target_data->SetString(kUrlField, url.spec()); | |
101 target_data->SetString(kNameField, net::EscapeForHTML(name)); | |
102 target_data->SetInteger(kPidField, base::GetProcId(handle)); | |
103 target_data->SetString(kFaviconUrlField, favicon_url.spec()); | |
104 | |
105 return target_data; | |
106 } | |
107 | |
108 bool HasClientHost(RenderViewHost* rvh) { | |
109 if (!DevToolsAgentHostRegistry::HasDevToolsAgentHost(rvh)) | |
110 return false; | |
111 | |
112 DevToolsAgentHost* agent = | |
113 DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh); | |
114 return !!DevToolsManager::GetInstance()->GetDevToolsClientHostFor(agent); | |
115 } | |
116 | |
117 DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) { | |
118 RenderViewHostDelegate* rvh_delegate = rvh->GetDelegate(); | |
119 WebContents* web_contents = rvh_delegate->GetAsWebContents(); | |
120 std::string title; | |
121 std::string target_type = is_tab ? kPageTargetType : ""; | |
122 GURL favicon_url; | |
123 if (web_contents) { | |
124 title = UTF16ToUTF8(web_contents->GetTitle()); | |
125 content::NavigationController& controller = web_contents->GetController(); | |
126 content::NavigationEntry* entry = controller.GetActiveEntry(); | |
127 if (entry != NULL && entry->GetURL().is_valid()) | |
128 favicon_url = entry->GetFavicon().url; | |
129 | |
130 Profile* profile = Profile::FromBrowserContext( | |
131 web_contents->GetBrowserContext()); | |
132 if (profile) { | |
133 ExtensionService* extension_service = profile->GetExtensionService(); | |
134 const Extension* extension = extension_service->extensions()->GetByID( | |
135 web_contents->GetURL().host()); | |
136 if (extension) { | |
137 target_type = kExtensionTargetType; | |
138 title = extension->name(); | |
139 } | |
140 } | |
141 } | |
142 | |
143 return BuildTargetDescriptor(target_type, | |
144 HasClientHost(rvh), | |
145 rvh_delegate->GetURL(), | |
146 title, | |
147 favicon_url, | |
148 rvh->GetProcess()->GetID(), | |
149 rvh->GetRoutingID()); | |
150 } | |
151 | |
152 class InspectDataSource : public ChromeWebUIDataSource { | |
153 public: | |
154 InspectDataSource(); | |
155 | |
156 virtual void StartDataRequest(const std::string& path, | |
157 bool is_incognito, | |
158 int request_id); | |
159 private: | |
160 ~InspectDataSource() {} | |
161 void SendSharedWorkersData(int request_id, ListValue* rvh_list); | |
162 DISALLOW_COPY_AND_ASSIGN(InspectDataSource); | |
163 }; | |
164 | |
165 InspectDataSource::InspectDataSource() | |
166 : ChromeWebUIDataSource(chrome::kChromeUIInspectHost, | |
167 MessageLoop::current()) { | |
168 add_resource_path("inspect.js", IDR_INSPECT_JS); | |
169 set_default_resource(IDR_INSPECT_HTML); | |
170 } | |
171 | |
172 void InspectDataSource::StartDataRequest(const std::string& path, | |
173 bool is_incognito, | |
174 int request_id) { | |
175 if (path != kDataFile) { | |
176 ChromeWebUIDataSource::StartDataRequest(path, is_incognito, request_id); | |
177 return; | |
178 } | |
179 | |
180 std::set<RenderViewHost*> tab_rvhs; | |
181 for (TabContentsIterator it; !it.done(); ++it) | |
182 tab_rvhs.insert(it->web_contents()->GetRenderViewHost()); | |
183 | |
184 scoped_ptr<ListValue> rvh_list(new ListValue()); | |
185 | |
186 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); | |
187 !it.IsAtEnd(); it.Advance()) { | |
188 RenderProcessHost* render_process_host = it.GetCurrentValue(); | |
189 DCHECK(render_process_host); | |
190 | |
191 // Ignore processes that don't have a connection, such as crashed tabs. | |
192 if (!render_process_host->HasConnection()) | |
193 continue; | |
194 | |
195 RenderProcessHost::RenderWidgetHostsIterator rwit( | |
196 render_process_host->GetRenderWidgetHostsIterator()); | |
197 for (; !rwit.IsAtEnd(); rwit.Advance()) { | |
198 const RenderWidgetHost* widget = rwit.GetCurrentValue(); | |
199 DCHECK(widget); | |
200 if (!widget || !widget->IsRenderView()) | |
201 continue; | |
202 | |
203 RenderViewHost* rvh = | |
204 RenderViewHost::From(const_cast<RenderWidgetHost*>(widget)); | |
205 | |
206 bool is_tab = tab_rvhs.find(rvh) != tab_rvhs.end(); | |
207 rvh_list->Append(BuildTargetDescriptor(rvh, is_tab)); | |
208 } | |
209 } | |
210 | |
211 BrowserThread::PostTask( | |
212 BrowserThread::IO, | |
213 FROM_HERE, | |
214 base::Bind(&InspectDataSource::SendSharedWorkersData, | |
215 this, | |
216 request_id, | |
217 base::Owned(rvh_list.release()))); | |
218 } | |
219 | |
220 void InspectDataSource::SendSharedWorkersData(int request_id, | |
221 ListValue* rvh_list) { | |
222 std::vector<WorkerService::WorkerInfo> worker_info = | |
223 WorkerService::GetInstance()->GetWorkers(); | |
224 for (size_t i = 0; i < worker_info.size(); ++i) { | |
225 rvh_list->Append(BuildTargetDescriptor( | |
226 kWorkerTargetType, | |
227 false, | |
228 worker_info[i].url, | |
229 UTF16ToUTF8(worker_info[i].name), | |
230 GURL(), | |
231 worker_info[i].process_id, | |
232 worker_info[i].route_id, | |
233 worker_info[i].handle)); | |
234 } | |
235 | |
236 std::string json_string; | |
237 base::JSONWriter::Write(rvh_list, &json_string); | |
238 | |
239 SendResponse(request_id, base::RefCountedString::TakeString(&json_string)); | |
240 } | |
241 | |
242 class InspectMessageHandler : public WebUIMessageHandler { | |
243 public: | |
244 InspectMessageHandler() {} | |
245 virtual ~InspectMessageHandler() {} | |
246 | |
247 private: | |
248 // WebUIMessageHandler implementation. | |
249 virtual void RegisterMessages() OVERRIDE; | |
250 | |
251 // Callback for "openDevTools" message. | |
252 void HandleInspectCommand(const ListValue* args); | |
253 void HandleTerminateCommand(const ListValue* args); | |
254 | |
255 DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler); | |
256 }; | |
257 | |
258 void InspectMessageHandler::RegisterMessages() { | |
259 web_ui()->RegisterMessageCallback(kInspectCommand, | |
260 base::Bind(&InspectMessageHandler::HandleInspectCommand, | |
261 base::Unretained(this))); | |
262 web_ui()->RegisterMessageCallback(kTerminateCommand, | |
263 base::Bind(&InspectMessageHandler::HandleTerminateCommand, | |
264 base::Unretained(this))); | |
265 } | |
266 | |
267 void InspectMessageHandler::HandleInspectCommand(const ListValue* args) { | |
268 std::string process_id_str; | |
269 std::string route_id_str; | |
270 int process_id; | |
271 int route_id; | |
272 CHECK(args->GetSize() == 2); | |
273 CHECK(args->GetString(0, &process_id_str)); | |
274 CHECK(args->GetString(1, &route_id_str)); | |
275 CHECK(base::StringToInt(process_id_str, | |
276 &process_id)); | |
277 CHECK(base::StringToInt(route_id_str, &route_id)); | |
278 | |
279 Profile* profile = Profile::FromWebUI(web_ui()); | |
280 if (!profile) | |
281 return; | |
282 | |
283 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); | |
284 if (rvh) { | |
285 DevToolsWindow::OpenDevToolsWindow(rvh); | |
286 return; | |
287 } | |
288 | |
289 DevToolsAgentHost* agent_host = | |
290 DevToolsAgentHostRegistry::GetDevToolsAgentHostForWorker(process_id, | |
291 route_id); | |
292 if (agent_host) | |
293 DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host); | |
294 } | |
295 | |
296 static void TerminateWorker(int process_id, int route_id) { | |
297 WorkerService::GetInstance()->TerminateWorker(process_id, route_id); | |
298 } | |
299 | |
300 void InspectMessageHandler::HandleTerminateCommand(const ListValue* args) { | |
301 std::string process_id_str; | |
302 std::string route_id_str; | |
303 int process_id; | |
304 int route_id; | |
305 CHECK(args->GetSize() == 2); | |
306 CHECK(args->GetString(0, &process_id_str)); | |
307 CHECK(args->GetString(1, &route_id_str)); | |
308 CHECK(base::StringToInt(process_id_str, | |
309 &process_id)); | |
310 CHECK(base::StringToInt(route_id_str, &route_id)); | |
311 | |
312 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
313 base::Bind(&TerminateWorker, process_id, route_id)); | |
314 } | |
315 | |
316 } // namespace | |
317 | |
318 class InspectUI::WorkerCreationDestructionListener | |
319 : public WorkerServiceObserver, | |
320 public base::RefCountedThreadSafe<WorkerCreationDestructionListener> { | |
321 public: | |
322 explicit WorkerCreationDestructionListener(InspectUI* workers_ui) | |
323 : discovery_ui_(workers_ui) { | |
324 BrowserThread::PostTask( | |
325 BrowserThread::IO, FROM_HERE, | |
326 base::Bind(&WorkerCreationDestructionListener::RegisterObserver, | |
327 this)); | |
328 } | |
329 | |
330 void InspectUIDestroyed() { | |
331 discovery_ui_ = NULL; | |
332 BrowserThread::PostTask( | |
333 BrowserThread::IO, FROM_HERE, | |
334 base::Bind(&WorkerCreationDestructionListener::UnregisterObserver, | |
335 this)); | |
336 } | |
337 | |
338 private: | |
339 friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>; | |
340 virtual ~WorkerCreationDestructionListener() { | |
apavlov
2012/03/19 16:16:47
The style for this is to use "virtual ~...() {}" (
| |
341 } | |
342 | |
343 virtual void WorkerCreated( | |
344 const GURL& url, | |
345 const string16& name, | |
346 int process_id, | |
347 int route_id) OVERRIDE { | |
348 BrowserThread::PostTask( | |
349 BrowserThread::UI, FROM_HERE, | |
350 base::Bind(&WorkerCreationDestructionListener::NotifyItemsChanged, | |
351 this)); | |
352 } | |
353 | |
354 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE { | |
355 BrowserThread::PostTask( | |
356 BrowserThread::UI, FROM_HERE, | |
357 base::Bind(&WorkerCreationDestructionListener::NotifyItemsChanged, | |
358 this)); | |
359 } | |
360 | |
361 void NotifyItemsChanged() { | |
362 if (discovery_ui_) | |
363 discovery_ui_->RefreshUI(); | |
364 } | |
365 | |
366 void RegisterObserver() { | |
367 WorkerService::GetInstance()->AddObserver(this); | |
368 } | |
369 | |
370 void UnregisterObserver() { | |
371 WorkerService::GetInstance()->RemoveObserver(this); | |
372 } | |
373 | |
374 InspectUI* discovery_ui_; | |
375 }; | |
376 | |
377 InspectUI::InspectUI(content::WebUI* web_ui) | |
378 : WebUIController(web_ui), | |
379 observer_(new WorkerCreationDestructionListener(this)) { | |
380 web_ui->AddMessageHandler(new InspectMessageHandler()); | |
381 | |
382 InspectDataSource* html_source = new InspectDataSource(); | |
383 // Set up the chrome://workers/ source. | |
apavlov
2012/03/19 16:16:47
Is this comment still correct?
| |
384 Profile* profile = Profile::FromWebUI(web_ui); | |
385 profile->GetChromeURLDataManager()->AddDataSource(html_source); | |
386 | |
387 registrar_.Add(this, | |
388 content::NOTIFICATION_WEB_CONTENTS_CONNECTED, | |
389 content::NotificationService::AllSources()); | |
390 registrar_.Add(this, | |
391 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, | |
392 content::NotificationService::AllSources()); | |
393 registrar_.Add(this, | |
394 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
395 content::NotificationService::AllSources()); | |
396 } | |
397 | |
398 InspectUI::~InspectUI() { | |
399 observer_->InspectUIDestroyed(); | |
400 observer_ = NULL; | |
401 } | |
402 | |
403 void InspectUI::RefreshUI() { | |
404 web_ui()->CallJavascriptFunction("populateLists"); | |
405 } | |
406 | |
407 void InspectUI::Observe(int type, | |
408 const content::NotificationSource& source, | |
409 const content::NotificationDetails& details) { | |
410 RefreshUI(); | |
411 } | |
OLD | NEW |