Home | History | Annotate | Download | only in devtools
      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/devtools/devtools_window.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/command_line.h"
     10 #include "base/json/json_writer.h"
     11 #include "base/lazy_instance.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/browser_process.h"
     16 #include "chrome/browser/chrome_notification_types.h"
     17 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
     18 #include "chrome/browser/extensions/extension_service.h"
     19 #include "chrome/browser/extensions/extension_system.h"
     20 #include "chrome/browser/file_select_helper.h"
     21 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
     22 #include "chrome/browser/prefs/pref_service_syncable.h"
     23 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/sessions/session_tab_helper.h"
     26 #include "chrome/browser/themes/theme_properties.h"
     27 #include "chrome/browser/themes/theme_service.h"
     28 #include "chrome/browser/themes/theme_service_factory.h"
     29 #include "chrome/browser/ui/browser.h"
     30 #include "chrome/browser/ui/browser_dialogs.h"
     31 #include "chrome/browser/ui/browser_iterator.h"
     32 #include "chrome/browser/ui/browser_list.h"
     33 #include "chrome/browser/ui/browser_window.h"
     34 #include "chrome/browser/ui/host_desktop.h"
     35 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
     36 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     37 #include "chrome/browser/ui/webui/devtools_ui.h"
     38 #include "chrome/common/chrome_switches.h"
     39 #include "chrome/common/extensions/manifest_url_handler.h"
     40 #include "chrome/common/pref_names.h"
     41 #include "chrome/common/render_messages.h"
     42 #include "chrome/common/url_constants.h"
     43 #include "components/user_prefs/pref_registry_syncable.h"
     44 #include "content/public/browser/browser_thread.h"
     45 #include "content/public/browser/child_process_security_policy.h"
     46 #include "content/public/browser/devtools_agent_host.h"
     47 #include "content/public/browser/devtools_client_host.h"
     48 #include "content/public/browser/devtools_manager.h"
     49 #include "content/public/browser/favicon_status.h"
     50 #include "content/public/browser/load_notification_details.h"
     51 #include "content/public/browser/navigation_controller.h"
     52 #include "content/public/browser/navigation_entry.h"
     53 #include "content/public/browser/notification_source.h"
     54 #include "content/public/browser/render_process_host.h"
     55 #include "content/public/browser/render_view_host.h"
     56 #include "content/public/browser/web_contents.h"
     57 #include "content/public/browser/web_contents_observer.h"
     58 #include "content/public/browser/web_contents_view.h"
     59 #include "content/public/common/bindings_policy.h"
     60 #include "content/public/common/content_client.h"
     61 #include "content/public/common/page_transition_types.h"
     62 #include "content/public/common/url_constants.h"
     63 #include "grit/generated_resources.h"
     64 #include "ui/base/l10n/l10n_util.h"
     65 
     66 using content::DevToolsAgentHost;
     67 
     68 
     69 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
     70 
     71 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
     72  public:
     73   // If |infobar_service| is NULL, runs |callback| with a single argument with
     74   // value "false".  Otherwise, creates a dev tools confirm infobar and delegate
     75   // and adds the inofbar to |infobar_service|.
     76   static void Create(InfoBarService* infobar_service,
     77                      const DevToolsWindow::InfoBarCallback& callback,
     78                      const string16& message);
     79 
     80  private:
     81   DevToolsConfirmInfoBarDelegate(
     82       InfoBarService* infobar_service,
     83       const DevToolsWindow::InfoBarCallback& callback,
     84       const string16& message);
     85   virtual ~DevToolsConfirmInfoBarDelegate();
     86 
     87   virtual string16 GetMessageText() const OVERRIDE;
     88   virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
     89   virtual bool Accept() OVERRIDE;
     90   virtual bool Cancel() OVERRIDE;
     91 
     92   DevToolsWindow::InfoBarCallback callback_;
     93   const string16 message_;
     94 
     95   DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
     96 };
     97 
     98 void DevToolsConfirmInfoBarDelegate::Create(
     99     InfoBarService* infobar_service,
    100     const DevToolsWindow::InfoBarCallback& callback,
    101     const string16& message) {
    102   if (!infobar_service) {
    103     callback.Run(false);
    104     return;
    105   }
    106 
    107   infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
    108       new DevToolsConfirmInfoBarDelegate(infobar_service, callback, message)));
    109 }
    110 
    111 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
    112     InfoBarService* infobar_service,
    113     const DevToolsWindow::InfoBarCallback& callback,
    114     const string16& message)
    115     : ConfirmInfoBarDelegate(infobar_service),
    116       callback_(callback),
    117       message_(message) {
    118 }
    119 
    120 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
    121   if (!callback_.is_null())
    122     callback_.Run(false);
    123 }
    124 
    125 string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
    126   return message_;
    127 }
    128 
    129 string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
    130     InfoBarButton button) const {
    131   return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
    132       IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
    133 }
    134 
    135 bool DevToolsConfirmInfoBarDelegate::Accept() {
    136   callback_.Run(true);
    137   callback_.Reset();
    138   return true;
    139 }
    140 
    141 bool DevToolsConfirmInfoBarDelegate::Cancel() {
    142   callback_.Run(false);
    143   callback_.Reset();
    144   return true;
    145 }
    146 
    147 
    148 // DevToolsWindow::InspectedWebContentsObserver -------------------------------
    149 
    150 class DevToolsWindow::InspectedWebContentsObserver
    151     : public content::WebContentsObserver {
    152  public:
    153   explicit InspectedWebContentsObserver(content::WebContents* web_contents);
    154   virtual ~InspectedWebContentsObserver();
    155 
    156   content::WebContents* web_contents() {
    157     return WebContentsObserver::web_contents();
    158   }
    159 
    160  private:
    161   DISALLOW_COPY_AND_ASSIGN(InspectedWebContentsObserver);
    162 };
    163 
    164 DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver(
    165     content::WebContents* web_contents)
    166     : WebContentsObserver(web_contents) {
    167 }
    168 
    169 DevToolsWindow::InspectedWebContentsObserver::~InspectedWebContentsObserver() {
    170 }
    171 
    172 
    173 // DevToolsWindow::FrontendWebContentsObserver --------------------------------
    174 
    175 class DevToolsWindow::FrontendWebContentsObserver
    176     : public content::WebContentsObserver {
    177  public:
    178   explicit FrontendWebContentsObserver(content::WebContents* web_contents);
    179   virtual ~FrontendWebContentsObserver();
    180 
    181  private:
    182   // contents::WebContentsObserver:
    183   virtual void AboutToNavigateRenderView(
    184       content::RenderViewHost* render_view_host) OVERRIDE;
    185 
    186   DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
    187 };
    188 
    189 DevToolsWindow::FrontendWebContentsObserver::FrontendWebContentsObserver(
    190     content::WebContents* web_contents)
    191     : WebContentsObserver(web_contents) {
    192 }
    193 
    194 DevToolsWindow::FrontendWebContentsObserver::~FrontendWebContentsObserver() {
    195 }
    196 
    197 void DevToolsWindow::FrontendWebContentsObserver::AboutToNavigateRenderView(
    198     content::RenderViewHost* render_view_host) {
    199   content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host);
    200 }
    201 
    202 
    203 // DevToolsWindow -------------------------------------------------------------
    204 
    205 namespace {
    206 
    207 typedef std::vector<DevToolsWindow*> DevToolsWindows;
    208 base::LazyInstance<DevToolsWindows>::Leaky g_instances =
    209     LAZY_INSTANCE_INITIALIZER;
    210 
    211 const char kPrefBottom[] = "dock_bottom";
    212 const char kPrefRight[] = "dock_right";
    213 const char kPrefUndocked[] = "undocked";
    214 
    215 const char kDockSideBottom[] = "bottom";
    216 const char kDockSideRight[] = "right";
    217 const char kDockSideUndocked[] = "undocked";
    218 const char kDockSideMinimized[] = "minimized";
    219 
    220 const int kMinContentsSize = 50;
    221 
    222 std::string SkColorToRGBAString(SkColor color) {
    223   // We avoid StringPrintf because it will use locale specific formatters for
    224   // the double (e.g. ',' instead of '.' in German).
    225   return "rgba(" + base::IntToString(SkColorGetR(color)) + "," +
    226       base::IntToString(SkColorGetG(color)) + "," +
    227       base::IntToString(SkColorGetB(color)) + "," +
    228       base::DoubleToString(SkColorGetA(color) / 255.0) + ")";
    229 }
    230 
    231 DictionaryValue* CreateFileSystemValue(
    232     DevToolsFileHelper::FileSystem file_system) {
    233   DictionaryValue* file_system_value = new DictionaryValue();
    234   file_system_value->SetString("fileSystemName", file_system.file_system_name);
    235   file_system_value->SetString("rootURL", file_system.root_url);
    236   file_system_value->SetString("fileSystemPath", file_system.file_system_path);
    237   return file_system_value;
    238 }
    239 
    240 }  // namespace
    241 
    242 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
    243 
    244 DevToolsWindow::~DevToolsWindow() {
    245   DevToolsWindows* instances = &g_instances.Get();
    246   DevToolsWindows::iterator it(
    247       std::find(instances->begin(), instances->end(), this));
    248   DCHECK(it != instances->end());
    249   instances->erase(it);
    250 
    251   for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
    252        jobs_it != indexing_jobs_.end(); ++jobs_it) {
    253     jobs_it->second->Stop();
    254   }
    255   indexing_jobs_.clear();
    256 }
    257 
    258 // static
    259 std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() {
    260   return std::string(prefs::kBrowserWindowPlacement) + "_" +
    261       std::string(kDevToolsApp);
    262 }
    263 
    264 // static
    265 void DevToolsWindow::RegisterProfilePrefs(
    266     user_prefs::PrefRegistrySyncable* registry) {
    267   registry->RegisterBooleanPref(
    268       prefs::kDevToolsOpenDocked, true,
    269       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    270   registry->RegisterStringPref(
    271       prefs::kDevToolsDockSide, kDockSideBottom,
    272       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    273   registry->RegisterDictionaryPref(
    274       prefs::kDevToolsEditedFiles,
    275       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    276   registry->RegisterDictionaryPref(
    277       prefs::kDevToolsFileSystemPaths,
    278       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    279   registry->RegisterStringPref(
    280       prefs::kDevToolsAdbKey, std::string(),
    281       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    282 
    283   registry->RegisterDictionaryPref(
    284       GetDevToolsWindowPlacementPrefKey().c_str(),
    285       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    286 
    287   registry->RegisterBooleanPref(
    288       prefs::kDevToolsPortForwardingEnabled,
    289       false,
    290       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    291   registry->RegisterDictionaryPref(
    292       prefs::kDevToolsPortForwardingConfig,
    293       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    294 }
    295 
    296 // static
    297 DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab(
    298     content::WebContents* inspected_web_contents) {
    299   if (!inspected_web_contents ||
    300       !DevToolsAgentHost::HasFor(inspected_web_contents->GetRenderViewHost()))
    301     return NULL;
    302   scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetOrCreateFor(
    303       inspected_web_contents->GetRenderViewHost()));
    304   DevToolsWindow* window = FindDevToolsWindow(agent.get());
    305   return (window && window->IsDocked()) ? window : NULL;
    306 }
    307 
    308 // static
    309 bool DevToolsWindow::IsDevToolsWindow(content::RenderViewHost* window_rvh) {
    310   return AsDevToolsWindow(window_rvh) != NULL;
    311 }
    312 
    313 // static
    314 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker(
    315     Profile* profile,
    316     DevToolsAgentHost* worker_agent) {
    317   DevToolsWindow* window = FindDevToolsWindow(worker_agent);
    318   if (!window) {
    319     window = DevToolsWindow::CreateDevToolsWindowForWorker(profile);
    320     // Will disconnect the current client host if there is one.
    321     content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
    322         worker_agent, window->frontend_host_.get());
    323   }
    324   window->Show(DEVTOOLS_TOGGLE_ACTION_SHOW);
    325   return window;
    326 }
    327 
    328 // static
    329 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
    330     Profile* profile) {
    331   return Create(profile, GURL(), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true);
    332 }
    333 
    334 // static
    335 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
    336     content::RenderViewHost* inspected_rvh) {
    337   return ToggleDevToolsWindow(inspected_rvh, true,
    338                               DEVTOOLS_TOGGLE_ACTION_SHOW);
    339 }
    340 
    341 // static
    342 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
    343     Browser* browser,
    344     DevToolsToggleAction action) {
    345   if (action == DEVTOOLS_TOGGLE_ACTION_TOGGLE && browser->is_devtools()) {
    346     browser->tab_strip_model()->CloseAllTabs();
    347     return NULL;
    348   }
    349 
    350   return ToggleDevToolsWindow(
    351       browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
    352       action == DEVTOOLS_TOGGLE_ACTION_INSPECT, action);
    353 }
    354 
    355 // static
    356 void DevToolsWindow::OpenExternalFrontend(
    357     Profile* profile,
    358     const std::string& frontend_url,
    359     content::DevToolsAgentHost* agent_host) {
    360   DevToolsWindow* window = FindDevToolsWindow(agent_host);
    361   if (!window) {
    362     window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL,
    363                     DEVTOOLS_DOCK_SIDE_UNDOCKED, false);
    364     content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
    365         agent_host, window->frontend_host_.get());
    366   }
    367   window->Show(DEVTOOLS_TOGGLE_ACTION_SHOW);
    368 }
    369 
    370 // static
    371 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
    372     content::RenderViewHost* inspected_rvh,
    373     bool force_open,
    374     DevToolsToggleAction action) {
    375   scoped_refptr<DevToolsAgentHost> agent(
    376       DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
    377   content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
    378   DevToolsWindow* window = FindDevToolsWindow(agent.get());
    379   bool do_open = force_open;
    380   if (!window) {
    381     Profile* profile = Profile::FromBrowserContext(
    382         inspected_rvh->GetProcess()->GetBrowserContext());
    383     DevToolsDockSide dock_side = GetDockSideFromPrefs(profile);
    384     window = Create(profile, GURL(), inspected_rvh, dock_side, false);
    385     manager->RegisterDevToolsClientHostFor(agent.get(),
    386                                            window->frontend_host_.get());
    387     do_open = true;
    388   }
    389 
    390   // Update toolbar to reflect DevTools changes.
    391   window->UpdateBrowserToolbar();
    392 
    393   // If window is docked and visible, we hide it on toggle. If window is
    394   // undocked, we show (activate) it. If window is minimized, we maximize it.
    395   if (window->dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
    396     window->Restore();
    397   else if (!window->IsDocked() || do_open)
    398     window->Show(action);
    399   else
    400     window->CloseWindow();
    401 
    402   return window;
    403 }
    404 
    405 // static
    406 void DevToolsWindow::InspectElement(content::RenderViewHost* inspected_rvh,
    407                                     int x,
    408                                     int y) {
    409   scoped_refptr<DevToolsAgentHost> agent(
    410       DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
    411   agent->InspectElement(x, y);
    412   // TODO(loislo): we should initiate DevTools window opening from within
    413   // renderer. Otherwise, we still can hit a race condition here.
    414   OpenDevToolsWindow(inspected_rvh);
    415 }
    416 
    417 // static
    418 int DevToolsWindow::GetMinimumWidth() {
    419   const int kMinDevToolsWidth = 150;
    420   return kMinDevToolsWidth;
    421 }
    422 
    423 // static
    424 int DevToolsWindow::GetMinimumHeight() {
    425   // Minimal height of devtools pane or content pane when devtools are docked
    426   // to the browser window.
    427   const int kMinDevToolsHeight = 50;
    428   return kMinDevToolsHeight;
    429 }
    430 
    431 // static
    432 int DevToolsWindow::GetMinimizedHeight() {
    433   const int kMinimizedDevToolsHeight = 24;
    434   return kMinimizedDevToolsHeight;
    435 }
    436 
    437 void DevToolsWindow::InspectedContentsClosing() {
    438   Hide();
    439 }
    440 
    441 content::RenderViewHost* DevToolsWindow::GetRenderViewHost() {
    442   return web_contents_->GetRenderViewHost();
    443 }
    444 
    445 content::DevToolsClientHost* DevToolsWindow::GetDevToolsClientHostForTest() {
    446   return frontend_host_.get();
    447 }
    448 
    449 int DevToolsWindow::GetWidth(int container_width) {
    450   if (width_ == -1) {
    451     width_ = profile_->GetPrefs()->
    452         GetInteger(prefs::kDevToolsVSplitLocation);
    453   }
    454 
    455   // By default, size devtools as 1/3 of the browser window.
    456   if (width_ == -1)
    457     width_ = container_width / 3;
    458 
    459   // Respect the minimum devtools width preset.
    460   width_ = std::max(GetMinimumWidth(), width_);
    461 
    462   // But it should never compromise the content window size unless the entire
    463   // window is tiny.
    464   width_ = std::min(container_width - kMinContentsSize, width_);
    465   return width_;
    466 }
    467 
    468 int DevToolsWindow::GetHeight(int container_height) {
    469   if (height_ == -1) {
    470     height_ = profile_->GetPrefs()->
    471         GetInteger(prefs::kDevToolsHSplitLocation);
    472   }
    473 
    474   // By default, size devtools as 1/3 of the browser window.
    475   if (height_ == -1)
    476     height_ = container_height / 3;
    477 
    478   // Respect the minimum devtools width preset.
    479   height_ = std::max(GetMinimumHeight(), height_);
    480 
    481   // But it should never compromise the content window size.
    482   height_ = std::min(container_height - kMinContentsSize, height_);
    483   return height_;
    484 }
    485 
    486 void DevToolsWindow::SetWidth(int width) {
    487   width_ = width;
    488   profile_->GetPrefs()->SetInteger(prefs::kDevToolsVSplitLocation, width);
    489 }
    490 
    491 void DevToolsWindow::SetHeight(int height) {
    492   height_ = height;
    493   profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height);
    494 }
    495 
    496 void DevToolsWindow::Show(DevToolsToggleAction action) {
    497   if (IsDocked()) {
    498     Browser* inspected_browser = NULL;
    499     int inspected_tab_index = -1;
    500     // Tell inspected browser to update splitter and switch to inspected panel.
    501     if (!IsInspectedBrowserPopup() &&
    502         FindInspectedBrowserAndTabIndex(&inspected_browser,
    503                                         &inspected_tab_index)) {
    504       BrowserWindow* inspected_window = inspected_browser->window();
    505       web_contents_->SetDelegate(this);
    506       inspected_window->UpdateDevTools();
    507       web_contents_->GetView()->SetInitialFocus();
    508       inspected_window->Show();
    509       TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
    510       tab_strip_model->ActivateTabAt(inspected_tab_index, true);
    511       PrefsTabHelper::CreateForWebContents(web_contents_);
    512       GetRenderViewHost()->SyncRendererPrefs();
    513       ScheduleAction(action);
    514       return;
    515     }
    516 
    517     // Sometimes we don't know where to dock. Stay undocked.
    518     dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED;
    519   }
    520 
    521   // Avoid consecutive window switching if the devtools window has been opened
    522   // and the Inspect Element shortcut is pressed in the inspected tab.
    523   bool should_show_window =
    524       !browser_ || (action != DEVTOOLS_TOGGLE_ACTION_INSPECT);
    525 
    526   if (!browser_)
    527     CreateDevToolsBrowser();
    528 
    529   if (should_show_window) {
    530     browser_->window()->Show();
    531     web_contents_->GetView()->SetInitialFocus();
    532   }
    533 
    534   ScheduleAction(action);
    535 }
    536 
    537 DevToolsWindow::DevToolsWindow(Profile* profile,
    538                                const GURL& url,
    539                                content::RenderViewHost* inspected_rvh,
    540                                DevToolsDockSide dock_side)
    541     : profile_(profile),
    542       browser_(NULL),
    543       dock_side_(dock_side),
    544       is_loaded_(false),
    545       action_on_load_(DEVTOOLS_TOGGLE_ACTION_SHOW),
    546       weak_factory_(this),
    547       width_(-1),
    548       height_(-1),
    549       dock_side_before_minimized_(dock_side) {
    550   web_contents_ =
    551       content::WebContents::Create(content::WebContents::CreateParams(profile));
    552   frontend_contents_observer_.reset(
    553       new FrontendWebContentsObserver(web_contents_));
    554 
    555   web_contents_->GetController().LoadURL(url, content::Referrer(),
    556       content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
    557 
    558   frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost(
    559       web_contents_, this));
    560   file_helper_.reset(new DevToolsFileHelper(web_contents_, profile));
    561   file_system_indexer_ = new DevToolsFileSystemIndexer();
    562 
    563   g_instances.Get().push_back(this);
    564 
    565   // Wipe out page icon so that the default application icon is used.
    566   content::NavigationEntry* entry =
    567       web_contents_->GetController().GetActiveEntry();
    568   entry->GetFavicon().image = gfx::Image();
    569   entry->GetFavicon().valid = true;
    570 
    571   // Register on-load actions.
    572   content::Source<content::NavigationController> nav_controller_source(
    573       &web_contents_->GetController());
    574   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, nav_controller_source);
    575   registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING, nav_controller_source);
    576   registrar_.Add(
    577       this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
    578       content::Source<ThemeService>(
    579           ThemeServiceFactory::GetForProfile(profile_)));
    580 
    581   // There is no inspected_rvh in case of shared workers.
    582   if (inspected_rvh)
    583     inspected_contents_observer_.reset(new InspectedWebContentsObserver(
    584         content::WebContents::FromRenderViewHost(inspected_rvh)));
    585 }
    586 
    587 // static
    588 DevToolsWindow* DevToolsWindow::Create(
    589     Profile* profile,
    590     const GURL& frontend_url,
    591     content::RenderViewHost* inspected_rvh,
    592     DevToolsDockSide dock_side,
    593     bool shared_worker_frontend) {
    594   // Create WebContents with devtools.
    595   GURL url(GetDevToolsURL(profile, frontend_url, dock_side,
    596                           shared_worker_frontend));
    597   return new DevToolsWindow(profile, url, inspected_rvh, dock_side);
    598 }
    599 
    600 // static
    601 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
    602                                     const GURL& base_url,
    603                                     DevToolsDockSide dock_side,
    604                                     bool shared_worker_frontend) {
    605   std::string frontend_url(
    606       base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec());
    607   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile);
    608   DCHECK(tp);
    609   std::string url_string(
    610       frontend_url +
    611       ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
    612       "dockSide=" + SideToString(dock_side) +
    613       "&toolbarColor=" +
    614       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
    615       "&textColor=" +
    616       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)));
    617   if (shared_worker_frontend)
    618     url_string += "&isSharedWorker=true";
    619   if (CommandLine::ForCurrentProcess()->HasSwitch(
    620       switches::kEnableDevToolsExperiments))
    621     url_string += "&experiments=true";
    622   return GURL(url_string);
    623 }
    624 
    625 // static
    626 DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
    627     DevToolsAgentHost* agent_host) {
    628   DevToolsWindows* instances = &g_instances.Get();
    629   content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
    630   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
    631        ++it) {
    632     if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) ==
    633         agent_host)
    634       return *it;
    635   }
    636   return NULL;
    637 }
    638 
    639 // static
    640 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
    641     content::RenderViewHost* window_rvh) {
    642   if (g_instances == NULL)
    643     return NULL;
    644   DevToolsWindows* instances = &g_instances.Get();
    645   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
    646        ++it) {
    647     if ((*it)->web_contents_->GetRenderViewHost() == window_rvh)
    648       return *it;
    649   }
    650   return NULL;
    651 }
    652 
    653 // static
    654 DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) {
    655   std::string dock_side =
    656       profile->GetPrefs()->GetString(prefs::kDevToolsDockSide);
    657 
    658   // Migrate prefs.
    659   const char kOldPrefBottom[] = "bottom";
    660   const char kOldPrefRight[] = "right";
    661   if ((dock_side == kOldPrefBottom) || (dock_side == kOldPrefRight)) {
    662     if (!profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked))
    663       return DEVTOOLS_DOCK_SIDE_UNDOCKED;
    664     return (dock_side == kOldPrefBottom) ?
    665         DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_RIGHT;
    666   }
    667 
    668   if (dock_side == kPrefUndocked)
    669     return DEVTOOLS_DOCK_SIDE_UNDOCKED;
    670   if (dock_side == kPrefRight)
    671     return DEVTOOLS_DOCK_SIDE_RIGHT;
    672   // Default to docked to bottom.
    673   return DEVTOOLS_DOCK_SIDE_BOTTOM;
    674 }
    675 
    676 // static
    677 std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) {
    678   switch (dock_side) {
    679     case DEVTOOLS_DOCK_SIDE_UNDOCKED:  return kDockSideUndocked;
    680     case DEVTOOLS_DOCK_SIDE_RIGHT:     return kDockSideRight;
    681     case DEVTOOLS_DOCK_SIDE_BOTTOM:    return kDockSideBottom;
    682     case DEVTOOLS_DOCK_SIDE_MINIMIZED: return kDockSideMinimized;
    683     default:                           return kDockSideUndocked;
    684   }
    685 }
    686 
    687 // static
    688 DevToolsDockSide DevToolsWindow::SideFromString(
    689     const std::string& dock_side) {
    690   if (dock_side == kDockSideRight)
    691     return DEVTOOLS_DOCK_SIDE_RIGHT;
    692   if (dock_side == kDockSideBottom)
    693     return DEVTOOLS_DOCK_SIDE_BOTTOM;
    694   return (dock_side == kDockSideMinimized) ?
    695       DEVTOOLS_DOCK_SIDE_MINIMIZED : DEVTOOLS_DOCK_SIDE_UNDOCKED;
    696 }
    697 
    698 void DevToolsWindow::Observe(int type,
    699                              const content::NotificationSource& source,
    700                              const content::NotificationDetails& details) {
    701   if (type == content::NOTIFICATION_LOAD_STOP) {
    702     if (!is_loaded_) {
    703       is_loaded_ = true;
    704       UpdateTheme();
    705       DoAction();
    706       AddDevToolsExtensionsToClient();
    707     }
    708   } else if (type == chrome::NOTIFICATION_TAB_CLOSING) {
    709     if (content::Source<content::NavigationController>(source).ptr() ==
    710         &web_contents_->GetController()) {
    711       // This happens when browser closes all of its tabs as a result
    712       // of window.Close event.
    713       // Notify manager that this DevToolsClientHost no longer exists and
    714       // initiate self-destuct here.
    715       content::DevToolsManager::GetInstance()->ClientHostClosing(
    716           frontend_host_.get());
    717       UpdateBrowserToolbar();
    718       delete this;
    719     }
    720   } else {
    721     DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
    722     UpdateTheme();
    723   }
    724 }
    725 
    726 content::WebContents* DevToolsWindow::OpenURLFromTab(
    727     content::WebContents* source,
    728     const content::OpenURLParams& params) {
    729   if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) {
    730     content::WebContents* inspected_web_contents = GetInspectedWebContents();
    731     return inspected_web_contents ?
    732         inspected_web_contents->OpenURL(params) : NULL;
    733   }
    734 
    735   content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
    736   scoped_refptr<DevToolsAgentHost> agent_host(
    737       manager->GetDevToolsAgentHostFor(frontend_host_.get()));
    738   if (!agent_host.get())
    739     return NULL;
    740   manager->ClientHostClosing(frontend_host_.get());
    741   manager->RegisterDevToolsClientHostFor(agent_host.get(),
    742                                          frontend_host_.get());
    743 
    744   chrome::NavigateParams nav_params(profile_, params.url, params.transition);
    745   FillNavigateParamsFromOpenURLParams(&nav_params, params);
    746   nav_params.source_contents = source;
    747   nav_params.tabstrip_add_types = TabStripModel::ADD_NONE;
    748   nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
    749   nav_params.user_gesture = params.user_gesture;
    750   chrome::Navigate(&nav_params);
    751   return nav_params.target_contents;
    752 }
    753 
    754 void DevToolsWindow::AddNewContents(content::WebContents* source,
    755                                     content::WebContents* new_contents,
    756                                     WindowOpenDisposition disposition,
    757                                     const gfx::Rect& initial_pos,
    758                                     bool user_gesture,
    759                                     bool* was_blocked) {
    760   content::WebContents* inspected_web_contents = GetInspectedWebContents();
    761   if (inspected_web_contents) {
    762     inspected_web_contents->GetDelegate()->AddNewContents(
    763         source, new_contents, disposition, initial_pos, user_gesture,
    764         was_blocked);
    765   }
    766 }
    767 
    768 void DevToolsWindow::CloseContents(content::WebContents* source) {
    769 }
    770 
    771 bool DevToolsWindow::PreHandleKeyboardEvent(
    772     content::WebContents* source,
    773     const content::NativeWebKeyboardEvent& event,
    774     bool* is_keyboard_shortcut) {
    775   if (IsDocked()) {
    776     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
    777     if (inspected_window) {
    778       return inspected_window->PreHandleKeyboardEvent(event,
    779                                                       is_keyboard_shortcut);
    780     }
    781   }
    782   return false;
    783 }
    784 
    785 void DevToolsWindow::HandleKeyboardEvent(
    786     content::WebContents* source,
    787     const content::NativeWebKeyboardEvent& event) {
    788   if (IsDocked()) {
    789     if (event.windowsKeyCode == 0x08) {
    790       // Do not navigate back in history on Windows (http://crbug.com/74156).
    791       return;
    792     }
    793     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
    794     if (inspected_window)
    795       inspected_window->HandleKeyboardEvent(event);
    796   }
    797 }
    798 
    799 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
    800   content::WebContents* inspected_web_contents = GetInspectedWebContents();
    801   return (inspected_web_contents && inspected_web_contents->GetDelegate()) ?
    802       inspected_web_contents->GetDelegate()->GetJavaScriptDialogManager() :
    803       content::WebContentsDelegate::GetJavaScriptDialogManager();
    804 }
    805 
    806 content::ColorChooser* DevToolsWindow::OpenColorChooser(
    807     content::WebContents* web_contents,
    808     SkColor initial_color) {
    809   return chrome::ShowColorChooser(web_contents, initial_color);
    810 }
    811 
    812 void DevToolsWindow::RunFileChooser(content::WebContents* web_contents,
    813                                     const content::FileChooserParams& params) {
    814   FileSelectHelper::RunFileChooser(web_contents, params);
    815 }
    816 
    817 void DevToolsWindow::WebContentsFocused(content::WebContents* contents) {
    818   Browser* inspected_browser = NULL;
    819   int inspected_tab_index = -1;
    820   if (IsDocked() && FindInspectedBrowserAndTabIndex(&inspected_browser,
    821                                                     &inspected_tab_index))
    822     inspected_browser->window()->WebContentsFocused(contents);
    823 }
    824 
    825 void DevToolsWindow::ActivateWindow() {
    826   if (IsDocked() && GetInspectedBrowserWindow())
    827     web_contents_->GetView()->Focus();
    828   else if (!IsDocked() && !browser_->window()->IsActive())
    829     browser_->window()->Activate();
    830 }
    831 
    832 void DevToolsWindow::ChangeAttachedWindowHeight(unsigned height) {
    833   NOTREACHED();  // TODO(dgozman): This is not used anymore, remove.
    834 }
    835 
    836 void DevToolsWindow::CloseWindow() {
    837   DCHECK(IsDocked());
    838   content::DevToolsManager::GetInstance()->ClientHostClosing(
    839       frontend_host_.get());
    840   Hide();
    841 }
    842 
    843 void DevToolsWindow::MoveWindow(int x, int y) {
    844   if (!IsDocked()) {
    845     gfx::Rect bounds = browser_->window()->GetBounds();
    846     bounds.Offset(x, y);
    847     browser_->window()->SetBounds(bounds);
    848   }
    849 }
    850 
    851 void DevToolsWindow::SetDockSide(const std::string& side) {
    852   DevToolsDockSide requested_side = SideFromString(side);
    853   bool dock_requested = requested_side != DEVTOOLS_DOCK_SIDE_UNDOCKED;
    854   bool is_docked = IsDocked();
    855 
    856   if (dock_requested &&
    857       (!GetInspectedWebContents() || !GetInspectedBrowserWindow() ||
    858        IsInspectedBrowserPopup())) {
    859       // Cannot dock, avoid window flashing due to close-reopen cycle.
    860     return;
    861   }
    862 
    863   if ((dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) &&
    864       (requested_side == DEVTOOLS_DOCK_SIDE_MINIMIZED))
    865     dock_side_before_minimized_ = dock_side_;
    866 
    867   dock_side_ = requested_side;
    868   if (dock_requested && !is_docked) {
    869     // Detach window from the external devtools browser. It will lead to
    870     // the browser object's close and delete. Remove observer first.
    871     TabStripModel* tab_strip_model = browser_->tab_strip_model();
    872     tab_strip_model->DetachWebContentsAt(
    873         tab_strip_model->GetIndexOfWebContents(web_contents_));
    874     browser_ = NULL;
    875   } else if (!dock_requested && is_docked) {
    876     // Update inspected window to hide split and reset it.
    877     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
    878     if (inspected_window)
    879       inspected_window->UpdateDevTools();
    880   }
    881 
    882   if (dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) {
    883     std::string pref_value = kPrefBottom;
    884     switch (dock_side_) {
    885       case DEVTOOLS_DOCK_SIDE_UNDOCKED:
    886           pref_value = kPrefUndocked;
    887           break;
    888       case DEVTOOLS_DOCK_SIDE_RIGHT:
    889           pref_value = kPrefRight;
    890           break;
    891       case DEVTOOLS_DOCK_SIDE_BOTTOM:
    892           pref_value = kPrefBottom;
    893           break;
    894       case DEVTOOLS_DOCK_SIDE_MINIMIZED:
    895           // We don't persist minimized state.
    896           break;
    897     }
    898     profile_->GetPrefs()->SetString(prefs::kDevToolsDockSide, pref_value);
    899   }
    900 
    901   Show(DEVTOOLS_TOGGLE_ACTION_SHOW);
    902 }
    903 
    904 void DevToolsWindow::OpenInNewTab(const std::string& url) {
    905   content::OpenURLParams params(
    906       GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
    907       content::PAGE_TRANSITION_LINK, false);
    908   content::WebContents* inspected_web_contents = GetInspectedWebContents();
    909   if (inspected_web_contents) {
    910     inspected_web_contents->OpenURL(params);
    911   } else {
    912     chrome::HostDesktopType host_desktop_type;
    913     if (browser_) {
    914       host_desktop_type = browser_->host_desktop_type();
    915     } else {
    916       // There should always be a browser when there are no inspected web
    917       // contents.
    918       NOTREACHED();
    919       host_desktop_type = chrome::GetActiveDesktop();
    920     }
    921 
    922     const BrowserList* browser_list =
    923         BrowserList::GetInstance(host_desktop_type);
    924     for (BrowserList::const_iterator it = browser_list->begin();
    925          it != browser_list->end(); ++it) {
    926       if ((*it)->type() == Browser::TYPE_TABBED) {
    927         (*it)->OpenURL(params);
    928         break;
    929       }
    930     }
    931   }
    932 }
    933 
    934 void DevToolsWindow::SaveToFile(const std::string& url,
    935                                 const std::string& content,
    936                                 bool save_as) {
    937   file_helper_->Save(url, content, save_as,
    938                      base::Bind(&DevToolsWindow::FileSavedAs,
    939                                 weak_factory_.GetWeakPtr(), url));
    940 }
    941 
    942 void DevToolsWindow::AppendToFile(const std::string& url,
    943                                   const std::string& content) {
    944   file_helper_->Append(url, content,
    945                        base::Bind(&DevToolsWindow::AppendedTo,
    946                                   weak_factory_.GetWeakPtr(), url));
    947 }
    948 
    949 void DevToolsWindow::RequestFileSystems() {
    950   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
    951   file_helper_->RequestFileSystems(base::Bind(
    952       &DevToolsWindow::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
    953 }
    954 
    955 void DevToolsWindow::AddFileSystem() {
    956   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
    957   file_helper_->AddFileSystem(
    958       base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
    959       base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
    960                  weak_factory_.GetWeakPtr()));
    961 }
    962 
    963 void DevToolsWindow::RemoveFileSystem(const std::string& file_system_path) {
    964   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
    965   file_helper_->RemoveFileSystem(file_system_path);
    966   StringValue file_system_path_value(file_system_path);
    967   CallClientFunction("InspectorFrontendAPI.fileSystemRemoved",
    968                      &file_system_path_value, NULL, NULL);
    969 }
    970 
    971 void DevToolsWindow::IndexPath(int request_id,
    972                                const std::string& file_system_path) {
    973   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    974   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
    975   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
    976     IndexingDone(request_id, file_system_path);
    977     return;
    978   }
    979   indexing_jobs_[request_id] =
    980       scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
    981           file_system_indexer_->IndexPath(
    982               file_system_path,
    983               Bind(&DevToolsWindow::IndexingTotalWorkCalculated,
    984                    weak_factory_.GetWeakPtr(),
    985                    request_id,
    986                    file_system_path),
    987               Bind(&DevToolsWindow::IndexingWorked,
    988                    weak_factory_.GetWeakPtr(),
    989                    request_id,
    990                    file_system_path),
    991               Bind(&DevToolsWindow::IndexingDone,
    992                    weak_factory_.GetWeakPtr(),
    993                    request_id,
    994                    file_system_path)));
    995 }
    996 
    997 void DevToolsWindow::StopIndexing(int request_id) {
    998   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    999   IndexingJobsMap::iterator it = indexing_jobs_.find(request_id);
   1000   if (it == indexing_jobs_.end())
   1001     return;
   1002   it->second->Stop();
   1003   indexing_jobs_.erase(it);
   1004 }
   1005 
   1006 void DevToolsWindow::SearchInPath(int request_id,
   1007                                   const std::string& file_system_path,
   1008                                   const std::string& query) {
   1009   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   1010   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
   1011   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
   1012     SearchCompleted(request_id, file_system_path, std::vector<std::string>());
   1013     return;
   1014   }
   1015   file_system_indexer_->SearchInPath(file_system_path,
   1016                                      query,
   1017                                      Bind(&DevToolsWindow::SearchCompleted,
   1018                                           weak_factory_.GetWeakPtr(),
   1019                                           request_id,
   1020                                           file_system_path));
   1021 }
   1022 
   1023 void DevToolsWindow::FileSavedAs(const std::string& url) {
   1024   StringValue url_value(url);
   1025   CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL);
   1026 }
   1027 
   1028 void DevToolsWindow::AppendedTo(const std::string& url) {
   1029   StringValue url_value(url);
   1030   CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL,
   1031                      NULL);
   1032 }
   1033 
   1034 void DevToolsWindow::FileSystemsLoaded(
   1035     const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
   1036   ListValue file_systems_value;
   1037   for (size_t i = 0; i < file_systems.size(); ++i)
   1038     file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
   1039   CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded",
   1040                      &file_systems_value, NULL, NULL);
   1041 }
   1042 
   1043 void DevToolsWindow::FileSystemAdded(
   1044     const DevToolsFileHelper::FileSystem& file_system) {
   1045   StringValue error_string_value((std::string()));
   1046   DictionaryValue* file_system_value = NULL;
   1047   if (!file_system.file_system_path.empty())
   1048     file_system_value = CreateFileSystemValue(file_system);
   1049   CallClientFunction("InspectorFrontendAPI.fileSystemAdded",
   1050                      &error_string_value, file_system_value, NULL);
   1051   if (file_system_value)
   1052     delete file_system_value;
   1053 }
   1054 
   1055 void DevToolsWindow::IndexingTotalWorkCalculated(
   1056     int request_id,
   1057     const std::string& file_system_path,
   1058     int total_work) {
   1059   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   1060   base::FundamentalValue request_id_value(request_id);
   1061   StringValue file_system_path_value(file_system_path);
   1062   base::FundamentalValue total_work_value(total_work);
   1063   CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated",
   1064                      &request_id_value, &file_system_path_value,
   1065                      &total_work_value);
   1066 }
   1067 
   1068 void DevToolsWindow::IndexingWorked(int request_id,
   1069                                     const std::string& file_system_path,
   1070                                     int worked) {
   1071   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   1072   base::FundamentalValue request_id_value(request_id);
   1073   StringValue file_system_path_value(file_system_path);
   1074   base::FundamentalValue worked_value(worked);
   1075   CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value,
   1076                      &file_system_path_value, &worked_value);
   1077 }
   1078 
   1079 void DevToolsWindow::IndexingDone(int request_id,
   1080                                   const std::string& file_system_path) {
   1081   indexing_jobs_.erase(request_id);
   1082   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   1083   base::FundamentalValue request_id_value(request_id);
   1084   StringValue file_system_path_value(file_system_path);
   1085   CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value,
   1086                      &file_system_path_value, NULL);
   1087 }
   1088 
   1089 void DevToolsWindow::SearchCompleted(
   1090     int request_id,
   1091     const std::string& file_system_path,
   1092     const std::vector<std::string>& file_paths) {
   1093   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   1094   ListValue file_paths_value;
   1095   for (std::vector<std::string>::const_iterator it(file_paths.begin());
   1096        it != file_paths.end(); ++it) {
   1097     file_paths_value.AppendString(*it);
   1098   }
   1099   base::FundamentalValue request_id_value(request_id);
   1100   StringValue file_system_path_value(file_system_path);
   1101   CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value,
   1102                      &file_system_path_value, &file_paths_value);
   1103 }
   1104 
   1105 void DevToolsWindow::ShowDevToolsConfirmInfoBar(
   1106     const string16& message,
   1107     const InfoBarCallback& callback) {
   1108   DevToolsConfirmInfoBarDelegate::Create(
   1109       IsDocked() ?
   1110           InfoBarService::FromWebContents(GetInspectedWebContents()) :
   1111           InfoBarService::FromWebContents(web_contents_),
   1112       callback, message);
   1113 }
   1114 
   1115 void DevToolsWindow::CreateDevToolsBrowser() {
   1116   std::string wp_key = GetDevToolsWindowPlacementPrefKey();
   1117   PrefService* prefs = profile_->GetPrefs();
   1118   const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
   1119   if (!wp_pref || wp_pref->empty()) {
   1120     DictionaryPrefUpdate update(prefs, wp_key.c_str());
   1121     DictionaryValue* defaults = update.Get();
   1122     defaults->SetInteger("left", 100);
   1123     defaults->SetInteger("top", 100);
   1124     defaults->SetInteger("right", 740);
   1125     defaults->SetInteger("bottom", 740);
   1126     defaults->SetBoolean("maximized", false);
   1127     defaults->SetBoolean("always_on_top", false);
   1128   }
   1129 
   1130   browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
   1131       profile_,
   1132       chrome::GetHostDesktopTypeForNativeView(
   1133           web_contents_->GetView()->GetNativeView())));
   1134   browser_->tab_strip_model()->AddWebContents(
   1135       web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL,
   1136       TabStripModel::ADD_ACTIVE);
   1137   GetRenderViewHost()->SyncRendererPrefs();
   1138 }
   1139 
   1140 bool DevToolsWindow::FindInspectedBrowserAndTabIndex(Browser** browser,
   1141                                                      int* tab) {
   1142   content::WebContents* inspected_web_contents = GetInspectedWebContents();
   1143   if (!inspected_web_contents)
   1144     return false;
   1145 
   1146   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
   1147     int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
   1148         inspected_web_contents);
   1149     if (tab_index != TabStripModel::kNoTab) {
   1150       *browser = *it;
   1151       *tab = tab_index;
   1152       return true;
   1153     }
   1154   }
   1155   return false;
   1156 }
   1157 
   1158 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
   1159   Browser* browser = NULL;
   1160   int tab;
   1161   return FindInspectedBrowserAndTabIndex(&browser, &tab) ?
   1162       browser->window() : NULL;
   1163 }
   1164 
   1165 bool DevToolsWindow::IsInspectedBrowserPopup() {
   1166   Browser* browser = NULL;
   1167   int tab;
   1168   return FindInspectedBrowserAndTabIndex(&browser, &tab) &&
   1169       browser->is_type_popup();
   1170 }
   1171 
   1172 void DevToolsWindow::UpdateFrontendDockSide() {
   1173   base::StringValue dock_side(SideToString(dock_side_));
   1174   CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side, NULL,
   1175                      NULL);
   1176   base::FundamentalValue docked(IsDocked());
   1177   CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked, NULL,
   1178                      NULL);
   1179 }
   1180 
   1181 void DevToolsWindow::Hide() {
   1182   if (IsDocked()) {
   1183     // Update dev tools to reflect removed dev tools window.
   1184     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
   1185     if (inspected_window)
   1186       inspected_window->UpdateDevTools();
   1187     // In case of docked web_contents_, we own it so delete here.
   1188     delete web_contents_;
   1189 
   1190     delete this;
   1191   } else {
   1192     // First, initiate self-destruct to free all the registrars.
   1193     // Then close all tabs. Browser will take care of deleting web_contents_
   1194     // for us.
   1195     Browser* browser = browser_;
   1196     delete this;
   1197     browser->tab_strip_model()->CloseAllTabs();
   1198   }
   1199 }
   1200 
   1201 void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) {
   1202   action_on_load_ = action;
   1203   if (is_loaded_)
   1204     DoAction();
   1205 }
   1206 
   1207 void DevToolsWindow::DoAction() {
   1208   UpdateFrontendDockSide();
   1209   switch (action_on_load_) {
   1210     case DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE:
   1211       CallClientFunction("InspectorFrontendAPI.showConsole", NULL, NULL, NULL);
   1212       break;
   1213 
   1214     case DEVTOOLS_TOGGLE_ACTION_INSPECT:
   1215       CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL,
   1216                          NULL, NULL);
   1217       break;
   1218 
   1219     case DEVTOOLS_TOGGLE_ACTION_SHOW:
   1220     case DEVTOOLS_TOGGLE_ACTION_TOGGLE:
   1221       // Do nothing.
   1222       break;
   1223 
   1224     default:
   1225       NOTREACHED();
   1226       break;
   1227   }
   1228   action_on_load_ = DEVTOOLS_TOGGLE_ACTION_SHOW;
   1229 }
   1230 
   1231 void DevToolsWindow::UpdateTheme() {
   1232   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
   1233   DCHECK(tp);
   1234 
   1235   std::string command("InspectorFrontendAPI.setToolbarColors(\"" +
   1236       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
   1237       "\", \"" +
   1238       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) +
   1239       "\")");
   1240   web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
   1241       string16(), ASCIIToUTF16(command));
   1242 }
   1243 
   1244 void DevToolsWindow::AddDevToolsExtensionsToClient() {
   1245   content::WebContents* inspected_web_contents = GetInspectedWebContents();
   1246   if (inspected_web_contents) {
   1247     SessionTabHelper* session_tab_helper =
   1248         SessionTabHelper::FromWebContents(inspected_web_contents);
   1249     if (session_tab_helper) {
   1250       base::FundamentalValue tabId(session_tab_helper->session_id().id());
   1251       CallClientFunction("WebInspector.setInspectedTabId", &tabId, NULL, NULL);
   1252     }
   1253   }
   1254 
   1255   Profile* profile =
   1256       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
   1257   const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
   1258       profile->GetOriginalProfile())->extension_service();
   1259   if (!extension_service)
   1260     return;
   1261   const ExtensionSet* extensions = extension_service->extensions();
   1262 
   1263   ListValue results;
   1264   for (ExtensionSet::const_iterator extension(extensions->begin());
   1265        extension != extensions->end(); ++extension) {
   1266     if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
   1267       continue;
   1268     DictionaryValue* extension_info = new DictionaryValue();
   1269     extension_info->Set(
   1270         "startPage",
   1271         new StringValue(
   1272             extensions::ManifestURL::GetDevToolsPage(extension->get()).spec()));
   1273     extension_info->Set("name", new StringValue((*extension)->name()));
   1274     extension_info->Set(
   1275         "exposeExperimentalAPIs",
   1276         new base::FundamentalValue((*extension)->HasAPIPermission(
   1277             extensions::APIPermission::kExperimental)));
   1278     results.Append(extension_info);
   1279   }
   1280   CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL);
   1281 }
   1282 
   1283 void DevToolsWindow::CallClientFunction(const std::string& function_name,
   1284                                         const Value* arg1,
   1285                                         const Value* arg2,
   1286                                         const Value* arg3) {
   1287   std::string params;
   1288   if (arg1) {
   1289     std::string json;
   1290     base::JSONWriter::Write(arg1, &json);
   1291     params.append(json);
   1292     if (arg2) {
   1293       base::JSONWriter::Write(arg2, &json);
   1294       params.append(", " + json);
   1295       if (arg3) {
   1296         base::JSONWriter::Write(arg3, &json);
   1297         params.append(", " + json);
   1298       }
   1299     }
   1300   }
   1301   string16 javascript = ASCIIToUTF16(function_name + "(" + params + ");");
   1302   web_contents_->GetRenderViewHost()->
   1303       ExecuteJavascriptInWebFrame(string16(), javascript);
   1304 }
   1305 
   1306 void DevToolsWindow::UpdateBrowserToolbar() {
   1307   content::WebContents* inspected_web_contents = GetInspectedWebContents();
   1308   if (!inspected_web_contents)
   1309     return;
   1310   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
   1311   if (inspected_window)
   1312     inspected_window->UpdateToolbar(inspected_web_contents, false);
   1313 }
   1314 
   1315 bool DevToolsWindow::IsDocked() {
   1316   return dock_side_ != DEVTOOLS_DOCK_SIDE_UNDOCKED;
   1317 }
   1318 
   1319 void DevToolsWindow::Restore() {
   1320   if (dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
   1321     SetDockSide(SideToString(dock_side_before_minimized_));
   1322 }
   1323 
   1324 content::WebContents* DevToolsWindow::GetInspectedWebContents() {
   1325   return inspected_contents_observer_ ?
   1326       inspected_contents_observer_->web_contents() : NULL;
   1327 }
   1328