1 // Copyright 2013 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/devtools/devtools_targets_ui.h" 6 7 #include "base/memory/weak_ptr.h" 8 #include "base/stl_util.h" 9 #include "base/strings/stringprintf.h" 10 #include "base/values.h" 11 #include "chrome/browser/devtools/devtools_adb_bridge.h" 12 #include "chrome/browser/devtools/devtools_target_impl.h" 13 #include "chrome/browser/devtools/port_forwarding_controller.h" 14 #include "content/public/browser/browser_child_process_observer.h" 15 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/child_process_data.h" 17 #include "content/public/browser/notification_observer.h" 18 #include "content/public/browser/notification_registrar.h" 19 #include "content/public/browser/notification_service.h" 20 #include "content/public/browser/notification_source.h" 21 #include "content/public/browser/notification_types.h" 22 #include "content/public/browser/render_process_host.h" 23 #include "content/public/browser/render_view_host.h" 24 #include "content/public/browser/web_contents.h" 25 #include "content/public/browser/worker_service.h" 26 #include "content/public/browser/worker_service_observer.h" 27 #include "content/public/common/process_type.h" 28 #include "net/base/escape.h" 29 30 using content::BrowserThread; 31 using content::WebContents; 32 33 namespace { 34 35 const char kTargetSourceField[] = "source"; 36 const char kTargetSourceRenderer[] = "renderers"; 37 const char kTargetSourceWorker[] = "workers"; 38 const char kTargetSourceAdb[] = "adb"; 39 40 const char kTargetIdField[] = "id"; 41 const char kTargetTypeField[] = "type"; 42 const char kAttachedField[] = "attached"; 43 const char kUrlField[] = "url"; 44 const char kNameField[] = "name"; 45 const char kFaviconUrlField[] = "faviconUrl"; 46 const char kDescriptionField[] = "description"; 47 48 const char kGuestList[] = "guests"; 49 50 const char kAdbModelField[] = "adbModel"; 51 const char kAdbConnectedField[] = "adbConnected"; 52 const char kAdbSerialField[] = "adbSerial"; 53 const char kAdbPortStatus[] = "adbPortStatus"; 54 const char kAdbBrowsersList[] = "browsers"; 55 56 const char kAdbBrowserNameField[] = "adbBrowserName"; 57 const char kAdbBrowserVersionField[] = "adbBrowserVersion"; 58 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion"; 59 const char kAdbPagesList[] = "pages"; 60 61 const char kAdbScreenWidthField[] = "adbScreenWidth"; 62 const char kAdbScreenHeightField[] = "adbScreenHeight"; 63 const char kAdbAttachedForeignField[] = "adbAttachedForeign"; 64 65 // CancelableTimer ------------------------------------------------------------ 66 67 class CancelableTimer { 68 public: 69 CancelableTimer(base::Closure callback, base::TimeDelta delay) 70 : callback_(callback), 71 weak_factory_(this) { 72 base::MessageLoop::current()->PostDelayedTask( 73 FROM_HERE, 74 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()), 75 delay); 76 }; 77 78 private: 79 void Fire() { callback_.Run(); } 80 81 base::Closure callback_; 82 base::WeakPtrFactory<CancelableTimer> weak_factory_; 83 }; 84 85 // RenderViewHostTargetsUIHandler --------------------------------------------- 86 87 class RenderViewHostTargetsUIHandler 88 : public DevToolsTargetsUIHandler, 89 public content::NotificationObserver { 90 public: 91 explicit RenderViewHostTargetsUIHandler(Callback callback); 92 virtual ~RenderViewHostTargetsUIHandler(); 93 private: 94 // content::NotificationObserver overrides. 95 virtual void Observe(int type, 96 const content::NotificationSource& source, 97 const content::NotificationDetails& details) OVERRIDE; 98 99 void UpdateTargets(); 100 101 content::NotificationRegistrar notification_registrar_; 102 scoped_ptr<CancelableTimer> timer_; 103 }; 104 105 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler( 106 Callback callback) 107 : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) { 108 notification_registrar_.Add(this, 109 content::NOTIFICATION_WEB_CONTENTS_CONNECTED, 110 content::NotificationService::AllSources()); 111 notification_registrar_.Add(this, 112 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 113 content::NotificationService::AllSources()); 114 notification_registrar_.Add(this, 115 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 116 content::NotificationService::AllSources()); 117 UpdateTargets(); 118 } 119 120 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() { 121 notification_registrar_.RemoveAll(); 122 } 123 124 void RenderViewHostTargetsUIHandler::Observe( 125 int type, 126 const content::NotificationSource& source, 127 const content::NotificationDetails& details) { 128 const int kUpdateDelay = 100; 129 timer_.reset( 130 new CancelableTimer( 131 base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets, 132 base::Unretained(this)), 133 base::TimeDelta::FromMilliseconds(kUpdateDelay))); 134 } 135 136 void RenderViewHostTargetsUIHandler::UpdateTargets() { 137 scoped_ptr<ListValue> list_value(new ListValue()); 138 139 std::map<WebContents*, DictionaryValue*> web_contents_to_descriptor_; 140 std::vector<DevToolsTargetImpl*> guest_targets; 141 142 DevToolsTargetImpl::List targets = 143 DevToolsTargetImpl::EnumerateRenderViewHostTargets(); 144 145 STLDeleteValues(&targets_); 146 for (DevToolsTargetImpl::List::iterator it = targets.begin(); 147 it != targets.end(); ++it) { 148 scoped_ptr<DevToolsTargetImpl> target(*it); 149 content::RenderViewHost* rvh = target->GetRenderViewHost(); 150 if (!rvh) 151 continue; 152 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 153 if (!web_contents) 154 continue; 155 156 DevToolsTargetImpl* target_ptr = target.get(); 157 targets_[target_ptr->GetId()] = target.release(); 158 if (rvh->GetProcess()->IsGuest()) { 159 guest_targets.push_back(target_ptr); 160 } else { 161 DictionaryValue* descriptor = Serialize(*target_ptr); 162 list_value->Append(descriptor); 163 web_contents_to_descriptor_[web_contents] = descriptor; 164 } 165 } 166 167 // Add the list of guest-views to each of its embedders. 168 for (std::vector<DevToolsTargetImpl*>::iterator it(guest_targets.begin()); 169 it != guest_targets.end(); ++it) { 170 DevToolsTargetImpl* guest = (*it); 171 WebContents* guest_web_contents = 172 WebContents::FromRenderViewHost(guest->GetRenderViewHost()); 173 WebContents* embedder = guest_web_contents->GetEmbedderWebContents(); 174 if (embedder && web_contents_to_descriptor_.count(embedder) > 0) { 175 DictionaryValue* parent = web_contents_to_descriptor_[embedder]; 176 ListValue* guests = NULL; 177 if (!parent->GetList(kGuestList, &guests)) { 178 guests = new ListValue(); 179 parent->Set(kGuestList, guests); 180 } 181 guests->Append(Serialize(*guest)); 182 } 183 } 184 185 SendSerializedTargets(list_value.Pass()); 186 } 187 188 // WorkerObserver ------------------------------------------------------------- 189 190 class WorkerObserver 191 : public content::WorkerServiceObserver, 192 public base::RefCountedThreadSafe<WorkerObserver> { 193 public: 194 WorkerObserver() {} 195 196 void Start(DevToolsTargetImpl::Callback callback) { 197 DCHECK(callback_.is_null()); 198 DCHECK(!callback.is_null()); 199 callback_ = callback; 200 BrowserThread::PostTask( 201 BrowserThread::IO, FROM_HERE, 202 base::Bind(&WorkerObserver::StartOnIOThread, this)); 203 } 204 205 void Stop() { 206 DCHECK(!callback_.is_null()); 207 callback_ = DevToolsTargetImpl::Callback(); 208 BrowserThread::PostTask( 209 BrowserThread::IO, FROM_HERE, 210 base::Bind(&WorkerObserver::StopOnIOThread, this)); 211 } 212 213 void Enumerate() { 214 BrowserThread::PostTask( 215 BrowserThread::IO, FROM_HERE, 216 base::Bind(&WorkerObserver::EnumerateOnIOThread, 217 this)); 218 } 219 220 private: 221 friend class base::RefCountedThreadSafe<WorkerObserver>; 222 virtual ~WorkerObserver() {} 223 224 // content::WorkerServiceObserver overrides: 225 virtual void WorkerCreated( 226 const GURL& url, 227 const base::string16& name, 228 int process_id, 229 int route_id) OVERRIDE { 230 EnumerateOnIOThread(); 231 } 232 233 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE { 234 EnumerateOnIOThread(); 235 } 236 237 void StartOnIOThread() { 238 content::WorkerService::GetInstance()->AddObserver(this); 239 EnumerateOnIOThread(); 240 } 241 242 void StopOnIOThread() { 243 content::WorkerService::GetInstance()->RemoveObserver(this); 244 } 245 246 void EnumerateOnIOThread() { 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 248 DevToolsTargetImpl::EnumerateWorkerTargets( 249 base::Bind(&WorkerObserver::RespondOnUIThread, this)); 250 } 251 252 void RespondOnUIThread(const DevToolsTargetImpl::List& targets) { 253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 254 if (callback_.is_null()) 255 return; 256 callback_.Run(targets); 257 } 258 259 DevToolsTargetImpl::Callback callback_; 260 }; 261 262 // WorkerTargetsUIHandler ----------------------------------------------------- 263 264 class WorkerTargetsUIHandler 265 : public DevToolsTargetsUIHandler, 266 public content::BrowserChildProcessObserver { 267 public: 268 explicit WorkerTargetsUIHandler(Callback callback); 269 virtual ~WorkerTargetsUIHandler(); 270 271 private: 272 // content::BrowserChildProcessObserver overrides. 273 virtual void BrowserChildProcessHostConnected( 274 const content::ChildProcessData& data) OVERRIDE; 275 virtual void BrowserChildProcessHostDisconnected( 276 const content::ChildProcessData& data) OVERRIDE; 277 278 void UpdateTargets(const DevToolsTargetImpl::List& targets); 279 280 scoped_refptr<WorkerObserver> observer_; 281 }; 282 283 WorkerTargetsUIHandler::WorkerTargetsUIHandler(Callback callback) 284 : DevToolsTargetsUIHandler(kTargetSourceWorker, callback), 285 observer_(new WorkerObserver()) { 286 observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets, 287 base::Unretained(this))); 288 BrowserChildProcessObserver::Add(this); 289 } 290 291 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() { 292 BrowserChildProcessObserver::Remove(this); 293 observer_->Stop(); 294 } 295 296 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected( 297 const content::ChildProcessData& data) { 298 if (data.process_type == content::PROCESS_TYPE_WORKER) 299 observer_->Enumerate(); 300 } 301 302 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected( 303 const content::ChildProcessData& data) { 304 if (data.process_type == content::PROCESS_TYPE_WORKER) 305 observer_->Enumerate(); 306 } 307 308 void WorkerTargetsUIHandler::UpdateTargets( 309 const DevToolsTargetImpl::List& targets) { 310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 311 scoped_ptr<ListValue> list_value(new ListValue()); 312 STLDeleteValues(&targets_); 313 for (DevToolsTargetImpl::List::const_iterator it = targets.begin(); 314 it != targets.end(); ++it) { 315 DevToolsTargetImpl* target = *it; 316 list_value->Append(Serialize(*target)); 317 targets_[target->GetId()] = target; 318 } 319 SendSerializedTargets(list_value.Pass()); 320 } 321 322 // AdbTargetsUIHandler -------------------------------------------------------- 323 324 class AdbTargetsUIHandler 325 : public DevToolsRemoteTargetsUIHandler, 326 public DevToolsAdbBridge::Listener { 327 public: 328 AdbTargetsUIHandler(Callback callback, Profile* profile); 329 virtual ~AdbTargetsUIHandler(); 330 331 virtual void Open(const std::string& browser_id, 332 const std::string& url) OVERRIDE; 333 334 private: 335 // DevToolsAdbBridge::Listener overrides. 336 virtual void RemoteDevicesChanged( 337 DevToolsAdbBridge::RemoteDevices* devices) OVERRIDE; 338 339 Profile* profile_; 340 341 typedef std::map<std::string, 342 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > RemoteBrowsers; 343 RemoteBrowsers remote_browsers_; 344 }; 345 346 AdbTargetsUIHandler::AdbTargetsUIHandler(Callback callback, Profile* profile) 347 : DevToolsRemoteTargetsUIHandler(kTargetSourceAdb, callback), 348 profile_(profile) { 349 DevToolsAdbBridge* adb_bridge = 350 DevToolsAdbBridge::Factory::GetForProfile(profile_); 351 if (adb_bridge) 352 adb_bridge->AddListener(this); 353 } 354 355 AdbTargetsUIHandler::~AdbTargetsUIHandler() { 356 DevToolsAdbBridge* adb_bridge = 357 DevToolsAdbBridge::Factory::GetForProfile(profile_); 358 if (adb_bridge) 359 adb_bridge->RemoveListener(this); 360 } 361 362 void AdbTargetsUIHandler::Open(const std::string& browser_id, 363 const std::string& url) { 364 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id); 365 if (it != remote_browsers_.end()) 366 it->second->Open(url); 367 } 368 369 void AdbTargetsUIHandler::RemoteDevicesChanged( 370 DevToolsAdbBridge::RemoteDevices* devices) { 371 PortForwardingController* port_forwarding_controller = 372 PortForwardingController::Factory::GetForProfile(profile_); 373 PortForwardingController::DevicesStatus port_forwarding_status; 374 if (port_forwarding_controller) 375 port_forwarding_status = 376 port_forwarding_controller->UpdateDeviceList(*devices); 377 378 remote_browsers_.clear(); 379 STLDeleteValues(&targets_); 380 381 scoped_ptr<ListValue> device_list(new ListValue()); 382 for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin(); 383 dit != devices->end(); ++dit) { 384 DevToolsAdbBridge::RemoteDevice* device = dit->get(); 385 DictionaryValue* device_data = new DictionaryValue(); 386 device_data->SetString(kAdbModelField, device->GetModel()); 387 device_data->SetString(kAdbSerialField, device->GetSerial()); 388 device_data->SetBoolean(kAdbConnectedField, device->IsConnected()); 389 std::string device_id = base::StringPrintf( 390 "device:%s", 391 device->GetSerial().c_str()); 392 device_data->SetString(kTargetIdField, device_id); 393 ListValue* browser_list = new ListValue(); 394 device_data->Set(kAdbBrowsersList, browser_list); 395 396 DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers(); 397 for (DevToolsAdbBridge::RemoteBrowsers::iterator bit = 398 browsers.begin(); bit != browsers.end(); ++bit) { 399 DevToolsAdbBridge::RemoteBrowser* browser = bit->get(); 400 DictionaryValue* browser_data = new DictionaryValue(); 401 browser_data->SetString(kAdbBrowserNameField, browser->display_name()); 402 browser_data->SetString(kAdbBrowserVersionField, browser->version()); 403 DevToolsAdbBridge::RemoteBrowser::ParsedVersion parsed = 404 browser->GetParsedVersion(); 405 browser_data->SetInteger( 406 kAdbBrowserChromeVersionField, 407 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0); 408 std::string browser_id = base::StringPrintf( 409 "browser:%s:%s:%s:%s", 410 device->GetSerial().c_str(), // Ensure uniqueness across devices. 411 browser->display_name().c_str(), // Sort by display name. 412 browser->version().c_str(), // Then by version. 413 browser->socket().c_str()); // Ensure uniqueness on the device. 414 browser_data->SetString(kTargetIdField, browser_id); 415 browser_data->SetString(kTargetSourceField, source_id()); 416 remote_browsers_[browser_id] = browser; 417 ListValue* page_list = new ListValue(); 418 browser_data->Set(kAdbPagesList, page_list); 419 420 DevToolsTargetImpl::List pages = browser->CreatePageTargets(); 421 for (DevToolsTargetImpl::List::iterator it = 422 pages.begin(); it != pages.end(); ++it) { 423 DevToolsTargetImpl* target = *it; 424 DictionaryValue* target_data = Serialize(*target); 425 target_data->SetBoolean( 426 kAdbAttachedForeignField, 427 target->IsAttached() && 428 !DevToolsAdbBridge::HasDevToolsWindow(target->GetId())); 429 // Pass the screen size in the target object to make sure that 430 // the caching logic does not prevent the target item from updating 431 // when the screen size changes. 432 gfx::Size screen_size = device->screen_size(); 433 target_data->SetInteger(kAdbScreenWidthField, screen_size.width()); 434 target_data->SetInteger(kAdbScreenHeightField, screen_size.height()); 435 targets_[target->GetId()] = target; 436 page_list->Append(target_data); 437 } 438 browser_list->Append(browser_data); 439 } 440 441 if (port_forwarding_controller) { 442 PortForwardingController::DevicesStatus::iterator sit = 443 port_forwarding_status.find(device->GetSerial()); 444 if (sit != port_forwarding_status.end()) { 445 DictionaryValue* port_status_dict = new DictionaryValue(); 446 typedef PortForwardingController::PortStatusMap StatusMap; 447 const StatusMap& port_status = sit->second; 448 for (StatusMap::const_iterator it = port_status.begin(); 449 it != port_status.end(); ++it) { 450 port_status_dict->SetInteger( 451 base::StringPrintf("%d", it->first), it->second); 452 } 453 device_data->Set(kAdbPortStatus, port_status_dict); 454 } 455 } 456 457 device_list->Append(device_data); 458 } 459 SendSerializedTargets(device_list.Pass()); 460 } 461 462 } // namespace 463 464 // DevToolsTargetsUIHandler --------------------------------------------------- 465 466 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler( 467 const std::string& source_id, 468 Callback callback) 469 : source_id_(source_id), 470 callback_(callback) { 471 } 472 473 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() { 474 STLDeleteValues(&targets_); 475 } 476 477 // static 478 scoped_ptr<DevToolsTargetsUIHandler> 479 DevToolsTargetsUIHandler::CreateForRenderers( 480 DevToolsTargetsUIHandler::Callback callback) { 481 return scoped_ptr<DevToolsTargetsUIHandler>( 482 new RenderViewHostTargetsUIHandler(callback)); 483 } 484 485 // static 486 scoped_ptr<DevToolsTargetsUIHandler> 487 DevToolsTargetsUIHandler::CreateForWorkers( 488 DevToolsTargetsUIHandler::Callback callback) { 489 return scoped_ptr<DevToolsTargetsUIHandler>( 490 new WorkerTargetsUIHandler(callback)); 491 } 492 493 void DevToolsTargetsUIHandler::Inspect(const std::string& target_id, 494 Profile* profile) { 495 TargetMap::iterator it = targets_.find(target_id); 496 if (it != targets_.end()) 497 it->second->Inspect(profile); 498 } 499 500 void DevToolsTargetsUIHandler::Activate(const std::string& target_id) { 501 TargetMap::iterator it = targets_.find(target_id); 502 if (it != targets_.end()) 503 it->second->Activate(); 504 } 505 506 void DevToolsTargetsUIHandler::Close(const std::string& target_id) { 507 TargetMap::iterator it = targets_.find(target_id); 508 if (it != targets_.end()) 509 it->second->Close(); 510 } 511 512 void DevToolsTargetsUIHandler::Reload(const std::string& target_id) { 513 TargetMap::iterator it = targets_.find(target_id); 514 if (it != targets_.end()) 515 it->second->Reload(); 516 } 517 518 base::DictionaryValue* 519 DevToolsTargetsUIHandler::Serialize( 520 const DevToolsTargetImpl& target) { 521 DictionaryValue* target_data = new DictionaryValue(); 522 target_data->SetString(kTargetSourceField, source_id_); 523 target_data->SetString(kTargetIdField, target.GetId()); 524 target_data->SetString(kTargetTypeField, target.GetType()); 525 target_data->SetBoolean(kAttachedField, target.IsAttached()); 526 target_data->SetString(kUrlField, target.GetUrl().spec()); 527 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle())); 528 target_data->SetString(kFaviconUrlField, target.GetFaviconUrl().spec()); 529 target_data->SetString(kDescriptionField, target.GetDescription()); 530 return target_data; 531 } 532 533 void DevToolsTargetsUIHandler::SendSerializedTargets( 534 scoped_ptr<ListValue> list) { 535 callback_.Run(source_id_, list.Pass()); 536 } 537 538 // DevToolsRemoteTargetsUIHandler --------------------------------------------- 539 540 DevToolsRemoteTargetsUIHandler::DevToolsRemoteTargetsUIHandler( 541 const std::string& source_id, 542 Callback callback) 543 : DevToolsTargetsUIHandler(source_id, callback) { 544 } 545 546 // static 547 scoped_ptr<DevToolsRemoteTargetsUIHandler> 548 DevToolsRemoteTargetsUIHandler::CreateForAdb( 549 DevToolsTargetsUIHandler::Callback callback, Profile* profile) { 550 return scoped_ptr<DevToolsRemoteTargetsUIHandler>( 551 new AdbTargetsUIHandler(callback, profile)); 552 } 553