Home | History | Annotate | Download | only in webui
      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 "base/prefs/pref_service.h"
      8 #include "base/stl_util.h"
      9 #include "chrome/browser/devtools/devtools_target_impl.h"
     10 #include "chrome/browser/devtools/devtools_targets_ui.h"
     11 #include "chrome/browser/devtools/devtools_ui_bindings.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/ui/browser_navigator.h"
     14 #include "chrome/browser/ui/singleton_tabs.h"
     15 #include "chrome/browser/ui/webui/theme_source.h"
     16 #include "chrome/common/pref_names.h"
     17 #include "chrome/common/url_constants.h"
     18 #include "content/public/browser/devtools_agent_host.h"
     19 #include "content/public/browser/devtools_manager.h"
     20 #include "content/public/browser/notification_service.h"
     21 #include "content/public/browser/notification_source.h"
     22 #include "content/public/browser/notification_types.h"
     23 #include "content/public/browser/user_metrics.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "content/public/browser/web_contents_delegate.h"
     26 #include "content/public/browser/web_ui.h"
     27 #include "content/public/browser/web_ui_data_source.h"
     28 #include "content/public/browser/web_ui_message_handler.h"
     29 #include "grit/browser_resources.h"
     30 
     31 using content::WebContents;
     32 using content::WebUIMessageHandler;
     33 
     34 namespace {
     35 
     36 const char kInitUICommand[]  = "init-ui";
     37 const char kInspectCommand[]  = "inspect";
     38 const char kActivateCommand[]  = "activate";
     39 const char kCloseCommand[]  = "close";
     40 const char kReloadCommand[]  = "reload";
     41 const char kOpenCommand[]  = "open";
     42 const char kInspectBrowser[] = "inspect-browser";
     43 const char kLocalHost[] = "localhost";
     44 
     45 const char kDiscoverUsbDevicesEnabledCommand[] =
     46     "set-discover-usb-devices-enabled";
     47 const char kPortForwardingEnabledCommand[] =
     48     "set-port-forwarding-enabled";
     49 const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
     50 
     51 const char kPortForwardingDefaultPort[] = "8080";
     52 const char kPortForwardingDefaultLocation[] = "localhost:8080";
     53 
     54 class InspectMessageHandler : public WebUIMessageHandler {
     55  public:
     56   explicit InspectMessageHandler(InspectUI* inspect_ui)
     57       : inspect_ui_(inspect_ui) {}
     58   virtual ~InspectMessageHandler() {}
     59 
     60  private:
     61   // WebUIMessageHandler implementation.
     62   virtual void RegisterMessages() OVERRIDE;
     63 
     64   void HandleInitUICommand(const base::ListValue* args);
     65   void HandleInspectCommand(const base::ListValue* args);
     66   void HandleActivateCommand(const base::ListValue* args);
     67   void HandleCloseCommand(const base::ListValue* args);
     68   void HandleReloadCommand(const base::ListValue* args);
     69   void HandleOpenCommand(const base::ListValue* args);
     70   void HandleInspectBrowserCommand(const base::ListValue* args);
     71   void HandleBooleanPrefChanged(const char* pref_name,
     72                                 const base::ListValue* args);
     73   void HandlePortForwardingConfigCommand(const base::ListValue* args);
     74 
     75   InspectUI* inspect_ui_;
     76 
     77   DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
     78 };
     79 
     80 void InspectMessageHandler::RegisterMessages() {
     81   web_ui()->RegisterMessageCallback(kInitUICommand,
     82       base::Bind(&InspectMessageHandler::HandleInitUICommand,
     83                  base::Unretained(this)));
     84   web_ui()->RegisterMessageCallback(kInspectCommand,
     85       base::Bind(&InspectMessageHandler::HandleInspectCommand,
     86                  base::Unretained(this)));
     87   web_ui()->RegisterMessageCallback(kActivateCommand,
     88       base::Bind(&InspectMessageHandler::HandleActivateCommand,
     89                  base::Unretained(this)));
     90   web_ui()->RegisterMessageCallback(kCloseCommand,
     91       base::Bind(&InspectMessageHandler::HandleCloseCommand,
     92                  base::Unretained(this)));
     93   web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand,
     94       base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
     95                   base::Unretained(this),
     96                   &prefs::kDevToolsDiscoverUsbDevicesEnabled[0]));
     97   web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
     98       base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
     99                  base::Unretained(this),
    100                  &prefs::kDevToolsPortForwardingEnabled[0]));
    101   web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
    102       base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
    103                  base::Unretained(this)));
    104   web_ui()->RegisterMessageCallback(kReloadCommand,
    105       base::Bind(&InspectMessageHandler::HandleReloadCommand,
    106                  base::Unretained(this)));
    107   web_ui()->RegisterMessageCallback(kOpenCommand,
    108       base::Bind(&InspectMessageHandler::HandleOpenCommand,
    109                  base::Unretained(this)));
    110   web_ui()->RegisterMessageCallback(kInspectBrowser,
    111       base::Bind(&InspectMessageHandler::HandleInspectBrowserCommand,
    112                  base::Unretained(this)));
    113 }
    114 
    115 void InspectMessageHandler::HandleInitUICommand(const base::ListValue*) {
    116   inspect_ui_->InitUI();
    117 }
    118 
    119 static bool ParseStringArgs(const base::ListValue* args,
    120                             std::string* arg0,
    121                             std::string* arg1,
    122                             std::string* arg2 = 0) {
    123   int arg_size = args->GetSize();
    124   return (!arg0 || (arg_size > 0 && args->GetString(0, arg0))) &&
    125          (!arg1 || (arg_size > 1 && args->GetString(1, arg1))) &&
    126          (!arg2 || (arg_size > 2 && args->GetString(2, arg2)));
    127 }
    128 
    129 void InspectMessageHandler::HandleInspectCommand(const base::ListValue* args) {
    130   std::string source;
    131   std::string id;
    132   if (ParseStringArgs(args, &source, &id))
    133     inspect_ui_->Inspect(source, id);
    134 }
    135 
    136 void InspectMessageHandler::HandleActivateCommand(const base::ListValue* args) {
    137   std::string source;
    138   std::string id;
    139   if (ParseStringArgs(args, &source, &id))
    140     inspect_ui_->Activate(source, id);
    141 }
    142 
    143 void InspectMessageHandler::HandleCloseCommand(const base::ListValue* args) {
    144   std::string source;
    145   std::string id;
    146   if (ParseStringArgs(args, &source, &id))
    147     inspect_ui_->Close(source, id);
    148 }
    149 
    150 void InspectMessageHandler::HandleReloadCommand(const base::ListValue* args) {
    151   std::string source;
    152   std::string id;
    153   if (ParseStringArgs(args, &source, &id))
    154     inspect_ui_->Reload(source, id);
    155 }
    156 
    157 void InspectMessageHandler::HandleOpenCommand(const base::ListValue* args) {
    158   std::string source_id;
    159   std::string browser_id;
    160   std::string url;
    161   if (ParseStringArgs(args, &source_id, &browser_id, &url))
    162     inspect_ui_->Open(source_id, browser_id, url);
    163 }
    164 
    165 void InspectMessageHandler::HandleInspectBrowserCommand(
    166     const base::ListValue* args) {
    167   std::string source_id;
    168   std::string browser_id;
    169   std::string front_end;
    170   if (ParseStringArgs(args, &source_id, &browser_id, &front_end)) {
    171     inspect_ui_->InspectBrowserWithCustomFrontend(
    172         source_id, browser_id, GURL(front_end));
    173   }
    174 }
    175 
    176 void InspectMessageHandler::HandleBooleanPrefChanged(
    177     const char* pref_name,
    178     const base::ListValue* args) {
    179   Profile* profile = Profile::FromWebUI(web_ui());
    180   if (!profile)
    181     return;
    182 
    183   bool enabled;
    184   if (args->GetSize() == 1 && args->GetBoolean(0, &enabled))
    185     profile->GetPrefs()->SetBoolean(pref_name, enabled);
    186 }
    187 
    188 void InspectMessageHandler::HandlePortForwardingConfigCommand(
    189     const base::ListValue* args) {
    190   Profile* profile = Profile::FromWebUI(web_ui());
    191   if (!profile)
    192     return;
    193 
    194   const base::DictionaryValue* dict_src;
    195   if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
    196     profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
    197 }
    198 
    199 }  // namespace
    200 
    201 InspectUI::InspectUI(content::WebUI* web_ui)
    202     : WebUIController(web_ui) {
    203   web_ui->AddMessageHandler(new InspectMessageHandler(this));
    204   Profile* profile = Profile::FromWebUI(web_ui);
    205   content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
    206 
    207   // Set up the chrome://theme/ source.
    208   ThemeSource* theme = new ThemeSource(profile);
    209   content::URLDataSource::Add(profile, theme);
    210 }
    211 
    212 InspectUI::~InspectUI() {
    213   StopListeningNotifications();
    214 }
    215 
    216 void InspectUI::InitUI() {
    217   SetPortForwardingDefaults();
    218   StartListeningNotifications();
    219   UpdateDiscoverUsbDevicesEnabled();
    220   UpdatePortForwardingEnabled();
    221   UpdatePortForwardingConfig();
    222 }
    223 
    224 void InspectUI::Inspect(const std::string& source_id,
    225                         const std::string& target_id) {
    226   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
    227   if (target)
    228     target->Inspect(Profile::FromWebUI(web_ui()));
    229 }
    230 
    231 void InspectUI::Activate(const std::string& source_id,
    232                          const std::string& target_id) {
    233   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
    234   if (target)
    235     target->Activate();
    236 }
    237 
    238 void InspectUI::Close(const std::string& source_id,
    239                       const std::string& target_id) {
    240   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
    241   if (target)
    242     target->Close();
    243 }
    244 
    245 void InspectUI::Reload(const std::string& source_id,
    246                        const std::string& target_id) {
    247   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
    248   if (target)
    249     target->Reload();
    250 }
    251 
    252 static void NoOp(DevToolsTargetImpl*) {}
    253 
    254 void InspectUI::Open(const std::string& source_id,
    255                      const std::string& browser_id,
    256                      const std::string& url) {
    257   DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
    258   if (handler)
    259     handler->Open(browser_id, url, base::Bind(&NoOp));
    260 }
    261 
    262 void InspectUI::InspectBrowserWithCustomFrontend(
    263     const std::string& source_id,
    264     const std::string& browser_id,
    265     const GURL& frontend_url) {
    266   if (!frontend_url.SchemeIs(content::kChromeUIScheme) &&
    267       !frontend_url.SchemeIs(content::kChromeDevToolsScheme) &&
    268       frontend_url.host() != kLocalHost) {
    269     return;
    270   }
    271 
    272   DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
    273   if (!handler)
    274     return;
    275 
    276   // Fetch agent host from remote browser.
    277   scoped_refptr<content::DevToolsAgentHost> agent_host =
    278       handler->GetBrowserAgentHost(browser_id);
    279   if (agent_host->IsAttached())
    280     return;
    281 
    282   // Create web contents for the front-end.
    283   WebContents* inspect_ui = web_ui()->GetWebContents();
    284   WebContents* front_end = inspect_ui->GetDelegate()->OpenURLFromTab(
    285       inspect_ui,
    286       content::OpenURLParams(GURL(url::kAboutBlankURL),
    287                              content::Referrer(),
    288                              NEW_FOREGROUND_TAB,
    289                              content::PAGE_TRANSITION_AUTO_TOPLEVEL,
    290                              false));
    291 
    292   // Install devtools bindings.
    293   DevToolsUIBindings* bindings = new DevToolsUIBindings(front_end,
    294                                                         frontend_url);
    295 
    296   // Engage remote debugging between front-end and agent host.
    297   content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
    298       agent_host, bindings->frontend_host());
    299 }
    300 
    301 void InspectUI::InspectDevices(Browser* browser) {
    302   content::RecordAction(base::UserMetricsAction("InspectDevices"));
    303   chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
    304       browser, GURL(chrome::kChromeUIInspectURL)));
    305   params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
    306   ShowSingletonTabOverwritingNTP(browser, params);
    307 }
    308 
    309 void InspectUI::Observe(int type,
    310     const content::NotificationSource& source,
    311     const content::NotificationDetails& details) {
    312   if (source == content::Source<WebContents>(web_ui()->GetWebContents()))
    313     StopListeningNotifications();
    314 }
    315 
    316 void InspectUI::StartListeningNotifications() {
    317   if (!target_handlers_.empty())  // Possible when reloading the page.
    318     StopListeningNotifications();
    319 
    320   Profile* profile = Profile::FromWebUI(web_ui());
    321 
    322   DevToolsTargetsUIHandler::Callback callback =
    323       base::Bind(&InspectUI::PopulateTargets, base::Unretained(this));
    324 
    325   AddTargetUIHandler(
    326       DevToolsTargetsUIHandler::CreateForRenderers(callback));
    327   AddTargetUIHandler(
    328       DevToolsTargetsUIHandler::CreateForWorkers(callback));
    329   if (profile->IsOffTheRecord()) {
    330     ShowIncognitoWarning();
    331   } else {
    332     AddTargetUIHandler(
    333         DevToolsTargetsUIHandler::CreateForAdb(callback, profile));
    334   }
    335 
    336   port_status_serializer_.reset(
    337       new PortForwardingStatusSerializer(
    338           base::Bind(&InspectUI::PopulatePortStatus, base::Unretained(this)),
    339           profile));
    340 
    341   notification_registrar_.Add(this,
    342                               content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
    343                               content::NotificationService::AllSources());
    344 
    345   pref_change_registrar_.Init(profile->GetPrefs());
    346   pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
    347       base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
    348                  base::Unretained(this)));
    349   pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
    350       base::Bind(&InspectUI::UpdatePortForwardingEnabled,
    351                  base::Unretained(this)));
    352   pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
    353       base::Bind(&InspectUI::UpdatePortForwardingConfig,
    354                  base::Unretained(this)));
    355 }
    356 
    357 void InspectUI::StopListeningNotifications() {
    358   if (target_handlers_.empty())
    359     return;
    360 
    361   STLDeleteValues(&target_handlers_);
    362 
    363   port_status_serializer_.reset();
    364 
    365   notification_registrar_.RemoveAll();
    366   pref_change_registrar_.RemoveAll();
    367 }
    368 
    369 content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
    370   content::WebUIDataSource* source =
    371       content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
    372   source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
    373   source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
    374   source->SetDefaultResource(IDR_INSPECT_HTML);
    375   source->OverrideContentSecurityPolicyFrameSrc(
    376       "frame-src chrome://serviceworker-internals;");
    377   serviceworker_webui_.reset(web_ui()->GetWebContents()->CreateWebUI(
    378       GURL(content::kChromeUIServiceWorkerInternalsURL)));
    379   serviceworker_webui_->OverrideJavaScriptFrame(
    380       content::kChromeUIServiceWorkerInternalsHost);
    381   return source;
    382 }
    383 
    384 void InspectUI::RenderViewCreated(content::RenderViewHost* render_view_host) {
    385   serviceworker_webui_->GetController()->RenderViewCreated(render_view_host);
    386 }
    387 
    388 void InspectUI::RenderViewReused(content::RenderViewHost* render_view_host) {
    389   serviceworker_webui_->GetController()->RenderViewReused(render_view_host);
    390 }
    391 
    392 bool InspectUI::OverrideHandleWebUIMessage(const GURL& source_url,
    393                                            const std::string& message,
    394                                            const base::ListValue& args) {
    395   if (source_url.SchemeIs(content::kChromeUIScheme) &&
    396       source_url.host() == content::kChromeUIServiceWorkerInternalsHost) {
    397     serviceworker_webui_->ProcessWebUIMessage(source_url, message, args);
    398     return true;
    399   }
    400   return false;
    401 }
    402 
    403 void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
    404   web_ui()->CallJavascriptFunction(
    405       "updateDiscoverUsbDevicesEnabled",
    406       *GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled));
    407 }
    408 
    409 void InspectUI::UpdatePortForwardingEnabled() {
    410   web_ui()->CallJavascriptFunction(
    411       "updatePortForwardingEnabled",
    412       *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
    413 }
    414 
    415 void InspectUI::UpdatePortForwardingConfig() {
    416   web_ui()->CallJavascriptFunction(
    417       "updatePortForwardingConfig",
    418       *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
    419 }
    420 
    421 void InspectUI::SetPortForwardingDefaults() {
    422   Profile* profile = Profile::FromWebUI(web_ui());
    423   PrefService* prefs = profile->GetPrefs();
    424 
    425   bool default_set;
    426   if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
    427       GetAsBoolean(&default_set) || default_set) {
    428     return;
    429   }
    430 
    431   // This is the first chrome://inspect invocation on a fresh profile or after
    432   // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
    433   prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
    434 
    435   bool enabled;
    436   const base::DictionaryValue* config;
    437   if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
    438         GetAsBoolean(&enabled) ||
    439       !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
    440         GetAsDictionary(&config)) {
    441     return;
    442   }
    443 
    444   // Do nothing if user already took explicit action.
    445   if (enabled || config->size() != 0)
    446     return;
    447 
    448   base::DictionaryValue default_config;
    449   default_config.SetString(
    450       kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
    451   prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
    452 }
    453 
    454 const base::Value* InspectUI::GetPrefValue(const char* name) {
    455   Profile* profile = Profile::FromWebUI(web_ui());
    456   return profile->GetPrefs()->FindPreference(name)->GetValue();
    457 }
    458 
    459 void InspectUI::AddTargetUIHandler(
    460     scoped_ptr<DevToolsTargetsUIHandler> handler) {
    461   DevToolsTargetsUIHandler* handler_ptr = handler.release();
    462   target_handlers_[handler_ptr->source_id()] = handler_ptr;
    463 }
    464 
    465 DevToolsTargetsUIHandler* InspectUI::FindTargetHandler(
    466     const std::string& source_id) {
    467   TargetHandlerMap::iterator it = target_handlers_.find(source_id);
    468      return it != target_handlers_.end() ? it->second : NULL;
    469 }
    470 
    471 DevToolsTargetImpl* InspectUI::FindTarget(
    472     const std::string& source_id, const std::string& target_id) {
    473   TargetHandlerMap::iterator it = target_handlers_.find(source_id);
    474   return it != target_handlers_.end() ?
    475          it->second->GetTarget(target_id) : NULL;
    476 }
    477 
    478 void InspectUI::PopulateTargets(const std::string& source,
    479                                 scoped_ptr<base::ListValue> targets) {
    480   scoped_ptr<base::Value> source_value(base::Value::CreateStringValue(source));
    481   web_ui()->CallJavascriptFunction(
    482       "populateTargets",
    483       *source_value.get(),
    484       *targets.get());
    485 }
    486 
    487 void InspectUI::PopulatePortStatus(const base::Value& status) {
    488   web_ui()->CallJavascriptFunction("populatePortStatus", status);
    489 }
    490 
    491 void InspectUI::ShowIncognitoWarning() {
    492   web_ui()->CallJavascriptFunction("showIncognitoWarning");
    493 }
    494