inspect_ui.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/inspect_ui.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
89ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/bind_helpers.h"
11116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/json/json_writer.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/ref_counted_memory.h"
136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/strings/string_util.h"
156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/values.h"
176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/devtools/devtools_window.h"
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/theme_source.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/common/url_constants.h"
246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/public/browser/child_process_data.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/devtools_agent_host.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/devtools_client_host.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/devtools_manager.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/favicon_status.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/navigation_entry.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_types.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_ui.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_ui_data_source.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_ui_message_handler.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/worker_service.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/worker_service_observer.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/process_type.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/browser_resources.h"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
48d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::ChildProcessData;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::DevToolsAgentHost;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::DevToolsClientHost;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::DevToolsManager;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::RenderProcessHost;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::RenderViewHost;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::RenderViewHostDelegate;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::RenderWidgetHost;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::WebContents;
596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using content::WebUIMessageHandler;
606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using content::WorkerService;
616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)using content::WorkerServiceObserver;
626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kDataFile[] = "targets-data.json";
66116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbPages[] = "adb-pages";
67116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kAppTargetType[] = "app";
69d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)static const char kExtensionTargetType[]  = "extension";
70d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)static const char kPageTargetType[]  = "page";
71d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)static const char kWorkerTargetType[]  = "worker";
72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)static const char kAdbTargetType[]  = "adb_page";
73d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kInitUICommand[]  = "init-ui";
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kInspectCommand[]  = "inspect";
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kTerminateCommand[]  = "terminate";
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kPortForwardingEnabledCommand[] =
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "set-port-forwarding-enabled";
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
81116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kTargetTypeField[]  = "type";
82116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAttachedField[]  = "attached";
83116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kProcessIdField[]  = "processId";
84116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kRouteIdField[]  = "routeId";
85116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kUrlField[]  = "url";
86116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kNameField[]  = "name";
87116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kFaviconUrlField[] = "faviconUrl";
88116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kPidField[]  = "pid";
89116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbSerialField[] = "adbSerial";
90116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbModelField[] = "adbModel";
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbBrowserNameField[] = "adbBrowserName";
92116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbPageIdField[] = "adbPageId";
93116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbBrowsersField[] = "browsers";
94116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char kAdbPagesField[] = "pages";
956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
96116680a4aac90f2aa7413d9095a592090648e557Ben MurdochDictionaryValue* BuildTargetDescriptor(
976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::string& target_type,
98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    bool attached,
99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const GURL& url,
100116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const std::string& name,
1016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const GURL& favicon_url,
102116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    int process_id,
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    int route_id,
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::ProcessHandle handle = base::kNullProcessHandle) {
105116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DictionaryValue* target_data = new DictionaryValue();
106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  target_data->SetString(kTargetTypeField, target_type);
107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  target_data->SetBoolean(kAttachedField, attached);
1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  target_data->SetInteger(kProcessIdField, process_id);
1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  target_data->SetInteger(kRouteIdField, route_id);
1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  target_data->SetString(kUrlField, url.spec());
1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  target_data->SetString(kNameField, net::EscapeForHTML(name));
1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  target_data->SetInteger(kPidField, base::GetProcId(handle));
1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  target_data->SetString(kFaviconUrlField, favicon_url.spec());
1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return target_data;
1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool HasClientHost(RenderViewHost* rvh) {
1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!DevToolsAgentHost::HasFor(rvh))
1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return false;
1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_refptr<DevToolsAgentHost> agent(
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      DevToolsAgentHost::GetOrCreateFor(rvh));
1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return agent->IsAttached();
125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string title;
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string target_type = is_tab ? kPageTargetType : "";
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL url;
132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  GURL favicon_url;
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (web_contents) {
134116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    url = web_contents->GetURL();
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    title = UTF16ToUTF8(web_contents->GetTitle());
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    content::NavigationController& controller = web_contents->GetController();
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    content::NavigationEntry* entry = controller.GetActiveEntry();
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (entry != NULL && entry->GetURL().is_valid())
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      favicon_url = entry->GetFavicon().url;
1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Profile* profile = Profile::FromBrowserContext(
14290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        web_contents->GetBrowserContext());
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (profile) {
1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      ExtensionService* extension_service = profile->GetExtensionService();
1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      const extensions::Extension* extension = extension_service->
1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          extensions()->GetByID(url.host());
1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (extension) {
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        if (extension->is_hosted_app()
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            || extension->is_legacy_packaged_app()
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            || extension->is_platform_app())
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          target_type = kAppTargetType;
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        else
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          target_type = kExtensionTargetType;
154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        title = extension->name();
155116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      }
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return BuildTargetDescriptor(target_type,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               HasClientHost(rvh),
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               url,
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                               title,
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                               favicon_url,
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               rvh->GetProcess()->GetID(),
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                               rvh->GetRoutingID());
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class InspectMessageHandler : public WebUIMessageHandler {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit InspectMessageHandler(InspectUI* inspect_ui)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : inspect_ui_(inspect_ui) {}
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~InspectMessageHandler() {}
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private:
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // WebUIMessageHandler implementation.
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void RegisterMessages() OVERRIDE;
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
178116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  void HandleInitUICommand(const ListValue* args);
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void HandleInspectCommand(const ListValue* args);
180116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  void HandleTerminateCommand(const ListValue* args);
181116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  void HandlePortForwardingEnabledCommand(const ListValue* args);
182116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  void HandlePortForwardingConfigCommand(const ListValue* args);
183116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
184116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool GetProcessAndRouteId(const ListValue* args,
185116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                            int* process_id,
186116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                            int* route_id);
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
188116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  InspectUI* inspect_ui_;
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
190116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
191116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch};
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void InspectMessageHandler::RegisterMessages() {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  web_ui()->RegisterMessageCallback(kInitUICommand,
1956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      base::Bind(&InspectMessageHandler::HandleInitUICommand,
1966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                 base::Unretained(this)));
1976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  web_ui()->RegisterMessageCallback(kInspectCommand,
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&InspectMessageHandler::HandleInspectCommand,
199116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                 base::Unretained(this)));
200116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  web_ui()->RegisterMessageCallback(kTerminateCommand,
201116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::Bind(&InspectMessageHandler::HandleTerminateCommand,
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this)));
2036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
204116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::Bind(&InspectMessageHandler::HandlePortForwardingEnabledCommand,
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this)));
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this)));
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void InspectMessageHandler::HandleInitUICommand(const ListValue*) {
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  inspect_ui_->InitUI();
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void InspectMessageHandler::HandleInspectCommand(const ListValue* args) {
2166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  Profile* profile = Profile::FromWebUI(web_ui());
21790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!profile)
21890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
22090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  int process_id;
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int route_id;
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GetProcessAndRouteId(args, &process_id, &route_id) || process_id == 0
22390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      || route_id == 0) {
2246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Check for ADB page id
2256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const DictionaryValue* data;
2266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    std::string page_id;
22790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
2286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        data->GetString(kAdbPageIdField, &page_id)) {
2296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      inspect_ui_->InspectRemotePage(page_id);
2306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
2316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
2326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
2356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (rvh) {
2366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DevToolsWindow::OpenDevToolsWindow(rvh);
2376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
2386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_refptr<DevToolsAgentHost> agent_host(
2416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      DevToolsAgentHost::GetForWorker(process_id, route_id));
2426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!agent_host.get())
2436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
2446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get());
2466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)static void TerminateWorker(int process_id, int route_id) {
2496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  WorkerService::GetInstance()->TerminateWorker(process_id, route_id);
2506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void InspectMessageHandler::HandleTerminateCommand(const ListValue* args) {
2536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int process_id;
2546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int route_id;
2556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!GetProcessAndRouteId(args, &process_id, &route_id))
2566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
2576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
2596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      base::Bind(&TerminateWorker, process_id, route_id));
2606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool InspectMessageHandler::GetProcessAndRouteId(const ListValue* args,
2636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                                 int* process_id,
2646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                                 int* route_id) {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const DictionaryValue* data;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data->GetInteger(kProcessIdField, process_id) &&
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data->GetInteger(kRouteIdField, route_id)) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
272868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
273868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
2746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void InspectMessageHandler::HandlePortForwardingEnabledCommand(
2756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const ListValue* args) {
2766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  Profile* profile = Profile::FromWebUI(web_ui());
2776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!profile)
2786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
2796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  bool enabled;
2816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (args->GetSize() == 1 && args->GetBoolean(0, &enabled)) {
2826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    profile->GetPrefs()->SetBoolean(
2836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        prefs::kDevToolsPortForwardingEnabled, enabled);
2846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void InspectMessageHandler::HandlePortForwardingConfigCommand(
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ListValue* args) {
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Profile* profile = Profile::FromWebUI(web_ui());
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!profile)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const DictionaryValue* dict_src;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
297116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
298116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}  // namespace
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
300116680a4aac90f2aa7413d9095a592090648e557Ben Murdochclass InspectUI::WorkerCreationDestructionListener
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public WorkerServiceObserver,
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      public base::RefCountedThreadSafe<WorkerCreationDestructionListener> {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WorkerCreationDestructionListener()
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : discovery_ui_(NULL) {}
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
307116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  void Init(InspectUI* workers_ui) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(workers_ui);
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!discovery_ui_);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    discovery_ui_ = workers_ui;
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BrowserThread::PostTask(
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        BrowserThread::IO, FROM_HERE,
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&WorkerCreationDestructionListener::RegisterObserver,
3146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   this));
3156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void InspectUIDestroyed() {
3186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(discovery_ui_);
3196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    discovery_ui_ = NULL;
3206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    BrowserThread::PostTask(
3216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        BrowserThread::IO, FROM_HERE,
3226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::Bind(&WorkerCreationDestructionListener::UnregisterObserver,
3236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   this));
3246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void InitUI() {
3276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    BrowserThread::PostTask(
328116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        BrowserThread::IO, FROM_HERE,
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&WorkerCreationDestructionListener::CollectWorkersData,
3306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   this));
3316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
332116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
3336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private:
3346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>;
335116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  virtual ~WorkerCreationDestructionListener() {}
3366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void WorkerCreated(
338116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      const GURL& url,
339116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      const string16& name,
3406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      int process_id,
3416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      int route_id) OVERRIDE {
342116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    CollectWorkersData();
3436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
3466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    CollectWorkersData();
347116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CollectWorkersData() {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
351    scoped_ptr<ListValue> target_list(new ListValue());
352    std::vector<WorkerService::WorkerInfo> worker_info =
353        WorkerService::GetInstance()->GetWorkers();
354    for (size_t i = 0; i < worker_info.size(); ++i) {
355      target_list->Append(BuildTargetDescriptor(
356          kWorkerTargetType,
357          false,
358          worker_info[i].url,
359          UTF16ToUTF8(worker_info[i].name),
360          GURL(),
361          worker_info[i].process_id,
362          worker_info[i].route_id,
363          worker_info[i].handle));
364    }
365
366    BrowserThread::PostTask(
367        BrowserThread::UI, FROM_HERE,
368        base::Bind(&WorkerCreationDestructionListener::PopulateWorkersList,
369                   this, base::Owned(target_list.release())));
370  }
371
372  void RegisterObserver() {
373    WorkerService::GetInstance()->AddObserver(this);
374  }
375
376  void UnregisterObserver() {
377    WorkerService::GetInstance()->RemoveObserver(this);
378  }
379
380  void PopulateWorkersList(ListValue* target_list) {
381    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382    if (discovery_ui_) {
383      discovery_ui_->web_ui()->CallJavascriptFunction(
384          "populateWorkersList", *target_list);
385    }
386  }
387
388  InspectUI* discovery_ui_;
389};
390
391InspectUI::InspectUI(content::WebUI* web_ui)
392    : WebUIController(web_ui) {
393  web_ui->AddMessageHandler(new InspectMessageHandler(this));
394  Profile* profile = Profile::FromWebUI(web_ui);
395  content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
396
397  // Set up the chrome://theme/ source.
398  ThemeSource* theme = new ThemeSource(profile);
399  content::URLDataSource::Add(profile, theme);
400}
401
402InspectUI::~InspectUI() {
403  StopListeningNotifications();
404}
405
406void InspectUI::InitUI() {
407  StartListeningNotifications();
408  PopulateLists();
409  UpdatePortForwardingEnabled();
410  UpdatePortForwardingConfig();
411  observer_->InitUI();
412}
413
414void InspectUI::InspectRemotePage(const std::string& id) {
415  RemotePages::iterator it = remote_pages_.find(id);
416  if (it != remote_pages_.end()) {
417    Profile* profile = Profile::FromWebUI(web_ui());
418    it->second->Inspect(profile);
419  }
420}
421
422void InspectUI::PopulateLists() {
423  std::set<RenderViewHost*> tab_rvhs;
424  for (TabContentsIterator it; !it.done(); it.Next())
425    tab_rvhs.insert(it->GetRenderViewHost());
426
427  scoped_ptr<ListValue> target_list(new ListValue());
428
429  std::vector<RenderViewHost*> rvh_vector =
430      DevToolsAgentHost::GetValidRenderViewHosts();
431
432  for (std::vector<RenderViewHost*>::iterator it(rvh_vector.begin());
433       it != rvh_vector.end(); it++) {
434    bool is_tab = tab_rvhs.find(*it) != tab_rvhs.end();
435    target_list->Append(BuildTargetDescriptor(*it, is_tab));
436  }
437  web_ui()->CallJavascriptFunction("populateLists", *target_list.get());
438}
439
440void InspectUI::Observe(int type,
441    const content::NotificationSource& source,
442    const content::NotificationDetails& details) {
443  if (source != content::Source<WebContents>(web_ui()->GetWebContents()))
444    PopulateLists();
445  else if (type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED)
446    StopListeningNotifications();
447}
448
449void InspectUI::StartListeningNotifications() {
450  if (observer_)
451    return;
452
453  observer_ = new WorkerCreationDestructionListener();
454  observer_->Init(this);
455
456  Profile* profile = Profile::FromWebUI(web_ui());
457  DevToolsAdbBridge* adb_bridge =
458      DevToolsAdbBridge::Factory::GetForProfile(profile);
459  if (adb_bridge)
460    adb_bridge->AddListener(this);
461
462  notification_registrar_.Add(this,
463                              content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
464                              content::NotificationService::AllSources());
465  notification_registrar_.Add(this,
466                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
467                              content::NotificationService::AllSources());
468  notification_registrar_.Add(this,
469                              content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
470                              content::NotificationService::AllSources());
471
472  pref_change_registrar_.Init(profile->GetPrefs());
473  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
474      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
475                 base::Unretained(this)));
476  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
477      base::Bind(&InspectUI::UpdatePortForwardingConfig,
478                 base::Unretained(this)));
479}
480
481void InspectUI::StopListeningNotifications()
482{
483  if (!observer_.get())
484    return;
485  Profile* profile = Profile::FromWebUI(web_ui());
486  DevToolsAdbBridge* adb_bridge =
487      DevToolsAdbBridge::Factory::GetForProfile(profile);
488  if (adb_bridge)
489    adb_bridge->RemoveListener(this);
490  observer_->InspectUIDestroyed();
491  observer_ = NULL;
492  notification_registrar_.RemoveAll();
493  pref_change_registrar_.RemoveAll();
494}
495
496content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
497  content::WebUIDataSource* source =
498      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
499  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
500  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
501  source->SetDefaultResource(IDR_INSPECT_HTML);
502  return source;
503}
504
505void InspectUI::RemoteDevicesChanged(
506    DevToolsAdbBridge::RemoteDevices* devices) {
507  remote_pages_.clear();
508  ListValue device_list;
509  for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
510       dit != devices->end(); ++dit) {
511    DevToolsAdbBridge::RemoteDevice& device = *(dit->get());
512    DictionaryValue* device_data = new DictionaryValue();
513    device_data->SetString(kAdbModelField, device.model());
514    device_data->SetString(kAdbSerialField, device.serial());
515    ListValue* browser_list = new ListValue();
516    device_data->Set(kAdbBrowsersField, browser_list);
517
518    DevToolsAdbBridge::RemoteBrowsers& browsers = device.browsers();
519    for (DevToolsAdbBridge::RemoteBrowsers::iterator bit =
520        browsers.begin(); bit != browsers.end(); ++bit) {
521      DevToolsAdbBridge::RemoteBrowser& browser = *(bit->get());
522      DictionaryValue* browser_data = new DictionaryValue();
523      browser_data->SetString(kAdbBrowserNameField, browser.name());
524      ListValue* page_list = new ListValue();
525      browser_data->Set(kAdbPagesField, page_list);
526
527      DevToolsAdbBridge::RemotePages& pages = browser.pages();
528      for (DevToolsAdbBridge::RemotePages::iterator it =
529          pages.begin(); it != pages.end(); ++it) {
530        DevToolsAdbBridge::RemotePage* page =  it->get();
531        DictionaryValue* page_data = BuildTargetDescriptor(kAdbTargetType,
532            false, GURL(page->url()), page->title(), GURL(page->favicon_url()),
533            0, 0);
534        page_data->SetString(kAdbPageIdField, page->global_id());
535        page_list->Append(page_data);
536        remote_pages_[page->global_id()] = page;
537      }
538      browser_list->Append(browser_data);
539    }
540    device_list.Append(device_data);
541  }
542  web_ui()->CallJavascriptFunction("populateDeviceLists", device_list);
543}
544
545void InspectUI::UpdatePortForwardingEnabled() {
546  Profile* profile = Profile::FromWebUI(web_ui());
547  const base::Value* value = profile->GetPrefs()->FindPreference(
548      prefs::kDevToolsPortForwardingEnabled)->GetValue();
549  web_ui()->CallJavascriptFunction("updatePortForwardingEnabled", *value);
550
551}
552
553void InspectUI::UpdatePortForwardingConfig() {
554  Profile* profile = Profile::FromWebUI(web_ui());
555  const base::Value* value = profile->GetPrefs()->FindPreference(
556      prefs::kDevToolsPortForwardingConfig)->GetValue();
557  web_ui()->CallJavascriptFunction("updatePortForwardingConfig", *value);
558}
559