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