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/json/json_reader.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/prefs/scoped_user_pref_update.h"
     12 #include "base/time/time.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/chrome_page_zoom.h"
     15 #include "chrome/browser/file_select_helper.h"
     16 #include "chrome/browser/infobars/infobar_service.h"
     17 #include "chrome/browser/prefs/pref_service_syncable.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/browser/sessions/session_tab_helper.h"
     20 #include "chrome/browser/ui/browser.h"
     21 #include "chrome/browser/ui/browser_dialogs.h"
     22 #include "chrome/browser/ui/browser_iterator.h"
     23 #include "chrome/browser/ui/browser_list.h"
     24 #include "chrome/browser/ui/browser_window.h"
     25 #include "chrome/browser/ui/host_desktop.h"
     26 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
     27 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     28 #include "chrome/browser/ui/webui/devtools_ui.h"
     29 #include "chrome/browser/ui/zoom/zoom_controller.h"
     30 #include "chrome/common/chrome_switches.h"
     31 #include "chrome/common/pref_names.h"
     32 #include "chrome/common/render_messages.h"
     33 #include "chrome/common/url_constants.h"
     34 #include "components/pref_registry/pref_registry_syncable.h"
     35 #include "content/public/browser/browser_thread.h"
     36 #include "content/public/browser/devtools_agent_host.h"
     37 #include "content/public/browser/native_web_keyboard_event.h"
     38 #include "content/public/browser/navigation_controller.h"
     39 #include "content/public/browser/navigation_entry.h"
     40 #include "content/public/browser/render_frame_host.h"
     41 #include "content/public/browser/render_process_host.h"
     42 #include "content/public/browser/render_view_host.h"
     43 #include "content/public/browser/render_widget_host_view.h"
     44 #include "content/public/browser/user_metrics.h"
     45 #include "content/public/browser/web_contents.h"
     46 #include "content/public/common/content_client.h"
     47 #include "content/public/common/url_constants.h"
     48 #include "third_party/WebKit/public/web/WebInputEvent.h"
     49 #include "ui/base/page_transition_types.h"
     50 #include "ui/events/keycodes/keyboard_codes.h"
     51 
     52 using base::DictionaryValue;
     53 using blink::WebInputEvent;
     54 using content::BrowserThread;
     55 using content::DevToolsAgentHost;
     56 using content::WebContents;
     57 
     58 namespace {
     59 
     60 typedef std::vector<DevToolsWindow*> DevToolsWindows;
     61 base::LazyInstance<DevToolsWindows>::Leaky g_instances =
     62     LAZY_INSTANCE_INITIALIZER;
     63 
     64 static const char kKeyUpEventName[] = "keyup";
     65 static const char kKeyDownEventName[] = "keydown";
     66 
     67 bool FindInspectedBrowserAndTabIndex(
     68     WebContents* inspected_web_contents, Browser** browser, int* tab) {
     69   if (!inspected_web_contents)
     70     return false;
     71 
     72   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
     73     int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
     74         inspected_web_contents);
     75     if (tab_index != TabStripModel::kNoTab) {
     76       *browser = *it;
     77       *tab = tab_index;
     78       return true;
     79     }
     80   }
     81   return false;
     82 }
     83 
     84 // DevToolsToolboxDelegate ----------------------------------------------------
     85 
     86 class DevToolsToolboxDelegate
     87     : public content::WebContentsObserver,
     88       public content::WebContentsDelegate {
     89  public:
     90   DevToolsToolboxDelegate(
     91       WebContents* toolbox_contents,
     92       DevToolsWindow::ObserverWithAccessor* web_contents_observer);
     93   virtual ~DevToolsToolboxDelegate();
     94 
     95   virtual content::WebContents* OpenURLFromTab(
     96       content::WebContents* source,
     97       const content::OpenURLParams& params) OVERRIDE;
     98   virtual bool PreHandleKeyboardEvent(
     99       content::WebContents* source,
    100       const content::NativeWebKeyboardEvent& event,
    101       bool* is_keyboard_shortcut) OVERRIDE;
    102   virtual void HandleKeyboardEvent(
    103       content::WebContents* source,
    104       const content::NativeWebKeyboardEvent& event) OVERRIDE;
    105   virtual void WebContentsDestroyed() OVERRIDE;
    106 
    107  private:
    108   BrowserWindow* GetInspectedBrowserWindow();
    109   DevToolsWindow::ObserverWithAccessor* inspected_contents_observer_;
    110   DISALLOW_COPY_AND_ASSIGN(DevToolsToolboxDelegate);
    111 };
    112 
    113 DevToolsToolboxDelegate::DevToolsToolboxDelegate(
    114     WebContents* toolbox_contents,
    115     DevToolsWindow::ObserverWithAccessor* web_contents_observer)
    116     : WebContentsObserver(toolbox_contents),
    117       inspected_contents_observer_(web_contents_observer) {
    118 }
    119 
    120 DevToolsToolboxDelegate::~DevToolsToolboxDelegate() {
    121 }
    122 
    123 content::WebContents* DevToolsToolboxDelegate::OpenURLFromTab(
    124     content::WebContents* source,
    125     const content::OpenURLParams& params) {
    126   DCHECK(source == web_contents());
    127   if (!params.url.SchemeIs(content::kChromeDevToolsScheme))
    128     return NULL;
    129   content::NavigationController::LoadURLParams load_url_params(params.url);
    130   source->GetController().LoadURLWithParams(load_url_params);
    131   return source;
    132 }
    133 
    134 bool DevToolsToolboxDelegate::PreHandleKeyboardEvent(
    135     content::WebContents* source,
    136     const content::NativeWebKeyboardEvent& event,
    137     bool* is_keyboard_shortcut) {
    138   BrowserWindow* window = GetInspectedBrowserWindow();
    139   if (window)
    140     return window->PreHandleKeyboardEvent(event, is_keyboard_shortcut);
    141   return false;
    142 }
    143 
    144 void DevToolsToolboxDelegate::HandleKeyboardEvent(
    145     content::WebContents* source,
    146     const content::NativeWebKeyboardEvent& event) {
    147   if (event.windowsKeyCode == 0x08) {
    148     // Do not navigate back in history on Windows (http://crbug.com/74156).
    149     return;
    150   }
    151   BrowserWindow* window = GetInspectedBrowserWindow();
    152   if (window)
    153     window->HandleKeyboardEvent(event);
    154 }
    155 
    156 void DevToolsToolboxDelegate::WebContentsDestroyed() {
    157   delete this;
    158 }
    159 
    160 BrowserWindow* DevToolsToolboxDelegate::GetInspectedBrowserWindow() {
    161   WebContents* inspected_contents =
    162       inspected_contents_observer_->web_contents();
    163   if (!inspected_contents)
    164     return NULL;
    165   Browser* browser = NULL;
    166   int tab = 0;
    167   if (FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
    168     return browser->window();
    169   return NULL;
    170 }
    171 
    172 }  // namespace
    173 
    174 // DevToolsEventForwarder -----------------------------------------------------
    175 
    176 class DevToolsEventForwarder {
    177  public:
    178   explicit DevToolsEventForwarder(DevToolsWindow* window)
    179      : devtools_window_(window) {}
    180 
    181   // Registers whitelisted shortcuts with the forwarder.
    182   // Only registered keys will be forwarded to the DevTools frontend.
    183   void SetWhitelistedShortcuts(const std::string& message);
    184 
    185   // Forwards a keyboard event to the DevTools frontend if it is whitelisted.
    186   // Returns |true| if the event has been forwarded, |false| otherwise.
    187   bool ForwardEvent(const content::NativeWebKeyboardEvent& event);
    188 
    189  private:
    190   static int VirtualKeyCodeWithoutLocation(int key_code);
    191   static bool KeyWhitelistingAllowed(int key_code, int modifiers);
    192   static int CombineKeyCodeAndModifiers(int key_code, int modifiers);
    193 
    194   DevToolsWindow* devtools_window_;
    195   std::set<int> whitelisted_keys_;
    196 
    197   DISALLOW_COPY_AND_ASSIGN(DevToolsEventForwarder);
    198 };
    199 
    200 void DevToolsEventForwarder::SetWhitelistedShortcuts(
    201     const std::string& message) {
    202   scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
    203   base::ListValue* shortcut_list;
    204   if (!parsed_message->GetAsList(&shortcut_list))
    205       return;
    206   base::ListValue::iterator it = shortcut_list->begin();
    207   for (; it != shortcut_list->end(); ++it) {
    208     base::DictionaryValue* dictionary;
    209     if (!(*it)->GetAsDictionary(&dictionary))
    210       continue;
    211     int key_code = 0;
    212     dictionary->GetInteger("keyCode", &key_code);
    213     if (key_code == 0)
    214       continue;
    215     int modifiers = 0;
    216     dictionary->GetInteger("modifiers", &modifiers);
    217     if (!KeyWhitelistingAllowed(key_code, modifiers)) {
    218       LOG(WARNING) << "Key whitelisting forbidden: "
    219                    << "(" << key_code << "," << modifiers << ")";
    220       continue;
    221     }
    222     whitelisted_keys_.insert(CombineKeyCodeAndModifiers(key_code, modifiers));
    223   }
    224 }
    225 
    226 bool DevToolsEventForwarder::ForwardEvent(
    227     const content::NativeWebKeyboardEvent& event) {
    228   std::string event_type;
    229   switch (event.type) {
    230     case WebInputEvent::KeyDown:
    231     case WebInputEvent::RawKeyDown:
    232       event_type = kKeyDownEventName;
    233       break;
    234     case WebInputEvent::KeyUp:
    235       event_type = kKeyUpEventName;
    236       break;
    237     default:
    238       return false;
    239   }
    240 
    241   int key_code = VirtualKeyCodeWithoutLocation(event.windowsKeyCode);
    242   int key = CombineKeyCodeAndModifiers(key_code, event.modifiers);
    243   if (whitelisted_keys_.find(key) == whitelisted_keys_.end())
    244     return false;
    245 
    246   base::DictionaryValue event_data;
    247   event_data.SetString("type", event_type);
    248   event_data.SetString("keyIdentifier", event.keyIdentifier);
    249   event_data.SetInteger("keyCode", key_code);
    250   event_data.SetInteger("modifiers", event.modifiers);
    251   devtools_window_->bindings_->CallClientFunction(
    252       "InspectorFrontendAPI.keyEventUnhandled", &event_data, NULL, NULL);
    253   return true;
    254 }
    255 
    256 int DevToolsEventForwarder::CombineKeyCodeAndModifiers(int key_code,
    257                                                        int modifiers) {
    258   return key_code | (modifiers << 16);
    259 }
    260 
    261 bool DevToolsEventForwarder::KeyWhitelistingAllowed(int key_code,
    262                                                     int modifiers) {
    263   return (ui::VKEY_F1 <= key_code && key_code <= ui::VKEY_F12) ||
    264       modifiers != 0;
    265 }
    266 
    267 // Mapping copied from Blink's KeyboardEvent.cpp.
    268 int DevToolsEventForwarder::VirtualKeyCodeWithoutLocation(int key_code)
    269 {
    270   switch (key_code) {
    271     case ui::VKEY_LCONTROL:
    272     case ui::VKEY_RCONTROL:
    273         return ui::VKEY_CONTROL;
    274     case ui::VKEY_LSHIFT:
    275     case ui::VKEY_RSHIFT:
    276         return ui::VKEY_SHIFT;
    277     case ui::VKEY_LMENU:
    278     case ui::VKEY_RMENU:
    279         return ui::VKEY_MENU;
    280     default:
    281         return key_code;
    282   }
    283 }
    284 
    285 // DevToolsWindow::ObserverWithAccessor -------------------------------
    286 
    287 DevToolsWindow::ObserverWithAccessor::ObserverWithAccessor(
    288     WebContents* web_contents)
    289     : WebContentsObserver(web_contents) {
    290 }
    291 
    292 DevToolsWindow::ObserverWithAccessor::~ObserverWithAccessor() {
    293 }
    294 
    295 // DevToolsWindow -------------------------------------------------------------
    296 
    297 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
    298 
    299 DevToolsWindow::~DevToolsWindow() {
    300   life_stage_ = kClosing;
    301 
    302   UpdateBrowserWindow();
    303   UpdateBrowserToolbar();
    304 
    305   if (toolbox_web_contents_)
    306     delete toolbox_web_contents_;
    307 
    308   DevToolsWindows* instances = g_instances.Pointer();
    309   DevToolsWindows::iterator it(
    310       std::find(instances->begin(), instances->end(), this));
    311   DCHECK(it != instances->end());
    312   instances->erase(it);
    313 
    314   if (!close_callback_.is_null()) {
    315     close_callback_.Run();
    316     close_callback_ = base::Closure();
    317   }
    318 }
    319 
    320 // static
    321 void DevToolsWindow::RegisterProfilePrefs(
    322     user_prefs::PrefRegistrySyncable* registry) {
    323   registry->RegisterDictionaryPref(
    324       prefs::kDevToolsEditedFiles,
    325       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    326   registry->RegisterDictionaryPref(
    327       prefs::kDevToolsFileSystemPaths,
    328       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    329   registry->RegisterStringPref(
    330       prefs::kDevToolsAdbKey, std::string(),
    331       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    332 
    333   registry->RegisterBooleanPref(
    334       prefs::kDevToolsDiscoverUsbDevicesEnabled,
    335       true,
    336       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    337   registry->RegisterBooleanPref(
    338       prefs::kDevToolsPortForwardingEnabled,
    339       false,
    340       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    341   registry->RegisterBooleanPref(
    342       prefs::kDevToolsPortForwardingDefaultSet,
    343       false,
    344       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    345   registry->RegisterDictionaryPref(
    346       prefs::kDevToolsPortForwardingConfig,
    347       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    348 }
    349 
    350 // static
    351 content::WebContents* DevToolsWindow::GetInTabWebContents(
    352     WebContents* inspected_web_contents,
    353     DevToolsContentsResizingStrategy* out_strategy) {
    354   DevToolsWindow* window = GetInstanceForInspectedWebContents(
    355       inspected_web_contents);
    356   if (!window || window->life_stage_ == kClosing)
    357     return NULL;
    358 
    359   // Not yet loaded window is treated as docked, but we should not present it
    360   // until we decided on docking.
    361   bool is_docked_set = window->life_stage_ == kLoadCompleted ||
    362       window->life_stage_ == kIsDockedSet;
    363   if (!is_docked_set)
    364     return NULL;
    365 
    366   // Undocked window should have toolbox web contents.
    367   if (!window->is_docked_ && !window->toolbox_web_contents_)
    368     return NULL;
    369 
    370   if (out_strategy)
    371     out_strategy->CopyFrom(window->contents_resizing_strategy_);
    372 
    373   return window->is_docked_ ? window->main_web_contents_ :
    374       window->toolbox_web_contents_;
    375 }
    376 
    377 // static
    378 DevToolsWindow* DevToolsWindow::GetInstanceForInspectedWebContents(
    379     WebContents* inspected_web_contents) {
    380   if (!inspected_web_contents || g_instances == NULL)
    381     return NULL;
    382   DevToolsWindows* instances = g_instances.Pointer();
    383   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
    384        ++it) {
    385     if ((*it)->GetInspectedWebContents() == inspected_web_contents)
    386       return *it;
    387   }
    388   return NULL;
    389 }
    390 
    391 // static
    392 bool DevToolsWindow::IsDevToolsWindow(content::WebContents* web_contents) {
    393   if (!web_contents || g_instances == NULL)
    394     return false;
    395   DevToolsWindows* instances = g_instances.Pointer();
    396   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
    397        ++it) {
    398     if ((*it)->main_web_contents_ == web_contents ||
    399         (*it)->toolbox_web_contents_ == web_contents)
    400       return true;
    401   }
    402   return false;
    403 }
    404 
    405 // static
    406 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker(
    407     Profile* profile,
    408     const scoped_refptr<DevToolsAgentHost>& worker_agent) {
    409   DevToolsWindow* window = FindDevToolsWindow(worker_agent.get());
    410   if (!window) {
    411     window = DevToolsWindow::CreateDevToolsWindowForWorker(profile);
    412     window->bindings_->AttachTo(worker_agent);
    413   }
    414   window->ScheduleShow(DevToolsToggleAction::Show());
    415   return window;
    416 }
    417 
    418 // static
    419 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
    420     Profile* profile) {
    421   content::RecordAction(base::UserMetricsAction("DevTools_InspectWorker"));
    422   return Create(profile, GURL(), NULL, true, false, false, "");
    423 }
    424 
    425 // static
    426 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
    427     content::WebContents* inspected_web_contents) {
    428   return ToggleDevToolsWindow(
    429       inspected_web_contents, true, DevToolsToggleAction::Show(), "");
    430 }
    431 
    432 // static
    433 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
    434     content::WebContents* inspected_web_contents,
    435     const DevToolsToggleAction& action) {
    436   return ToggleDevToolsWindow(inspected_web_contents, true, action, "");
    437 }
    438 
    439 // static
    440 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
    441     Browser* browser,
    442     const DevToolsToggleAction& action) {
    443   if (action.type() == DevToolsToggleAction::kToggle &&
    444       browser->is_devtools()) {
    445     browser->tab_strip_model()->CloseAllTabs();
    446     return NULL;
    447   }
    448 
    449   return ToggleDevToolsWindow(
    450       browser->tab_strip_model()->GetActiveWebContents(),
    451       action.type() == DevToolsToggleAction::kInspect,
    452       action, "");
    453 }
    454 
    455 // static
    456 void DevToolsWindow::OpenExternalFrontend(
    457     Profile* profile,
    458     const std::string& frontend_url,
    459     const scoped_refptr<content::DevToolsAgentHost>& agent_host,
    460     bool isWorker) {
    461   DevToolsWindow* window = FindDevToolsWindow(agent_host.get());
    462   if (!window) {
    463     window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL,
    464                     isWorker, true, false, "");
    465     window->bindings_->AttachTo(agent_host);
    466   }
    467   window->ScheduleShow(DevToolsToggleAction::Show());
    468 }
    469 
    470 // static
    471 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
    472     content::WebContents* inspected_web_contents,
    473     bool force_open,
    474     const DevToolsToggleAction& action,
    475     const std::string& settings) {
    476   scoped_refptr<DevToolsAgentHost> agent(
    477       DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
    478   DevToolsWindow* window = FindDevToolsWindow(agent.get());
    479   bool do_open = force_open;
    480   if (!window) {
    481     Profile* profile = Profile::FromBrowserContext(
    482         inspected_web_contents->GetBrowserContext());
    483     content::RecordAction(
    484         base::UserMetricsAction("DevTools_InspectRenderer"));
    485     window = Create(
    486         profile, GURL(), inspected_web_contents, false, false, true, settings);
    487     window->bindings_->AttachTo(agent.get());
    488     do_open = true;
    489   }
    490 
    491   // Update toolbar to reflect DevTools changes.
    492   window->UpdateBrowserToolbar();
    493 
    494   // If window is docked and visible, we hide it on toggle. If window is
    495   // undocked, we show (activate) it.
    496   if (!window->is_docked_ || do_open)
    497     window->ScheduleShow(action);
    498   else
    499     window->CloseWindow();
    500 
    501   return window;
    502 }
    503 
    504 // static
    505 void DevToolsWindow::InspectElement(
    506     content::WebContents* inspected_web_contents,
    507     int x,
    508     int y) {
    509   scoped_refptr<DevToolsAgentHost> agent(
    510       DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
    511   agent->InspectElement(x, y);
    512   bool should_measure_time = FindDevToolsWindow(agent.get()) == NULL;
    513   base::TimeTicks start_time = base::TimeTicks::Now();
    514   // TODO(loislo): we should initiate DevTools window opening from within
    515   // renderer. Otherwise, we still can hit a race condition here.
    516   DevToolsWindow* window = OpenDevToolsWindow(inspected_web_contents);
    517   if (should_measure_time)
    518     window->inspect_element_start_time_ = start_time;
    519 }
    520 
    521 void DevToolsWindow::ScheduleShow(const DevToolsToggleAction& action) {
    522   if (life_stage_ == kLoadCompleted) {
    523     Show(action);
    524     return;
    525   }
    526 
    527   // Action will be done only after load completed.
    528   action_on_load_ = action;
    529 
    530   if (!can_dock_) {
    531     // No harm to show always-undocked window right away.
    532     is_docked_ = false;
    533     Show(DevToolsToggleAction::Show());
    534   }
    535 }
    536 
    537 void DevToolsWindow::Show(const DevToolsToggleAction& action) {
    538   if (life_stage_ == kClosing)
    539     return;
    540 
    541   if (action.type() == DevToolsToggleAction::kNoOp)
    542     return;
    543 
    544   if (is_docked_) {
    545     DCHECK(can_dock_);
    546     Browser* inspected_browser = NULL;
    547     int inspected_tab_index = -1;
    548     FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
    549                                     &inspected_browser,
    550                                     &inspected_tab_index);
    551     DCHECK(inspected_browser);
    552     DCHECK(inspected_tab_index != -1);
    553 
    554     // Tell inspected browser to update splitter and switch to inspected panel.
    555     BrowserWindow* inspected_window = inspected_browser->window();
    556     main_web_contents_->SetDelegate(this);
    557 
    558     TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
    559     tab_strip_model->ActivateTabAt(inspected_tab_index, true);
    560 
    561     inspected_window->UpdateDevTools();
    562     main_web_contents_->SetInitialFocus();
    563     inspected_window->Show();
    564     // On Aura, focusing once is not enough. Do it again.
    565     // Note that focusing only here but not before isn't enough either. We just
    566     // need to focus twice.
    567     main_web_contents_->SetInitialFocus();
    568 
    569     PrefsTabHelper::CreateForWebContents(main_web_contents_);
    570     main_web_contents_->GetRenderViewHost()->SyncRendererPrefs();
    571 
    572     DoAction(action);
    573     return;
    574   }
    575 
    576   // Avoid consecutive window switching if the devtools window has been opened
    577   // and the Inspect Element shortcut is pressed in the inspected tab.
    578   bool should_show_window =
    579       !browser_ || (action.type() != DevToolsToggleAction::kInspect);
    580 
    581   if (!browser_)
    582     CreateDevToolsBrowser();
    583 
    584   if (should_show_window) {
    585     browser_->window()->Show();
    586     main_web_contents_->SetInitialFocus();
    587   }
    588   if (toolbox_web_contents_)
    589     UpdateBrowserWindow();
    590 
    591   DoAction(action);
    592 }
    593 
    594 // static
    595 bool DevToolsWindow::HandleBeforeUnload(WebContents* frontend_contents,
    596     bool proceed, bool* proceed_to_fire_unload) {
    597   DevToolsWindow* window = AsDevToolsWindow(frontend_contents);
    598   if (!window)
    599     return false;
    600   if (!window->intercepted_page_beforeunload_)
    601     return false;
    602   window->BeforeUnloadFired(frontend_contents, proceed,
    603       proceed_to_fire_unload);
    604   return true;
    605 }
    606 
    607 // static
    608 bool DevToolsWindow::InterceptPageBeforeUnload(WebContents* contents) {
    609   DevToolsWindow* window =
    610       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
    611   if (!window || window->intercepted_page_beforeunload_)
    612     return false;
    613 
    614   // Not yet loaded frontend will not handle beforeunload.
    615   if (window->life_stage_ != kLoadCompleted)
    616     return false;
    617 
    618   window->intercepted_page_beforeunload_ = true;
    619   // Handle case of devtools inspecting another devtools instance by passing
    620   // the call up to the inspecting devtools instance.
    621   if (!DevToolsWindow::InterceptPageBeforeUnload(window->main_web_contents_)) {
    622     window->main_web_contents_->DispatchBeforeUnload(false);
    623   }
    624   return true;
    625 }
    626 
    627 // static
    628 bool DevToolsWindow::NeedsToInterceptBeforeUnload(
    629     WebContents* contents) {
    630   DevToolsWindow* window =
    631       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
    632   return window && !window->intercepted_page_beforeunload_;
    633 }
    634 
    635 // static
    636 bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(
    637     Browser* browser) {
    638   DCHECK(browser->is_devtools());
    639   // When FastUnloadController is used, devtools frontend will be detached
    640   // from the browser window at this point which means we've already fired
    641   // beforeunload.
    642   if (browser->tab_strip_model()->empty())
    643     return true;
    644   WebContents* contents =
    645       browser->tab_strip_model()->GetWebContentsAt(0);
    646   DevToolsWindow* window = AsDevToolsWindow(contents);
    647   if (!window)
    648     return false;
    649   return window->intercepted_page_beforeunload_;
    650 }
    651 
    652 // static
    653 void DevToolsWindow::OnPageCloseCanceled(WebContents* contents) {
    654   DevToolsWindow *window =
    655       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
    656   if (!window)
    657     return;
    658   window->intercepted_page_beforeunload_ = false;
    659   // Propagate to devtools opened on devtools if any.
    660   DevToolsWindow::OnPageCloseCanceled(window->main_web_contents_);
    661 }
    662 
    663 DevToolsWindow::DevToolsWindow(Profile* profile,
    664                                const GURL& url,
    665                                content::WebContents* inspected_web_contents,
    666                                bool can_dock)
    667     : profile_(profile),
    668       main_web_contents_(
    669           WebContents::Create(WebContents::CreateParams(profile))),
    670       toolbox_web_contents_(NULL),
    671       bindings_(NULL),
    672       browser_(NULL),
    673       is_docked_(true),
    674       can_dock_(can_dock),
    675       // This initialization allows external front-end to work without changes.
    676       // We don't wait for docking call, but instead immediately show undocked.
    677       // Passing "dockSide=undocked" parameter ensures proper UI.
    678       life_stage_(can_dock ? kNotLoaded : kIsDockedSet),
    679       action_on_load_(DevToolsToggleAction::NoOp()),
    680       intercepted_page_beforeunload_(false) {
    681   // Set up delegate, so we get fully-functional window immediately.
    682   // It will not appear in UI though until |life_stage_ == kLoadCompleted|.
    683   main_web_contents_->SetDelegate(this);
    684 
    685   main_web_contents_->GetController().LoadURL(
    686       DevToolsUIBindings::ApplyThemeToURL(profile, url), content::Referrer(),
    687       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
    688 
    689   bindings_ = DevToolsUIBindings::ForWebContents(main_web_contents_);
    690   DCHECK(bindings_);
    691 
    692   // Bindings take ownership over devtools as its delegate.
    693   bindings_->SetDelegate(this);
    694   // DevTools uses chrome_page_zoom::Zoom(), so main_web_contents_ requires a
    695   // ZoomController.
    696   ZoomController::CreateForWebContents(main_web_contents_);
    697   ZoomController::FromWebContents(main_web_contents_)
    698       ->SetShowsNotificationBubble(false);
    699 
    700   g_instances.Get().push_back(this);
    701 
    702   // There is no inspected_web_contents in case of various workers.
    703   if (inspected_web_contents)
    704     inspected_contents_observer_.reset(
    705         new ObserverWithAccessor(inspected_web_contents));
    706 
    707   // Initialize docked page to be of the right size.
    708   if (can_dock_ && inspected_web_contents) {
    709     content::RenderWidgetHostView* inspected_view =
    710         inspected_web_contents->GetRenderWidgetHostView();
    711     if (inspected_view && main_web_contents_->GetRenderWidgetHostView()) {
    712       gfx::Size size = inspected_view->GetViewBounds().size();
    713       main_web_contents_->GetRenderWidgetHostView()->SetSize(size);
    714     }
    715   }
    716 
    717   event_forwarder_.reset(new DevToolsEventForwarder(this));
    718 }
    719 
    720 // static
    721 DevToolsWindow* DevToolsWindow::Create(
    722     Profile* profile,
    723     const GURL& frontend_url,
    724     content::WebContents* inspected_web_contents,
    725     bool shared_worker_frontend,
    726     bool external_frontend,
    727     bool can_dock,
    728     const std::string& settings) {
    729   if (inspected_web_contents) {
    730     // Check for a place to dock.
    731     Browser* browser = NULL;
    732     int tab;
    733     if (!FindInspectedBrowserAndTabIndex(inspected_web_contents,
    734                                          &browser, &tab) ||
    735         browser->is_type_popup()) {
    736       can_dock = false;
    737     }
    738   }
    739 
    740   // Create WebContents with devtools.
    741   GURL url(GetDevToolsURL(profile, frontend_url,
    742                           shared_worker_frontend,
    743                           external_frontend,
    744                           can_dock, settings));
    745   return new DevToolsWindow(profile, url, inspected_web_contents, can_dock);
    746 }
    747 
    748 // static
    749 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
    750                                     const GURL& base_url,
    751                                     bool shared_worker_frontend,
    752                                     bool external_frontend,
    753                                     bool can_dock,
    754                                     const std::string& settings) {
    755   // Compatibility errors are encoded with data urls, pass them
    756   // through with no decoration.
    757   if (base_url.SchemeIs("data"))
    758     return base_url;
    759 
    760   std::string frontend_url(
    761       base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec());
    762   std::string url_string(
    763       frontend_url +
    764       ((frontend_url.find("?") == std::string::npos) ? "?" : "&"));
    765   if (shared_worker_frontend)
    766     url_string += "&isSharedWorker=true";
    767   if (external_frontend)
    768     url_string += "&remoteFrontend=true";
    769   if (can_dock)
    770     url_string += "&can_dock=true";
    771   if (settings.size())
    772     url_string += "&settings=" + settings;
    773   return GURL(url_string);
    774 }
    775 
    776 // static
    777 DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
    778     DevToolsAgentHost* agent_host) {
    779   if (!agent_host || g_instances == NULL)
    780     return NULL;
    781   DevToolsWindows* instances = g_instances.Pointer();
    782   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
    783        ++it) {
    784     if ((*it)->bindings_->IsAttachedTo(agent_host))
    785       return *it;
    786   }
    787   return NULL;
    788 }
    789 
    790 // static
    791 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
    792     content::WebContents* web_contents) {
    793   if (!web_contents || g_instances == NULL)
    794     return NULL;
    795   DevToolsWindows* instances = g_instances.Pointer();
    796   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
    797        ++it) {
    798     if ((*it)->main_web_contents_ == web_contents)
    799       return *it;
    800   }
    801   return NULL;
    802 }
    803 
    804 WebContents* DevToolsWindow::OpenURLFromTab(
    805     WebContents* source,
    806     const content::OpenURLParams& params) {
    807   DCHECK(source == main_web_contents_);
    808   if (!params.url.SchemeIs(content::kChromeDevToolsScheme)) {
    809     WebContents* inspected_web_contents = GetInspectedWebContents();
    810     return inspected_web_contents ?
    811         inspected_web_contents->OpenURL(params) : NULL;
    812   }
    813 
    814   bindings_->Reattach();
    815 
    816   content::NavigationController::LoadURLParams load_url_params(params.url);
    817   main_web_contents_->GetController().LoadURLWithParams(load_url_params);
    818   return main_web_contents_;
    819 }
    820 
    821 void DevToolsWindow::ActivateContents(WebContents* contents) {
    822   if (is_docked_) {
    823     WebContents* inspected_tab = GetInspectedWebContents();
    824     inspected_tab->GetDelegate()->ActivateContents(inspected_tab);
    825   } else {
    826     browser_->window()->Activate();
    827   }
    828 }
    829 
    830 void DevToolsWindow::AddNewContents(WebContents* source,
    831                                     WebContents* new_contents,
    832                                     WindowOpenDisposition disposition,
    833                                     const gfx::Rect& initial_pos,
    834                                     bool user_gesture,
    835                                     bool* was_blocked) {
    836   if (new_contents == toolbox_web_contents_) {
    837     toolbox_web_contents_->SetDelegate(
    838         new DevToolsToolboxDelegate(toolbox_web_contents_,
    839                                     inspected_contents_observer_.get()));
    840     if (main_web_contents_->GetRenderWidgetHostView() &&
    841         toolbox_web_contents_->GetRenderWidgetHostView()) {
    842       gfx::Size size =
    843           main_web_contents_->GetRenderWidgetHostView()->GetViewBounds().size();
    844       toolbox_web_contents_->GetRenderWidgetHostView()->SetSize(size);
    845     }
    846     UpdateBrowserWindow();
    847     return;
    848   }
    849 
    850   WebContents* inspected_web_contents = GetInspectedWebContents();
    851   if (inspected_web_contents) {
    852     inspected_web_contents->GetDelegate()->AddNewContents(
    853         source, new_contents, disposition, initial_pos, user_gesture,
    854         was_blocked);
    855   }
    856 }
    857 
    858 void DevToolsWindow::WebContentsCreated(WebContents* source_contents,
    859                                         int opener_render_frame_id,
    860                                         const base::string16& frame_name,
    861                                         const GURL& target_url,
    862                                         WebContents* new_contents) {
    863   if (target_url.SchemeIs(content::kChromeDevToolsScheme) &&
    864       target_url.query().find("toolbox=true") != std::string::npos) {
    865     CHECK(can_dock_);
    866     toolbox_web_contents_ = new_contents;
    867   }
    868 }
    869 
    870 void DevToolsWindow::CloseContents(WebContents* source) {
    871   CHECK(is_docked_);
    872   life_stage_ = kClosing;
    873   UpdateBrowserWindow();
    874   // In case of docked main_web_contents_, we own it so delete here.
    875   // Embedding DevTools window will be deleted as a result of
    876   // DevToolsUIBindings destruction.
    877   delete main_web_contents_;
    878 }
    879 
    880 void DevToolsWindow::ContentsZoomChange(bool zoom_in) {
    881   DCHECK(is_docked_);
    882   chrome_page_zoom::Zoom(main_web_contents_,
    883       zoom_in ? content::PAGE_ZOOM_IN : content::PAGE_ZOOM_OUT);
    884 }
    885 
    886 void DevToolsWindow::BeforeUnloadFired(WebContents* tab,
    887                                        bool proceed,
    888                                        bool* proceed_to_fire_unload) {
    889   if (!intercepted_page_beforeunload_) {
    890     // Docked devtools window closed directly.
    891     if (proceed)
    892       bindings_->Detach();
    893     *proceed_to_fire_unload = proceed;
    894   } else {
    895     // Inspected page is attempting to close.
    896     WebContents* inspected_web_contents = GetInspectedWebContents();
    897     if (proceed) {
    898       inspected_web_contents->DispatchBeforeUnload(false);
    899     } else {
    900       bool should_proceed;
    901       inspected_web_contents->GetDelegate()->BeforeUnloadFired(
    902           inspected_web_contents, false, &should_proceed);
    903       DCHECK(!should_proceed);
    904     }
    905     *proceed_to_fire_unload = false;
    906   }
    907 }
    908 
    909 bool DevToolsWindow::PreHandleKeyboardEvent(
    910     WebContents* source,
    911     const content::NativeWebKeyboardEvent& event,
    912     bool* is_keyboard_shortcut) {
    913   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
    914   if (inspected_window) {
    915     return inspected_window->PreHandleKeyboardEvent(event,
    916                                                     is_keyboard_shortcut);
    917   }
    918   return false;
    919 }
    920 
    921 void DevToolsWindow::HandleKeyboardEvent(
    922     WebContents* source,
    923     const content::NativeWebKeyboardEvent& event) {
    924   if (event.windowsKeyCode == 0x08) {
    925     // Do not navigate back in history on Windows (http://crbug.com/74156).
    926     return;
    927   }
    928   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
    929   if (inspected_window)
    930     inspected_window->HandleKeyboardEvent(event);
    931 }
    932 
    933 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
    934   WebContents* inspected_web_contents = GetInspectedWebContents();
    935   return (inspected_web_contents && inspected_web_contents->GetDelegate()) ?
    936       inspected_web_contents->GetDelegate()->GetJavaScriptDialogManager() :
    937       content::WebContentsDelegate::GetJavaScriptDialogManager();
    938 }
    939 
    940 content::ColorChooser* DevToolsWindow::OpenColorChooser(
    941     WebContents* web_contents,
    942     SkColor initial_color,
    943     const std::vector<content::ColorSuggestion>& suggestions) {
    944   return chrome::ShowColorChooser(web_contents, initial_color);
    945 }
    946 
    947 void DevToolsWindow::RunFileChooser(WebContents* web_contents,
    948                                     const content::FileChooserParams& params) {
    949   FileSelectHelper::RunFileChooser(web_contents, params);
    950 }
    951 
    952 void DevToolsWindow::WebContentsFocused(WebContents* contents) {
    953   Browser* inspected_browser = NULL;
    954   int inspected_tab_index = -1;
    955   if (is_docked_ && FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
    956                                                     &inspected_browser,
    957                                                     &inspected_tab_index))
    958     inspected_browser->window()->WebContentsFocused(contents);
    959 }
    960 
    961 bool DevToolsWindow::PreHandleGestureEvent(
    962     WebContents* source,
    963     const blink::WebGestureEvent& event) {
    964   // Disable pinch zooming.
    965   return event.type == blink::WebGestureEvent::GesturePinchBegin ||
    966       event.type == blink::WebGestureEvent::GesturePinchUpdate ||
    967       event.type == blink::WebGestureEvent::GesturePinchEnd;
    968 }
    969 
    970 void DevToolsWindow::ActivateWindow() {
    971   if (is_docked_ && GetInspectedBrowserWindow())
    972     main_web_contents_->Focus();
    973   else if (!is_docked_ && !browser_->window()->IsActive())
    974     browser_->window()->Activate();
    975 }
    976 
    977 void DevToolsWindow::CloseWindow() {
    978   DCHECK(is_docked_);
    979   life_stage_ = kClosing;
    980   main_web_contents_->DispatchBeforeUnload(false);
    981 }
    982 
    983 void DevToolsWindow::SetInspectedPageBounds(const gfx::Rect& rect) {
    984   DevToolsContentsResizingStrategy strategy(rect);
    985   if (contents_resizing_strategy_.Equals(strategy))
    986     return;
    987 
    988   contents_resizing_strategy_.CopyFrom(strategy);
    989   UpdateBrowserWindow();
    990 }
    991 
    992 void DevToolsWindow::InspectElementCompleted() {
    993   if (!inspect_element_start_time_.is_null()) {
    994     UMA_HISTOGRAM_TIMES("DevTools.InspectElement",
    995         base::TimeTicks::Now() - inspect_element_start_time_);
    996     inspect_element_start_time_ = base::TimeTicks();
    997   }
    998 }
    999 
   1000 void DevToolsWindow::MoveWindow(int x, int y) {
   1001   if (!is_docked_) {
   1002     gfx::Rect bounds = browser_->window()->GetBounds();
   1003     bounds.Offset(x, y);
   1004     browser_->window()->SetBounds(bounds);
   1005   }
   1006 }
   1007 
   1008 void DevToolsWindow::SetIsDocked(bool dock_requested) {
   1009   if (life_stage_ == kClosing)
   1010     return;
   1011 
   1012   DCHECK(can_dock_ || !dock_requested);
   1013   if (!can_dock_)
   1014     dock_requested = false;
   1015 
   1016   bool was_docked = is_docked_;
   1017   is_docked_ = dock_requested;
   1018 
   1019   if (life_stage_ != kLoadCompleted) {
   1020     // This is a first time call we waited for to initialize.
   1021     life_stage_ = life_stage_ == kOnLoadFired ? kLoadCompleted : kIsDockedSet;
   1022     if (life_stage_ == kLoadCompleted)
   1023       LoadCompleted();
   1024     return;
   1025   }
   1026 
   1027   if (dock_requested == was_docked)
   1028     return;
   1029 
   1030   if (dock_requested && !was_docked) {
   1031     // Detach window from the external devtools browser. It will lead to
   1032     // the browser object's close and delete. Remove observer first.
   1033     TabStripModel* tab_strip_model = browser_->tab_strip_model();
   1034     tab_strip_model->DetachWebContentsAt(
   1035         tab_strip_model->GetIndexOfWebContents(main_web_contents_));
   1036     browser_ = NULL;
   1037   } else if (!dock_requested && was_docked) {
   1038     UpdateBrowserWindow();
   1039   }
   1040 
   1041   Show(DevToolsToggleAction::Show());
   1042 }
   1043 
   1044 void DevToolsWindow::OpenInNewTab(const std::string& url) {
   1045   content::OpenURLParams params(
   1046       GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
   1047       ui::PAGE_TRANSITION_LINK, false);
   1048   WebContents* inspected_web_contents = GetInspectedWebContents();
   1049   if (inspected_web_contents) {
   1050     inspected_web_contents->OpenURL(params);
   1051   } else {
   1052     chrome::HostDesktopType host_desktop_type;
   1053     if (browser_) {
   1054       host_desktop_type = browser_->host_desktop_type();
   1055     } else {
   1056       // There should always be a browser when there are no inspected web
   1057       // contents.
   1058       NOTREACHED();
   1059       host_desktop_type = chrome::GetActiveDesktop();
   1060     }
   1061 
   1062     const BrowserList* browser_list =
   1063         BrowserList::GetInstance(host_desktop_type);
   1064     for (BrowserList::const_iterator it = browser_list->begin();
   1065          it != browser_list->end(); ++it) {
   1066       if ((*it)->type() == Browser::TYPE_TABBED) {
   1067         (*it)->OpenURL(params);
   1068         break;
   1069       }
   1070     }
   1071   }
   1072 }
   1073 
   1074 void DevToolsWindow::SetWhitelistedShortcuts(
   1075     const std::string& message) {
   1076   event_forwarder_->SetWhitelistedShortcuts(message);
   1077 }
   1078 
   1079 void DevToolsWindow::InspectedContentsClosing() {
   1080   intercepted_page_beforeunload_ = false;
   1081   life_stage_ = kClosing;
   1082   main_web_contents_->GetRenderViewHost()->ClosePage();
   1083 }
   1084 
   1085 InfoBarService* DevToolsWindow::GetInfoBarService() {
   1086   return is_docked_ ?
   1087       InfoBarService::FromWebContents(GetInspectedWebContents()) :
   1088       InfoBarService::FromWebContents(main_web_contents_);
   1089 }
   1090 
   1091 void DevToolsWindow::RenderProcessGone() {
   1092   // Docked DevToolsWindow owns its main_web_contents_ and must delete it.
   1093   // Undocked main_web_contents_ are owned and handled by browser.
   1094   // see crbug.com/369932
   1095   if (is_docked_)
   1096     CloseContents(main_web_contents_);
   1097 }
   1098 
   1099 void DevToolsWindow::OnLoadCompleted() {
   1100   // First seed inspected tab id for extension APIs.
   1101   WebContents* inspected_web_contents = GetInspectedWebContents();
   1102   if (inspected_web_contents) {
   1103     SessionTabHelper* session_tab_helper =
   1104         SessionTabHelper::FromWebContents(inspected_web_contents);
   1105     if (session_tab_helper) {
   1106       base::FundamentalValue tabId(session_tab_helper->session_id().id());
   1107       bindings_->CallClientFunction("WebInspector.setInspectedTabId",
   1108           &tabId, NULL, NULL);
   1109     }
   1110   }
   1111 
   1112   if (life_stage_ == kClosing)
   1113     return;
   1114 
   1115   // We could be in kLoadCompleted state already if frontend reloads itself.
   1116   if (life_stage_ != kLoadCompleted) {
   1117     // Load is completed when both kIsDockedSet and kOnLoadFired happened.
   1118     // Here we set kOnLoadFired.
   1119     life_stage_ = life_stage_ == kIsDockedSet ? kLoadCompleted : kOnLoadFired;
   1120   }
   1121   if (life_stage_ == kLoadCompleted)
   1122     LoadCompleted();
   1123 }
   1124 
   1125 void DevToolsWindow::CreateDevToolsBrowser() {
   1126   PrefService* prefs = profile_->GetPrefs();
   1127   if (!prefs->GetDictionary(prefs::kAppWindowPlacement)->HasKey(kDevToolsApp)) {
   1128     DictionaryPrefUpdate update(prefs, prefs::kAppWindowPlacement);
   1129     base::DictionaryValue* wp_prefs = update.Get();
   1130     base::DictionaryValue* dev_tools_defaults = new base::DictionaryValue;
   1131     wp_prefs->Set(kDevToolsApp, dev_tools_defaults);
   1132     dev_tools_defaults->SetInteger("left", 100);
   1133     dev_tools_defaults->SetInteger("top", 100);
   1134     dev_tools_defaults->SetInteger("right", 740);
   1135     dev_tools_defaults->SetInteger("bottom", 740);
   1136     dev_tools_defaults->SetBoolean("maximized", false);
   1137     dev_tools_defaults->SetBoolean("always_on_top", false);
   1138   }
   1139 
   1140   browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
   1141       profile_,
   1142       chrome::GetHostDesktopTypeForNativeView(
   1143           main_web_contents_->GetNativeView())));
   1144   browser_->tab_strip_model()->AddWebContents(
   1145       main_web_contents_, -1, ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
   1146       TabStripModel::ADD_ACTIVE);
   1147   main_web_contents_->GetRenderViewHost()->SyncRendererPrefs();
   1148 }
   1149 
   1150 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
   1151   Browser* browser = NULL;
   1152   int tab;
   1153   return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
   1154                                          &browser, &tab) ?
   1155       browser->window() : NULL;
   1156 }
   1157 
   1158 void DevToolsWindow::DoAction(const DevToolsToggleAction& action) {
   1159   switch (action.type()) {
   1160     case DevToolsToggleAction::kShowConsole:
   1161       bindings_->CallClientFunction(
   1162           "InspectorFrontendAPI.showConsole", NULL, NULL, NULL);
   1163       break;
   1164 
   1165     case DevToolsToggleAction::kInspect:
   1166       bindings_->CallClientFunction(
   1167           "InspectorFrontendAPI.enterInspectElementMode", NULL, NULL, NULL);
   1168       break;
   1169 
   1170     case DevToolsToggleAction::kShow:
   1171     case DevToolsToggleAction::kToggle:
   1172       // Do nothing.
   1173       break;
   1174 
   1175     case DevToolsToggleAction::kReveal: {
   1176       const DevToolsToggleAction::RevealParams* params =
   1177           action.params();
   1178       CHECK(params);
   1179       base::StringValue url_value(params->url);
   1180       base::FundamentalValue line_value(static_cast<int>(params->line_number));
   1181       base::FundamentalValue column_value(
   1182           static_cast<int>(params->column_number));
   1183       bindings_->CallClientFunction("InspectorFrontendAPI.revealSourceLine",
   1184                                     &url_value, &line_value, &column_value);
   1185       break;
   1186     }
   1187     default:
   1188       NOTREACHED();
   1189       break;
   1190   }
   1191 }
   1192 
   1193 void DevToolsWindow::UpdateBrowserToolbar() {
   1194   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
   1195   if (inspected_window)
   1196     inspected_window->UpdateToolbar(NULL);
   1197 }
   1198 
   1199 void DevToolsWindow::UpdateBrowserWindow() {
   1200   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
   1201   if (inspected_window)
   1202     inspected_window->UpdateDevTools();
   1203 }
   1204 
   1205 WebContents* DevToolsWindow::GetInspectedWebContents() {
   1206   return inspected_contents_observer_
   1207              ? inspected_contents_observer_->web_contents()
   1208              : NULL;
   1209 }
   1210 
   1211 void DevToolsWindow::LoadCompleted() {
   1212   Show(action_on_load_);
   1213   action_on_load_ = DevToolsToggleAction::NoOp();
   1214   if (!load_completed_callback_.is_null()) {
   1215     load_completed_callback_.Run();
   1216     load_completed_callback_ = base::Closure();
   1217   }
   1218 }
   1219 
   1220 void DevToolsWindow::SetLoadCompletedCallback(const base::Closure& closure) {
   1221   if (life_stage_ == kLoadCompleted || life_stage_ == kClosing) {
   1222     if (!closure.is_null())
   1223       closure.Run();
   1224     return;
   1225   }
   1226   load_completed_callback_ = closure;
   1227 }
   1228 
   1229 bool DevToolsWindow::ForwardKeyboardEvent(
   1230     const content::NativeWebKeyboardEvent& event) {
   1231   return event_forwarder_->ForwardEvent(event);
   1232 }
   1233