Home | History | Annotate | Download | only in devtools
      1 // Copyright 2014 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_ui_bindings.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/json/json_reader.h"
      9 #include "base/json/json_writer.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/chrome_notification_types.h"
     16 #include "chrome/browser/chrome_page_zoom.h"
     17 #include "chrome/browser/devtools/devtools_target_impl.h"
     18 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
     19 #include "chrome/browser/extensions/extension_service.h"
     20 #include "chrome/browser/infobars/infobar_service.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/themes/theme_properties.h"
     23 #include "chrome/browser/themes/theme_service.h"
     24 #include "chrome/browser/themes/theme_service_factory.h"
     25 #include "chrome/browser/ui/browser.h"
     26 #include "chrome/browser/ui/browser_iterator.h"
     27 #include "chrome/browser/ui/browser_list.h"
     28 #include "chrome/browser/ui/browser_window.h"
     29 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     30 #include "chrome/common/chrome_switches.h"
     31 #include "chrome/common/extensions/manifest_url_handler.h"
     32 #include "chrome/common/url_constants.h"
     33 #include "chrome/grit/generated_resources.h"
     34 #include "components/infobars/core/confirm_infobar_delegate.h"
     35 #include "components/infobars/core/infobar.h"
     36 #include "content/public/browser/favicon_status.h"
     37 #include "content/public/browser/invalidate_type.h"
     38 #include "content/public/browser/navigation_controller.h"
     39 #include "content/public/browser/navigation_entry.h"
     40 #include "content/public/browser/notification_source.h"
     41 #include "content/public/browser/render_frame_host.h"
     42 #include "content/public/browser/render_view_host.h"
     43 #include "content/public/browser/user_metrics.h"
     44 #include "content/public/browser/web_contents.h"
     45 #include "content/public/browser/web_contents_observer.h"
     46 #include "content/public/common/renderer_preferences.h"
     47 #include "content/public/common/url_constants.h"
     48 #include "extensions/browser/extension_system.h"
     49 #include "extensions/common/extension_set.h"
     50 #include "extensions/common/permissions/permissions_data.h"
     51 #include "ui/base/l10n/l10n_util.h"
     52 #include "ui/base/page_transition_types.h"
     53 
     54 using base::DictionaryValue;
     55 using content::BrowserThread;
     56 
     57 namespace {
     58 
     59 static const char kFrontendHostId[] = "id";
     60 static const char kFrontendHostMethod[] = "method";
     61 static const char kFrontendHostParams[] = "params";
     62 static const char kTitleFormat[] = "Developer Tools - %s";
     63 
     64 typedef std::vector<DevToolsUIBindings*> DevToolsUIBindingsList;
     65 base::LazyInstance<DevToolsUIBindingsList>::Leaky g_instances =
     66     LAZY_INSTANCE_INITIALIZER;
     67 
     68 std::string SkColorToRGBAString(SkColor color) {
     69   // We avoid StringPrintf because it will use locale specific formatters for
     70   // the double (e.g. ',' instead of '.' in German).
     71   return "rgba(" + base::IntToString(SkColorGetR(color)) + "," +
     72       base::IntToString(SkColorGetG(color)) + "," +
     73       base::IntToString(SkColorGetB(color)) + "," +
     74       base::DoubleToString(SkColorGetA(color) / 255.0) + ")";
     75 }
     76 
     77 base::DictionaryValue* CreateFileSystemValue(
     78     DevToolsFileHelper::FileSystem file_system) {
     79   base::DictionaryValue* file_system_value = new base::DictionaryValue();
     80   file_system_value->SetString("fileSystemName", file_system.file_system_name);
     81   file_system_value->SetString("rootURL", file_system.root_url);
     82   file_system_value->SetString("fileSystemPath", file_system.file_system_path);
     83   return file_system_value;
     84 }
     85 
     86 Browser* FindBrowser(content::WebContents* web_contents) {
     87   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
     88     int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
     89         web_contents);
     90     if (tab_index != TabStripModel::kNoTab)
     91       return *it;
     92   }
     93   return NULL;
     94 }
     95 
     96 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
     97 
     98 typedef base::Callback<void(bool)> InfoBarCallback;
     99 
    100 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
    101  public:
    102   // If |infobar_service| is NULL, runs |callback| with a single argument with
    103   // value "false".  Otherwise, creates a dev tools confirm infobar and delegate
    104   // and adds the infobar to |infobar_service|.
    105   static void Create(InfoBarService* infobar_service,
    106                      const InfoBarCallback& callback,
    107                      const base::string16& message);
    108 
    109  private:
    110   DevToolsConfirmInfoBarDelegate(
    111       const InfoBarCallback& callback,
    112       const base::string16& message);
    113   virtual ~DevToolsConfirmInfoBarDelegate();
    114 
    115   virtual base::string16 GetMessageText() const OVERRIDE;
    116   virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
    117   virtual bool Accept() OVERRIDE;
    118   virtual bool Cancel() OVERRIDE;
    119 
    120   InfoBarCallback callback_;
    121   const base::string16 message_;
    122 
    123   DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
    124 };
    125 
    126 void DevToolsConfirmInfoBarDelegate::Create(
    127     InfoBarService* infobar_service,
    128     const InfoBarCallback& callback,
    129     const base::string16& message) {
    130   if (!infobar_service) {
    131     callback.Run(false);
    132     return;
    133   }
    134 
    135   infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
    136       scoped_ptr<ConfirmInfoBarDelegate>(
    137           new DevToolsConfirmInfoBarDelegate(callback, message))));
    138 }
    139 
    140 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
    141     const InfoBarCallback& callback,
    142     const base::string16& message)
    143     : ConfirmInfoBarDelegate(),
    144       callback_(callback),
    145       message_(message) {
    146 }
    147 
    148 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
    149   if (!callback_.is_null())
    150     callback_.Run(false);
    151 }
    152 
    153 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
    154   return message_;
    155 }
    156 
    157 base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
    158     InfoBarButton button) const {
    159   return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
    160       IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
    161 }
    162 
    163 bool DevToolsConfirmInfoBarDelegate::Accept() {
    164   callback_.Run(true);
    165   callback_.Reset();
    166   return true;
    167 }
    168 
    169 bool DevToolsConfirmInfoBarDelegate::Cancel() {
    170   callback_.Run(false);
    171   callback_.Reset();
    172   return true;
    173 }
    174 
    175 // DevToolsUIDefaultDelegate --------------------------------------------------
    176 
    177 class DefaultBindingsDelegate : public DevToolsUIBindings::Delegate {
    178  public:
    179   explicit DefaultBindingsDelegate(content::WebContents* web_contents)
    180       : web_contents_(web_contents) {}
    181 
    182  private:
    183   virtual ~DefaultBindingsDelegate() {}
    184 
    185   virtual void ActivateWindow() OVERRIDE;
    186   virtual void CloseWindow() OVERRIDE {}
    187   virtual void SetInspectedPageBounds(const gfx::Rect& rect) OVERRIDE {}
    188   virtual void InspectElementCompleted() OVERRIDE {}
    189   virtual void MoveWindow(int x, int y) OVERRIDE {}
    190   virtual void SetIsDocked(bool is_docked) OVERRIDE {}
    191   virtual void OpenInNewTab(const std::string& url) OVERRIDE;
    192   virtual void SetWhitelistedShortcuts(const std::string& message) OVERRIDE {}
    193 
    194   virtual void InspectedContentsClosing() OVERRIDE;
    195   virtual void OnLoadCompleted() OVERRIDE {}
    196   virtual InfoBarService* GetInfoBarService() OVERRIDE;
    197   virtual void RenderProcessGone() OVERRIDE {}
    198 
    199   content::WebContents* web_contents_;
    200   DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate);
    201 };
    202 
    203 void DefaultBindingsDelegate::ActivateWindow() {
    204   web_contents_->GetDelegate()->ActivateContents(web_contents_);
    205   web_contents_->Focus();
    206 }
    207 
    208 void DefaultBindingsDelegate::OpenInNewTab(const std::string& url) {
    209   content::OpenURLParams params(
    210       GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
    211       ui::PAGE_TRANSITION_LINK, false);
    212   Browser* browser = FindBrowser(web_contents_);
    213   browser->OpenURL(params);
    214 }
    215 
    216 void DefaultBindingsDelegate::InspectedContentsClosing() {
    217   web_contents_->GetRenderViewHost()->ClosePage();
    218 }
    219 
    220 InfoBarService* DefaultBindingsDelegate::GetInfoBarService() {
    221   return InfoBarService::FromWebContents(web_contents_);
    222 }
    223 
    224 }  // namespace
    225 
    226 // DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
    227 
    228 class DevToolsUIBindings::FrontendWebContentsObserver
    229     : public content::WebContentsObserver {
    230  public:
    231   explicit FrontendWebContentsObserver(DevToolsUIBindings* ui_bindings);
    232   virtual ~FrontendWebContentsObserver();
    233 
    234  private:
    235   // contents::WebContentsObserver:
    236   virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
    237   virtual void AboutToNavigateRenderView(
    238       content::RenderViewHost* render_view_host) OVERRIDE;
    239   virtual void DocumentOnLoadCompletedInMainFrame() OVERRIDE;
    240 
    241   DevToolsUIBindings* devtools_bindings_;
    242   DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
    243 };
    244 
    245 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
    246     DevToolsUIBindings* devtools_ui_bindings)
    247     : WebContentsObserver(devtools_ui_bindings->web_contents()),
    248       devtools_bindings_(devtools_ui_bindings) {
    249 }
    250 
    251 DevToolsUIBindings::FrontendWebContentsObserver::
    252     ~FrontendWebContentsObserver() {
    253 }
    254 
    255 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
    256     base::TerminationStatus status) {
    257   switch (status) {
    258     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
    259     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
    260     case base::TERMINATION_STATUS_PROCESS_CRASHED:
    261       if (devtools_bindings_->agent_host_.get())
    262         devtools_bindings_->Detach();
    263       break;
    264     default:
    265       break;
    266   }
    267   devtools_bindings_->delegate_->RenderProcessGone();
    268 }
    269 
    270 void DevToolsUIBindings::FrontendWebContentsObserver::AboutToNavigateRenderView(
    271     content::RenderViewHost* render_view_host) {
    272   devtools_bindings_->frontend_host_.reset(
    273       content::DevToolsFrontendHost::Create(
    274           render_view_host, devtools_bindings_));
    275 }
    276 
    277 void DevToolsUIBindings::FrontendWebContentsObserver::
    278     DocumentOnLoadCompletedInMainFrame() {
    279   devtools_bindings_->DocumentOnLoadCompletedInMainFrame();
    280 }
    281 
    282 // DevToolsUIBindings ---------------------------------------------------------
    283 
    284 DevToolsUIBindings* DevToolsUIBindings::ForWebContents(
    285      content::WebContents* web_contents) {
    286  if (g_instances == NULL)
    287    return NULL;
    288  DevToolsUIBindingsList* instances = g_instances.Pointer();
    289  for (DevToolsUIBindingsList::iterator it(instances->begin());
    290       it != instances->end(); ++it) {
    291    if ((*it)->web_contents() == web_contents)
    292      return *it;
    293  }
    294  return NULL;
    295 }
    296 
    297 // static
    298 GURL DevToolsUIBindings::ApplyThemeToURL(Profile* profile,
    299                                          const GURL& base_url) {
    300   std::string frontend_url = base_url.spec();
    301   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile);
    302   DCHECK(tp);
    303   std::string url_string(
    304       frontend_url +
    305       ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
    306       "dockSide=undocked" + // TODO(dgozman): remove this support in M38.
    307       "&toolbarColor=" +
    308       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
    309       "&textColor=" +
    310       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)));
    311   if (CommandLine::ForCurrentProcess()->HasSwitch(
    312       switches::kEnableDevToolsExperiments))
    313     url_string += "&experiments=true";
    314 #if defined(DEBUG_DEVTOOLS)
    315   url_string += "&debugFrontend=true";
    316 #endif  // defined(DEBUG_DEVTOOLS)
    317   return GURL(url_string);
    318 }
    319 
    320 DevToolsUIBindings::DevToolsUIBindings(content::WebContents* web_contents)
    321     : profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
    322       web_contents_(web_contents),
    323       delegate_(new DefaultBindingsDelegate(web_contents_)),
    324       device_count_updates_enabled_(false),
    325       devices_updates_enabled_(false),
    326       weak_factory_(this) {
    327   g_instances.Get().push_back(this);
    328   frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
    329   web_contents_->GetMutableRendererPrefs()->can_accept_load_drops = false;
    330 
    331   file_helper_.reset(new DevToolsFileHelper(web_contents_, profile_));
    332   file_system_indexer_ = new DevToolsFileSystemIndexer();
    333   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
    334       web_contents_);
    335 
    336   // Wipe out page icon so that the default application icon is used.
    337   content::NavigationEntry* entry =
    338       web_contents_->GetController().GetActiveEntry();
    339   entry->GetFavicon().image = gfx::Image();
    340   entry->GetFavicon().valid = true;
    341 
    342   // Register on-load actions.
    343   registrar_.Add(
    344       this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
    345       content::Source<ThemeService>(
    346           ThemeServiceFactory::GetForProfile(profile_)));
    347 
    348   embedder_message_dispatcher_.reset(
    349       DevToolsEmbedderMessageDispatcher::createForDevToolsFrontend(this));
    350 
    351   frontend_host_.reset(
    352       content::DevToolsFrontendHost::Create(
    353           web_contents_->GetRenderViewHost(), this));
    354 }
    355 
    356 DevToolsUIBindings::~DevToolsUIBindings() {
    357   if (agent_host_.get())
    358     agent_host_->DetachClient();
    359 
    360   for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
    361        jobs_it != indexing_jobs_.end(); ++jobs_it) {
    362     jobs_it->second->Stop();
    363   }
    364   indexing_jobs_.clear();
    365   SetDeviceCountUpdatesEnabled(false);
    366   SetDevicesUpdatesEnabled(false);
    367 
    368   // Remove self from global list.
    369   DevToolsUIBindingsList* instances = g_instances.Pointer();
    370   DevToolsUIBindingsList::iterator it(
    371       std::find(instances->begin(), instances->end(), this));
    372   DCHECK(it != instances->end());
    373   instances->erase(it);
    374 }
    375 
    376 // content::NotificationObserver overrides ------------------------------------
    377 void DevToolsUIBindings::Observe(int type,
    378                                  const content::NotificationSource& source,
    379                                  const content::NotificationDetails& details) {
    380   DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
    381   UpdateTheme();
    382 }
    383 
    384 // content::DevToolsFrontendHost::Delegate implementation ---------------------
    385 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
    386     const std::string& message) {
    387   std::string method;
    388   base::ListValue empty_params;
    389   base::ListValue* params = &empty_params;
    390 
    391   base::DictionaryValue* dict = NULL;
    392   scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
    393   if (!parsed_message ||
    394       !parsed_message->GetAsDictionary(&dict) ||
    395       !dict->GetString(kFrontendHostMethod, &method) ||
    396       (dict->HasKey(kFrontendHostParams) &&
    397           !dict->GetList(kFrontendHostParams, &params))) {
    398     LOG(ERROR) << "Invalid message was sent to embedder: " << message;
    399     return;
    400   }
    401 
    402   int id = 0;
    403   dict->GetInteger(kFrontendHostId, &id);
    404 
    405   std::string error;
    406   embedder_message_dispatcher_->Dispatch(method, params, &error);
    407   if (id) {
    408     base::FundamentalValue id_value(id);
    409     base::StringValue error_value(error);
    410     CallClientFunction("InspectorFrontendAPI.embedderMessageAck",
    411                        &id_value, &error_value, NULL);
    412   }
    413 }
    414 
    415 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
    416     const std::string& message) {
    417   if (agent_host_.get())
    418     agent_host_->DispatchProtocolMessage(message);
    419 }
    420 
    421 // content::DevToolsAgentHostClient implementation --------------------------
    422 void DevToolsUIBindings::DispatchProtocolMessage(
    423     content::DevToolsAgentHost* agent_host, const std::string& message) {
    424   DCHECK(agent_host == agent_host_.get());
    425   base::StringValue message_value(message);
    426   CallClientFunction("InspectorFrontendAPI.dispatchMessage",
    427                      &message_value, NULL, NULL);
    428 }
    429 
    430 void DevToolsUIBindings::AgentHostClosed(
    431     content::DevToolsAgentHost* agent_host,
    432     bool replaced_with_another_client) {
    433   DCHECK(agent_host == agent_host_.get());
    434   agent_host_ = NULL;
    435   delegate_->InspectedContentsClosing();
    436 }
    437 
    438 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
    439 void DevToolsUIBindings::ActivateWindow() {
    440   delegate_->ActivateWindow();
    441 }
    442 
    443 void DevToolsUIBindings::CloseWindow() {
    444   delegate_->CloseWindow();
    445 }
    446 
    447 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect& rect) {
    448   delegate_->SetInspectedPageBounds(rect);
    449 }
    450 
    451 void DevToolsUIBindings::MoveWindow(int x, int y) {
    452   delegate_->MoveWindow(x, y);
    453 }
    454 
    455 void DevToolsUIBindings::SetIsDocked(bool dock_requested) {
    456   delegate_->SetIsDocked(dock_requested);
    457 }
    458 
    459 void DevToolsUIBindings::InspectElementCompleted() {
    460   delegate_->InspectElementCompleted();
    461 }
    462 
    463 void DevToolsUIBindings::InspectedURLChanged(const std::string& url) {
    464   content::NavigationController& controller = web_contents()->GetController();
    465   content::NavigationEntry* entry = controller.GetActiveEntry();
    466   // DevTools UI is not localized.
    467   entry->SetTitle(
    468       base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str())));
    469   web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
    470 }
    471 
    472 void DevToolsUIBindings::OpenInNewTab(const std::string& url) {
    473   delegate_->OpenInNewTab(url);
    474 }
    475 
    476 void DevToolsUIBindings::SaveToFile(const std::string& url,
    477                                     const std::string& content,
    478                                     bool save_as) {
    479   file_helper_->Save(url, content, save_as,
    480                      base::Bind(&DevToolsUIBindings::FileSavedAs,
    481                                 weak_factory_.GetWeakPtr(), url),
    482                      base::Bind(&DevToolsUIBindings::CanceledFileSaveAs,
    483                                 weak_factory_.GetWeakPtr(), url));
    484 }
    485 
    486 void DevToolsUIBindings::AppendToFile(const std::string& url,
    487                                       const std::string& content) {
    488   file_helper_->Append(url, content,
    489                        base::Bind(&DevToolsUIBindings::AppendedTo,
    490                                   weak_factory_.GetWeakPtr(), url));
    491 }
    492 
    493 void DevToolsUIBindings::RequestFileSystems() {
    494   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
    495   file_helper_->RequestFileSystems(base::Bind(
    496       &DevToolsUIBindings::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
    497 }
    498 
    499 void DevToolsUIBindings::AddFileSystem() {
    500   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
    501   file_helper_->AddFileSystem(
    502       base::Bind(&DevToolsUIBindings::FileSystemAdded,
    503                  weak_factory_.GetWeakPtr()),
    504       base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
    505                  weak_factory_.GetWeakPtr()));
    506 }
    507 
    508 void DevToolsUIBindings::RemoveFileSystem(
    509     const std::string& file_system_path) {
    510   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
    511   file_helper_->RemoveFileSystem(file_system_path);
    512   base::StringValue file_system_path_value(file_system_path);
    513   CallClientFunction("InspectorFrontendAPI.fileSystemRemoved",
    514                      &file_system_path_value, NULL, NULL);
    515 }
    516 
    517 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
    518     const std::string& file_system_url) {
    519   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
    520   file_helper_->UpgradeDraggedFileSystemPermissions(
    521       file_system_url,
    522       base::Bind(&DevToolsUIBindings::FileSystemAdded,
    523                  weak_factory_.GetWeakPtr()),
    524       base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
    525                  weak_factory_.GetWeakPtr()));
    526 }
    527 
    528 void DevToolsUIBindings::IndexPath(int request_id,
    529                                    const std::string& file_system_path) {
    530   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    531   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
    532   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
    533     IndexingDone(request_id, file_system_path);
    534     return;
    535   }
    536   indexing_jobs_[request_id] =
    537       scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
    538           file_system_indexer_->IndexPath(
    539               file_system_path,
    540               Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated,
    541                    weak_factory_.GetWeakPtr(),
    542                    request_id,
    543                    file_system_path),
    544               Bind(&DevToolsUIBindings::IndexingWorked,
    545                    weak_factory_.GetWeakPtr(),
    546                    request_id,
    547                    file_system_path),
    548               Bind(&DevToolsUIBindings::IndexingDone,
    549                    weak_factory_.GetWeakPtr(),
    550                    request_id,
    551                    file_system_path)));
    552 }
    553 
    554 void DevToolsUIBindings::StopIndexing(int request_id) {
    555   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    556   IndexingJobsMap::iterator it = indexing_jobs_.find(request_id);
    557   if (it == indexing_jobs_.end())
    558     return;
    559   it->second->Stop();
    560   indexing_jobs_.erase(it);
    561 }
    562 
    563 void DevToolsUIBindings::SearchInPath(int request_id,
    564                                       const std::string& file_system_path,
    565                                       const std::string& query) {
    566   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    567   CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
    568   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
    569     SearchCompleted(request_id, file_system_path, std::vector<std::string>());
    570     return;
    571   }
    572   file_system_indexer_->SearchInPath(file_system_path,
    573                                      query,
    574                                      Bind(&DevToolsUIBindings::SearchCompleted,
    575                                           weak_factory_.GetWeakPtr(),
    576                                           request_id,
    577                                           file_system_path));
    578 }
    579 
    580 void DevToolsUIBindings::SetWhitelistedShortcuts(
    581     const std::string& message) {
    582   delegate_->SetWhitelistedShortcuts(message);
    583 }
    584 
    585 void DevToolsUIBindings::ZoomIn() {
    586   chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_IN);
    587 }
    588 
    589 void DevToolsUIBindings::ZoomOut() {
    590   chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT);
    591 }
    592 
    593 void DevToolsUIBindings::ResetZoom() {
    594   chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET);
    595 }
    596 
    597 static void InspectTarget(Profile* profile, DevToolsTargetImpl* target) {
    598   if (target)
    599     target->Inspect(profile);
    600 }
    601 
    602 void DevToolsUIBindings::OpenUrlOnRemoteDeviceAndInspect(
    603     const std::string& browser_id,
    604     const std::string& url) {
    605   if (remote_targets_handler_) {
    606     remote_targets_handler_->Open(browser_id, url,
    607         base::Bind(&InspectTarget, profile_));
    608   }
    609 }
    610 
    611 void DevToolsUIBindings::SetDeviceCountUpdatesEnabled(bool enabled) {
    612   if (device_count_updates_enabled_ == enabled)
    613     return;
    614   DevToolsAndroidBridge* adb_bridge =
    615       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
    616   if (!adb_bridge)
    617     return;
    618 
    619   device_count_updates_enabled_ = enabled;
    620   if (enabled)
    621     adb_bridge->AddDeviceCountListener(this);
    622   else
    623     adb_bridge->RemoveDeviceCountListener(this);
    624 }
    625 
    626 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled) {
    627   if (devices_updates_enabled_ == enabled)
    628     return;
    629   devices_updates_enabled_ = enabled;
    630   if (enabled) {
    631     remote_targets_handler_ = DevToolsTargetsUIHandler::CreateForAdb(
    632         base::Bind(&DevToolsUIBindings::DevicesUpdated,
    633                    base::Unretained(this)),
    634         profile_);
    635   } else {
    636     remote_targets_handler_.reset();
    637   }
    638 }
    639 
    640 void DevToolsUIBindings::SendMessageToBrowser(const std::string& message) {
    641   if (agent_host_.get())
    642     agent_host_->DispatchProtocolMessage(message);
    643 }
    644 
    645 void DevToolsUIBindings::DeviceCountChanged(int count) {
    646   base::FundamentalValue value(count);
    647   CallClientFunction("InspectorFrontendAPI.deviceCountUpdated", &value, NULL,
    648                      NULL);
    649 }
    650 
    651 void DevToolsUIBindings::DevicesUpdated(
    652     const std::string& source,
    653     const base::ListValue& targets) {
    654   CallClientFunction("InspectorFrontendAPI.devicesUpdated", &targets, NULL,
    655                      NULL);
    656 }
    657 
    658 void DevToolsUIBindings::FileSavedAs(const std::string& url) {
    659   base::StringValue url_value(url);
    660   CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL);
    661 }
    662 
    663 void DevToolsUIBindings::CanceledFileSaveAs(const std::string& url) {
    664   base::StringValue url_value(url);
    665   CallClientFunction("InspectorFrontendAPI.canceledSaveURL",
    666                      &url_value, NULL, NULL);
    667 }
    668 
    669 void DevToolsUIBindings::AppendedTo(const std::string& url) {
    670   base::StringValue url_value(url);
    671   CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL,
    672                      NULL);
    673 }
    674 
    675 void DevToolsUIBindings::FileSystemsLoaded(
    676     const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
    677   base::ListValue file_systems_value;
    678   for (size_t i = 0; i < file_systems.size(); ++i)
    679     file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
    680   CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded",
    681                      &file_systems_value, NULL, NULL);
    682 }
    683 
    684 void DevToolsUIBindings::FileSystemAdded(
    685     const DevToolsFileHelper::FileSystem& file_system) {
    686   scoped_ptr<base::StringValue> error_string_value(
    687       new base::StringValue(std::string()));
    688   scoped_ptr<base::DictionaryValue> file_system_value;
    689   if (!file_system.file_system_path.empty())
    690     file_system_value.reset(CreateFileSystemValue(file_system));
    691   CallClientFunction("InspectorFrontendAPI.fileSystemAdded",
    692                      error_string_value.get(), file_system_value.get(), NULL);
    693 }
    694 
    695 void DevToolsUIBindings::IndexingTotalWorkCalculated(
    696     int request_id,
    697     const std::string& file_system_path,
    698     int total_work) {
    699   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    700   base::FundamentalValue request_id_value(request_id);
    701   base::StringValue file_system_path_value(file_system_path);
    702   base::FundamentalValue total_work_value(total_work);
    703   CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated",
    704                      &request_id_value, &file_system_path_value,
    705                      &total_work_value);
    706 }
    707 
    708 void DevToolsUIBindings::IndexingWorked(int request_id,
    709                                         const std::string& file_system_path,
    710                                         int worked) {
    711   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    712   base::FundamentalValue request_id_value(request_id);
    713   base::StringValue file_system_path_value(file_system_path);
    714   base::FundamentalValue worked_value(worked);
    715   CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value,
    716                      &file_system_path_value, &worked_value);
    717 }
    718 
    719 void DevToolsUIBindings::IndexingDone(int request_id,
    720                                       const std::string& file_system_path) {
    721   indexing_jobs_.erase(request_id);
    722   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    723   base::FundamentalValue request_id_value(request_id);
    724   base::StringValue file_system_path_value(file_system_path);
    725   CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value,
    726                      &file_system_path_value, NULL);
    727 }
    728 
    729 void DevToolsUIBindings::SearchCompleted(
    730     int request_id,
    731     const std::string& file_system_path,
    732     const std::vector<std::string>& file_paths) {
    733   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    734   base::ListValue file_paths_value;
    735   for (std::vector<std::string>::const_iterator it(file_paths.begin());
    736        it != file_paths.end(); ++it) {
    737     file_paths_value.AppendString(*it);
    738   }
    739   base::FundamentalValue request_id_value(request_id);
    740   base::StringValue file_system_path_value(file_system_path);
    741   CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value,
    742                      &file_system_path_value, &file_paths_value);
    743 }
    744 
    745 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
    746     const base::string16& message,
    747     const InfoBarCallback& callback) {
    748   DevToolsConfirmInfoBarDelegate::Create(delegate_->GetInfoBarService(),
    749       callback, message);
    750 }
    751 
    752 void DevToolsUIBindings::UpdateTheme() {
    753   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
    754   DCHECK(tp);
    755 
    756   std::string command("InspectorFrontendAPI.setToolbarColors(\"" +
    757       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
    758       "\", \"" +
    759       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) +
    760       "\")");
    761   web_contents_->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(command));
    762 }
    763 
    764 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
    765   const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
    766       profile_->GetOriginalProfile())->extension_service();
    767   if (!extension_service)
    768     return;
    769   const extensions::ExtensionSet* extensions = extension_service->extensions();
    770 
    771   base::ListValue results;
    772   for (extensions::ExtensionSet::const_iterator extension(extensions->begin());
    773        extension != extensions->end(); ++extension) {
    774     if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
    775       continue;
    776     base::DictionaryValue* extension_info = new base::DictionaryValue();
    777     extension_info->Set(
    778         "startPage",
    779         new base::StringValue(
    780             extensions::ManifestURL::GetDevToolsPage(
    781                 extension->get()).spec()));
    782     extension_info->Set("name", new base::StringValue((*extension)->name()));
    783     extension_info->Set("exposeExperimentalAPIs",
    784                         new base::FundamentalValue(
    785                             (*extension)->permissions_data()->HasAPIPermission(
    786                                 extensions::APIPermission::kExperimental)));
    787     results.Append(extension_info);
    788   }
    789   CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL);
    790 }
    791 
    792 void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
    793   delegate_.reset(delegate);
    794 }
    795 
    796 void DevToolsUIBindings::AttachTo(
    797     const scoped_refptr<content::DevToolsAgentHost>& agent_host) {
    798   if (agent_host_.get())
    799     Detach();
    800   agent_host_ = agent_host;
    801   agent_host_->AttachClient(this);
    802 }
    803 
    804 void DevToolsUIBindings::Reattach() {
    805   DCHECK(agent_host_.get());
    806   agent_host_->DetachClient();
    807   agent_host_->AttachClient(this);
    808 }
    809 
    810 void DevToolsUIBindings::Detach() {
    811   if (agent_host_.get())
    812     agent_host_->DetachClient();
    813   agent_host_ = NULL;
    814 }
    815 
    816 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost* agent_host) {
    817   return agent_host_.get() == agent_host;
    818 }
    819 
    820 void DevToolsUIBindings::CallClientFunction(const std::string& function_name,
    821                                             const base::Value* arg1,
    822                                             const base::Value* arg2,
    823                                             const base::Value* arg3) {
    824   std::string params;
    825   if (arg1) {
    826     std::string json;
    827     base::JSONWriter::Write(arg1, &json);
    828     params.append(json);
    829     if (arg2) {
    830       base::JSONWriter::Write(arg2, &json);
    831       params.append(", " + json);
    832       if (arg3) {
    833         base::JSONWriter::Write(arg3, &json);
    834         params.append(", " + json);
    835       }
    836     }
    837   }
    838 
    839   base::string16 javascript = base::UTF8ToUTF16(
    840       function_name + "(" + params + ");");
    841   web_contents_->GetMainFrame()->ExecuteJavaScript(javascript);
    842 }
    843 
    844 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
    845   // Call delegate first - it seeds importants bit of information.
    846   delegate_->OnLoadCompleted();
    847 
    848   UpdateTheme();
    849   AddDevToolsExtensionsToClient();
    850 }
    851