Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: chrome/browser/ui/webui/inspect_ui.cc

Issue 9724038: DevTools: convert about:workers into about:inspect (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698