inspect_ui.cc revision bb1529ce867d8845a77ec7cdf3e3003ef1771a40
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/strings/string_number_conversions.h" 14#include "base/strings/string_util.h" 15#include "base/strings/stringprintf.h" 16#include "base/strings/utf_string_conversions.h" 17#include "base/values.h" 18#include "chrome/browser/devtools/devtools_window.h" 19#include "chrome/browser/extensions/extension_service.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" 22#include "chrome/browser/ui/webui/theme_source.h" 23#include "chrome/common/pref_names.h" 24#include "chrome/common/url_constants.h" 25#include "content/public/browser/browser_thread.h" 26#include "content/public/browser/child_process_data.h" 27#include "content/public/browser/devtools_agent_host.h" 28#include "content/public/browser/devtools_client_host.h" 29#include "content/public/browser/devtools_manager.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/web_contents.h" 38#include "content/public/browser/web_ui.h" 39#include "content/public/browser/web_ui_data_source.h" 40#include "content/public/browser/web_ui_message_handler.h" 41#include "content/public/browser/worker_service.h" 42#include "content/public/browser/worker_service_observer.h" 43#include "content/public/common/process_type.h" 44#include "grit/browser_resources.h" 45#include "grit/generated_resources.h" 46#include "net/base/escape.h" 47#include "net/base/net_errors.h" 48#include "ui/base/resource/resource_bundle.h" 49 50using content::BrowserThread; 51using content::ChildProcessData; 52using content::DevToolsAgentHost; 53using content::DevToolsClientHost; 54using content::DevToolsManager; 55using content::RenderProcessHost; 56using content::RenderViewHost; 57using content::RenderViewHostDelegate; 58using content::RenderWidgetHost; 59using content::WebContents; 60using content::WebUIMessageHandler; 61using content::WorkerService; 62using content::WorkerServiceObserver; 63 64namespace { 65 66static const char kDataFile[] = "targets-data.json"; 67static const char kAdbPages[] = "adb-pages"; 68 69static const char kAppTargetType[] = "app"; 70static const char kExtensionTargetType[] = "extension"; 71static const char kPageTargetType[] = "page"; 72static const char kWorkerTargetType[] = "worker"; 73static const char kAdbTargetType[] = "adb_page"; 74 75static const char kInitUICommand[] = "init-ui"; 76static const char kInspectCommand[] = "inspect"; 77static const char kTerminateCommand[] = "terminate"; 78static const char kReloadCommand[] = "reload"; 79static const char kOpenCommand[] = "open"; 80 81static const char kPortForwardingEnabledCommand[] = 82 "set-port-forwarding-enabled"; 83static const char kPortForwardingConfigCommand[] = "set-port-forwarding-config"; 84 85static const char kTargetTypeField[] = "type"; 86static const char kAttachedField[] = "attached"; 87static const char kProcessIdField[] = "processId"; 88static const char kRouteIdField[] = "routeId"; 89static const char kUrlField[] = "url"; 90static const char kNameField[] = "name"; 91static const char kFaviconUrlField[] = "faviconUrl"; 92static const char kPidField[] = "pid"; 93static const char kAdbSerialField[] = "adbSerial"; 94static const char kAdbModelField[] = "adbModel"; 95static const char kAdbBrowserNameField[] = "adbBrowserName"; 96static const char kAdbGlobalIdField[] = "adbGlobalId"; 97static const char kAdbBrowsersField[] = "browsers"; 98static const char kAdbPagesField[] = "pages"; 99 100DictionaryValue* BuildTargetDescriptor( 101 const std::string& target_type, 102 bool attached, 103 const GURL& url, 104 const std::string& name, 105 const GURL& favicon_url, 106 int process_id, 107 int route_id, 108 base::ProcessHandle handle = base::kNullProcessHandle) { 109 DictionaryValue* target_data = new DictionaryValue(); 110 target_data->SetString(kTargetTypeField, target_type); 111 target_data->SetBoolean(kAttachedField, attached); 112 target_data->SetInteger(kProcessIdField, process_id); 113 target_data->SetInteger(kRouteIdField, route_id); 114 target_data->SetString(kUrlField, url.spec()); 115 target_data->SetString(kNameField, net::EscapeForHTML(name)); 116 target_data->SetInteger(kPidField, base::GetProcId(handle)); 117 target_data->SetString(kFaviconUrlField, favicon_url.spec()); 118 119 return target_data; 120} 121 122bool HasClientHost(RenderViewHost* rvh) { 123 if (!DevToolsAgentHost::HasFor(rvh)) 124 return false; 125 126 scoped_refptr<DevToolsAgentHost> agent( 127 DevToolsAgentHost::GetOrCreateFor(rvh)); 128 return agent->IsAttached(); 129} 130 131DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) { 132 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 133 std::string title; 134 std::string target_type = is_tab ? kPageTargetType : ""; 135 GURL url; 136 GURL favicon_url; 137 if (web_contents) { 138 url = web_contents->GetURL(); 139 title = UTF16ToUTF8(web_contents->GetTitle()); 140 content::NavigationController& controller = web_contents->GetController(); 141 content::NavigationEntry* entry = controller.GetActiveEntry(); 142 if (entry != NULL && entry->GetURL().is_valid()) 143 favicon_url = entry->GetFavicon().url; 144 145 Profile* profile = Profile::FromBrowserContext( 146 web_contents->GetBrowserContext()); 147 if (profile) { 148 ExtensionService* extension_service = profile->GetExtensionService(); 149 const extensions::Extension* extension = extension_service-> 150 extensions()->GetByID(url.host()); 151 if (extension) { 152 if (extension->is_hosted_app() 153 || extension->is_legacy_packaged_app() 154 || extension->is_platform_app()) 155 target_type = kAppTargetType; 156 else 157 target_type = kExtensionTargetType; 158 title = extension->name(); 159 } 160 } 161 } 162 163 return BuildTargetDescriptor(target_type, 164 HasClientHost(rvh), 165 url, 166 title, 167 favicon_url, 168 rvh->GetProcess()->GetID(), 169 rvh->GetRoutingID()); 170} 171 172class InspectMessageHandler : public WebUIMessageHandler { 173 public: 174 explicit InspectMessageHandler(InspectUI* inspect_ui) 175 : inspect_ui_(inspect_ui) {} 176 virtual ~InspectMessageHandler() {} 177 178 private: 179 // WebUIMessageHandler implementation. 180 virtual void RegisterMessages() OVERRIDE; 181 182 void HandleInitUICommand(const ListValue* args); 183 void HandleInspectCommand(const ListValue* args); 184 void HandleTerminateCommand(const ListValue* args); 185 void HandleReloadCommand(const ListValue* args); 186 void HandleOpenCommand(const ListValue* args); 187 void HandlePortForwardingEnabledCommand(const ListValue* args); 188 void HandlePortForwardingConfigCommand(const ListValue* args); 189 190 static bool GetProcessAndRouteId(const ListValue* args, 191 int* process_id, 192 int* route_id); 193 194 static bool GetRemotePageId(const ListValue* args, std::string* page_id); 195 196 InspectUI* inspect_ui_; 197 198 DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler); 199}; 200 201void InspectMessageHandler::RegisterMessages() { 202 web_ui()->RegisterMessageCallback(kInitUICommand, 203 base::Bind(&InspectMessageHandler::HandleInitUICommand, 204 base::Unretained(this))); 205 web_ui()->RegisterMessageCallback(kInspectCommand, 206 base::Bind(&InspectMessageHandler::HandleInspectCommand, 207 base::Unretained(this))); 208 web_ui()->RegisterMessageCallback(kTerminateCommand, 209 base::Bind(&InspectMessageHandler::HandleTerminateCommand, 210 base::Unretained(this))); 211 web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand, 212 base::Bind(&InspectMessageHandler::HandlePortForwardingEnabledCommand, 213 base::Unretained(this))); 214 web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand, 215 base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand, 216 base::Unretained(this))); 217 web_ui()->RegisterMessageCallback(kReloadCommand, 218 base::Bind(&InspectMessageHandler::HandleReloadCommand, 219 base::Unretained(this))); 220 web_ui()->RegisterMessageCallback(kOpenCommand, 221 base::Bind(&InspectMessageHandler::HandleOpenCommand, 222 base::Unretained(this))); 223} 224 225void InspectMessageHandler::HandleInitUICommand(const ListValue*) { 226 inspect_ui_->InitUI(); 227} 228 229void InspectMessageHandler::HandleInspectCommand(const ListValue* args) { 230 Profile* profile = Profile::FromWebUI(web_ui()); 231 if (!profile) 232 return; 233 234 std::string page_id; 235 if (GetRemotePageId(args, &page_id)) { 236 inspect_ui_->InspectRemotePage(page_id); 237 return; 238 } 239 240 int process_id; 241 int route_id; 242 if (!GetProcessAndRouteId(args, &process_id, &route_id) || process_id == 0 243 || route_id == 0) { 244 return; 245 } 246 247 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 248 if (rvh) { 249 DevToolsWindow::OpenDevToolsWindow(rvh); 250 return; 251 } 252 253 scoped_refptr<DevToolsAgentHost> agent_host( 254 DevToolsAgentHost::GetForWorker(process_id, route_id)); 255 if (!agent_host.get()) 256 return; 257 258 DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get()); 259} 260 261static void TerminateWorker(int process_id, int route_id) { 262 WorkerService::GetInstance()->TerminateWorker(process_id, route_id); 263} 264 265void InspectMessageHandler::HandleTerminateCommand(const ListValue* args) { 266 std::string page_id; 267 if (GetRemotePageId(args, &page_id)) { 268 inspect_ui_->CloseRemotePage(page_id); 269 return; 270 } 271 272 int process_id; 273 int route_id; 274 if (!GetProcessAndRouteId(args, &process_id, &route_id)) 275 return; 276 277 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 278 base::Bind(&TerminateWorker, process_id, route_id)); 279} 280 281void InspectMessageHandler::HandleReloadCommand(const ListValue* args) { 282 std::string page_id; 283 if (GetRemotePageId(args, &page_id)) 284 inspect_ui_->ReloadRemotePage(page_id); 285} 286 287void InspectMessageHandler::HandleOpenCommand(const ListValue* args) { 288 std::string browser_id; 289 std::string url; 290 if (args->GetSize() == 2 && 291 args->GetString(0, &browser_id) && 292 args->GetString(1, &url)) { 293 inspect_ui_->OpenRemotePage(browser_id, url); 294 } 295} 296 297bool InspectMessageHandler::GetProcessAndRouteId(const ListValue* args, 298 int* process_id, 299 int* route_id) { 300 const DictionaryValue* data; 301 if (args->GetSize() == 1 && args->GetDictionary(0, &data) && 302 data->GetInteger(kProcessIdField, process_id) && 303 data->GetInteger(kRouteIdField, route_id)) { 304 return true; 305 } 306 return false; 307} 308 309void InspectMessageHandler::HandlePortForwardingEnabledCommand( 310 const ListValue* args) { 311 Profile* profile = Profile::FromWebUI(web_ui()); 312 if (!profile) 313 return; 314 315 bool enabled; 316 if (args->GetSize() == 1 && args->GetBoolean(0, &enabled)) { 317 profile->GetPrefs()->SetBoolean( 318 prefs::kDevToolsPortForwardingEnabled, enabled); 319 } 320} 321 322void InspectMessageHandler::HandlePortForwardingConfigCommand( 323 const ListValue* args) { 324 Profile* profile = Profile::FromWebUI(web_ui()); 325 if (!profile) 326 return; 327 328 const DictionaryValue* dict_src; 329 if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src)) 330 profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src); 331} 332 333bool InspectMessageHandler::GetRemotePageId(const ListValue* args, 334 std::string* page_id) { 335 const DictionaryValue* data; 336 if (args->GetSize() == 1 && args->GetDictionary(0, &data) && 337 data->GetString(kAdbGlobalIdField, page_id)) { 338 return true; 339 } 340 return false; 341} 342 343} // namespace 344 345class InspectUI::WorkerCreationDestructionListener 346 : public WorkerServiceObserver, 347 public base::RefCountedThreadSafe<WorkerCreationDestructionListener> { 348 public: 349 WorkerCreationDestructionListener() 350 : discovery_ui_(NULL) {} 351 352 void Init(InspectUI* workers_ui) { 353 DCHECK(workers_ui); 354 DCHECK(!discovery_ui_); 355 discovery_ui_ = workers_ui; 356 BrowserThread::PostTask( 357 BrowserThread::IO, FROM_HERE, 358 base::Bind(&WorkerCreationDestructionListener::RegisterObserver, 359 this)); 360 } 361 362 void InspectUIDestroyed() { 363 DCHECK(discovery_ui_); 364 discovery_ui_ = NULL; 365 BrowserThread::PostTask( 366 BrowserThread::IO, FROM_HERE, 367 base::Bind(&WorkerCreationDestructionListener::UnregisterObserver, 368 this)); 369 } 370 371 void InitUI() { 372 BrowserThread::PostTask( 373 BrowserThread::IO, FROM_HERE, 374 base::Bind(&WorkerCreationDestructionListener::CollectWorkersData, 375 this)); 376 } 377 378 private: 379 friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>; 380 virtual ~WorkerCreationDestructionListener() {} 381 382 virtual void WorkerCreated( 383 const GURL& url, 384 const string16& name, 385 int process_id, 386 int route_id) OVERRIDE { 387 CollectWorkersData(); 388 } 389 390 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE { 391 CollectWorkersData(); 392 } 393 394 void CollectWorkersData() { 395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 396 scoped_ptr<ListValue> target_list(new ListValue()); 397 std::vector<WorkerService::WorkerInfo> worker_info = 398 WorkerService::GetInstance()->GetWorkers(); 399 for (size_t i = 0; i < worker_info.size(); ++i) { 400 target_list->Append(BuildTargetDescriptor( 401 kWorkerTargetType, 402 false, 403 worker_info[i].url, 404 UTF16ToUTF8(worker_info[i].name), 405 GURL(), 406 worker_info[i].process_id, 407 worker_info[i].route_id, 408 worker_info[i].handle)); 409 } 410 411 BrowserThread::PostTask( 412 BrowserThread::UI, FROM_HERE, 413 base::Bind(&WorkerCreationDestructionListener::PopulateWorkersList, 414 this, base::Owned(target_list.release()))); 415 } 416 417 void RegisterObserver() { 418 WorkerService::GetInstance()->AddObserver(this); 419 } 420 421 void UnregisterObserver() { 422 WorkerService::GetInstance()->RemoveObserver(this); 423 } 424 425 void PopulateWorkersList(ListValue* target_list) { 426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 427 if (discovery_ui_) { 428 discovery_ui_->web_ui()->CallJavascriptFunction( 429 "populateWorkersList", *target_list); 430 } 431 } 432 433 InspectUI* discovery_ui_; 434}; 435 436InspectUI::InspectUI(content::WebUI* web_ui) 437 : WebUIController(web_ui) { 438 web_ui->AddMessageHandler(new InspectMessageHandler(this)); 439 Profile* profile = Profile::FromWebUI(web_ui); 440 content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource()); 441 442 // Set up the chrome://theme/ source. 443 ThemeSource* theme = new ThemeSource(profile); 444 content::URLDataSource::Add(profile, theme); 445} 446 447InspectUI::~InspectUI() { 448 StopListeningNotifications(); 449} 450 451void InspectUI::InitUI() { 452 StartListeningNotifications(); 453 PopulateLists(); 454 UpdatePortForwardingEnabled(); 455 UpdatePortForwardingConfig(); 456 observer_->InitUI(); 457} 458 459void InspectUI::InspectRemotePage(const std::string& id) { 460 RemotePages::iterator it = remote_pages_.find(id); 461 if (it != remote_pages_.end()) { 462 Profile* profile = Profile::FromWebUI(web_ui()); 463 it->second->Inspect(profile); 464 } 465} 466 467void InspectUI::ReloadRemotePage(const std::string& id) { 468 RemotePages::iterator it = remote_pages_.find(id); 469 if (it != remote_pages_.end()) 470 it->second->Reload(); 471} 472 473void InspectUI::CloseRemotePage(const std::string& id) { 474 RemotePages::iterator it = remote_pages_.find(id); 475 if (it != remote_pages_.end()) 476 it->second->Close(); 477} 478 479void InspectUI::OpenRemotePage(const std::string& browser_id, 480 const std::string& url) { 481 GURL gurl(url); 482 if (!gurl.is_valid()) { 483 gurl = GURL("http://" + url); 484 if (!gurl.is_valid()) 485 return; 486 } 487 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id); 488 if (it != remote_browsers_.end()) 489 it->second->Open(gurl.spec()); 490} 491 492void InspectUI::PopulateLists() { 493 std::set<RenderViewHost*> tab_rvhs; 494 for (TabContentsIterator it; !it.done(); it.Next()) 495 tab_rvhs.insert(it->GetRenderViewHost()); 496 497 scoped_ptr<ListValue> target_list(new ListValue()); 498 499 std::vector<RenderViewHost*> rvh_vector = 500 DevToolsAgentHost::GetValidRenderViewHosts(); 501 502 for (std::vector<RenderViewHost*>::iterator it(rvh_vector.begin()); 503 it != rvh_vector.end(); it++) { 504 bool is_tab = tab_rvhs.find(*it) != tab_rvhs.end(); 505 target_list->Append(BuildTargetDescriptor(*it, is_tab)); 506 } 507 web_ui()->CallJavascriptFunction("populateLists", *target_list.get()); 508} 509 510void InspectUI::Observe(int type, 511 const content::NotificationSource& source, 512 const content::NotificationDetails& details) { 513 if (source != content::Source<WebContents>(web_ui()->GetWebContents())) 514 PopulateLists(); 515 else if (type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED) 516 StopListeningNotifications(); 517} 518 519void InspectUI::StartListeningNotifications() { 520 if (observer_) 521 return; 522 523 observer_ = new WorkerCreationDestructionListener(); 524 observer_->Init(this); 525 526 Profile* profile = Profile::FromWebUI(web_ui()); 527 DevToolsAdbBridge* adb_bridge = 528 DevToolsAdbBridge::Factory::GetForProfile(profile); 529 if (adb_bridge) 530 adb_bridge->AddListener(this); 531 532 notification_registrar_.Add(this, 533 content::NOTIFICATION_WEB_CONTENTS_CONNECTED, 534 content::NotificationService::AllSources()); 535 notification_registrar_.Add(this, 536 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 537 content::NotificationService::AllSources()); 538 notification_registrar_.Add(this, 539 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 540 content::NotificationService::AllSources()); 541 542 pref_change_registrar_.Init(profile->GetPrefs()); 543 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, 544 base::Bind(&InspectUI::UpdatePortForwardingEnabled, 545 base::Unretained(this))); 546 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, 547 base::Bind(&InspectUI::UpdatePortForwardingConfig, 548 base::Unretained(this))); 549} 550 551void InspectUI::StopListeningNotifications() 552{ 553 if (!observer_.get()) 554 return; 555 Profile* profile = Profile::FromWebUI(web_ui()); 556 DevToolsAdbBridge* adb_bridge = 557 DevToolsAdbBridge::Factory::GetForProfile(profile); 558 if (adb_bridge) 559 adb_bridge->RemoveListener(this); 560 observer_->InspectUIDestroyed(); 561 observer_ = NULL; 562 notification_registrar_.RemoveAll(); 563 pref_change_registrar_.RemoveAll(); 564} 565 566content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() { 567 content::WebUIDataSource* source = 568 content::WebUIDataSource::Create(chrome::kChromeUIInspectHost); 569 source->AddResourcePath("inspect.css", IDR_INSPECT_CSS); 570 source->AddResourcePath("inspect.js", IDR_INSPECT_JS); 571 source->SetDefaultResource(IDR_INSPECT_HTML); 572 return source; 573} 574 575void InspectUI::RemoteDevicesChanged( 576 DevToolsAdbBridge::RemoteDevices* devices) { 577 remote_browsers_.clear(); 578 remote_pages_.clear(); 579 ListValue device_list; 580 for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin(); 581 dit != devices->end(); ++dit) { 582 DevToolsAdbBridge::RemoteDevice* device = dit->get(); 583 DictionaryValue* device_data = new DictionaryValue(); 584 device_data->SetString(kAdbModelField, device->model()); 585 device_data->SetString(kAdbSerialField, device->serial()); 586 std::string device_id = base::StringPrintf( 587 "device:%s", 588 device->serial().c_str()); 589 device_data->SetString(kAdbGlobalIdField, device_id); 590 ListValue* browser_list = new ListValue(); 591 device_data->Set(kAdbBrowsersField, browser_list); 592 593 DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers(); 594 for (DevToolsAdbBridge::RemoteBrowsers::iterator bit = 595 browsers.begin(); bit != browsers.end(); ++bit) { 596 DevToolsAdbBridge::RemoteBrowser* browser = bit->get(); 597 DictionaryValue* browser_data = new DictionaryValue(); 598 browser_data->SetString(kAdbBrowserNameField, browser->name()); 599 std::string browser_id = base::StringPrintf( 600 "browser:%s:%s:%s", 601 device->serial().c_str(), 602 browser->socket().c_str(), 603 browser->name().c_str()); 604 browser_data->SetString(kAdbGlobalIdField, browser_id); 605 remote_browsers_[browser_id] = browser; 606 ListValue* page_list = new ListValue(); 607 browser_data->Set(kAdbPagesField, page_list); 608 609 DevToolsAdbBridge::RemotePages& pages = browser->pages(); 610 for (DevToolsAdbBridge::RemotePages::iterator it = 611 pages.begin(); it != pages.end(); ++it) { 612 DevToolsAdbBridge::RemotePage* page = it->get(); 613 DictionaryValue* page_data = BuildTargetDescriptor( 614 kAdbTargetType, page->attached(), 615 GURL(page->url()), page->title(), GURL(page->favicon_url()), 616 0, 0); 617 std::string page_id = base::StringPrintf("page:%s:%s:%s", 618 device->serial().c_str(), 619 browser->socket().c_str(), 620 page->id().c_str()); 621 page_data->SetString(kAdbGlobalIdField, page_id); 622 remote_pages_[page_id] = page; 623 page_list->Append(page_data); 624 } 625 browser_list->Append(browser_data); 626 } 627 device_list.Append(device_data); 628 } 629 web_ui()->CallJavascriptFunction("populateDeviceLists", device_list); 630} 631 632void InspectUI::UpdatePortForwardingEnabled() { 633 Profile* profile = Profile::FromWebUI(web_ui()); 634 const base::Value* value = profile->GetPrefs()->FindPreference( 635 prefs::kDevToolsPortForwardingEnabled)->GetValue(); 636 web_ui()->CallJavascriptFunction("updatePortForwardingEnabled", *value); 637 638} 639 640void InspectUI::UpdatePortForwardingConfig() { 641 Profile* profile = Profile::FromWebUI(web_ui()); 642 const base::Value* value = profile->GetPrefs()->FindPreference( 643 prefs::kDevToolsPortForwardingConfig)->GetValue(); 644 web_ui()->CallJavascriptFunction("updatePortForwardingConfig", *value); 645} 646