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