Chromium Code Reviews| 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() {} | |
|
yurys
2012/03/20 12:42:06
Should it be marked as virtual?
pfeldman
2012/03/20 13:22:10
Done.
| |
| 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, | |
|
csilv
2012/03/20 18:22:02
nit, fix alignment on lines 173 & 174
pfeldman
2012/03/20 19:18:47
Done.
| |
| 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, | |
|
yurys
2012/03/20 12:42:06
We may want to rename this method as it now sends
pfeldman
2012/03/20 13:22:10
Done.
| |
| 221 ListValue* rvh_list) { | |
|
yurys
2012/03/20 12:42:06
style: wrong alignment
pfeldman
2012/03/20 13:22:10
Done.
csilv
2012/03/20 18:22:02
nit: fix alignment of line 221
pfeldman
2012/03/20 19:18:47
Done.
| |
| 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() {} | |
| 341 | |
| 342 virtual void WorkerCreated( | |
| 343 const GURL& url, | |
| 344 const string16& name, | |
| 345 int process_id, | |
| 346 int route_id) OVERRIDE { | |
| 347 BrowserThread::PostTask( | |
| 348 BrowserThread::UI, FROM_HERE, | |
| 349 base::Bind(&WorkerCreationDestructionListener::NotifyItemsChanged, | |
| 350 this)); | |
| 351 } | |
| 352 | |
| 353 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE { | |
| 354 BrowserThread::PostTask( | |
| 355 BrowserThread::UI, FROM_HERE, | |
| 356 base::Bind(&WorkerCreationDestructionListener::NotifyItemsChanged, | |
| 357 this)); | |
| 358 } | |
| 359 | |
| 360 void NotifyItemsChanged() { | |
| 361 if (discovery_ui_) | |
| 362 discovery_ui_->RefreshUI(); | |
| 363 } | |
| 364 | |
| 365 void RegisterObserver() { | |
| 366 WorkerService::GetInstance()->AddObserver(this); | |
| 367 } | |
| 368 | |
| 369 void UnregisterObserver() { | |
| 370 WorkerService::GetInstance()->RemoveObserver(this); | |
| 371 } | |
| 372 | |
| 373 InspectUI* discovery_ui_; | |
| 374 }; | |
| 375 | |
| 376 InspectUI::InspectUI(content::WebUI* web_ui) | |
| 377 : WebUIController(web_ui), | |
| 378 observer_(new WorkerCreationDestructionListener(this)) { | |
| 379 web_ui->AddMessageHandler(new InspectMessageHandler()); | |
| 380 | |
| 381 InspectDataSource* html_source = new InspectDataSource(); | |
| 382 | |
| 383 Profile* profile = Profile::FromWebUI(web_ui); | |
| 384 profile->GetChromeURLDataManager()->AddDataSource(html_source); | |
| 385 | |
| 386 registrar_.Add(this, | |
| 387 content::NOTIFICATION_WEB_CONTENTS_CONNECTED, | |
| 388 content::NotificationService::AllSources()); | |
| 389 registrar_.Add(this, | |
| 390 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, | |
| 391 content::NotificationService::AllSources()); | |
| 392 registrar_.Add(this, | |
| 393 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
| 394 content::NotificationService::AllSources()); | |
| 395 } | |
| 396 | |
| 397 InspectUI::~InspectUI() { | |
| 398 observer_->InspectUIDestroyed(); | |
| 399 observer_ = NULL; | |
| 400 } | |
| 401 | |
| 402 void InspectUI::RefreshUI() { | |
| 403 web_ui()->CallJavascriptFunction("populateLists"); | |
| 404 } | |
| 405 | |
| 406 void InspectUI::Observe(int type, | |
| 407 const content::NotificationSource& source, | |
| 408 const content::NotificationDetails& details) { | |
| 409 RefreshUI(); | |
| 410 } | |
| OLD | NEW |