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