• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DevToolsEventForwarder(DevToolsWindow * window)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 
SetWhitelistedShortcuts(const std::string & message)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 
ForwardEvent(const content::NativeWebKeyboardEvent & event)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 
CombineKeyCodeAndModifiers(int key_code,int modifiers)155 int DevToolsEventForwarder::CombineKeyCodeAndModifiers(int key_code,
156                                                        int modifiers) {
157   return key_code | (modifiers << 16);
158 }
159 
KeyWhitelistingAllowed(int key_code,int modifiers)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.
VirtualKeyCodeWithoutLocation(int key_code)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 
web_contents()192   WebContents* web_contents() {
193     return WebContentsObserver::web_contents();
194   }
195 
196  private:
197   DISALLOW_COPY_AND_ASSIGN(InspectedWebContentsObserver);
198 };
199 
InspectedWebContentsObserver(WebContents * web_contents)200 DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver(
201     WebContents* web_contents)
202     : WebContentsObserver(web_contents) {
203 }
204 
~InspectedWebContentsObserver()205 DevToolsWindow::InspectedWebContentsObserver::~InspectedWebContentsObserver() {
206 }
207 
208 // DevToolsToolboxDelegate ----------------------------------------------------
209 
210 class DevToolsToolboxDelegate
211     : public content::WebContentsObserver,
212       public content::WebContentsDelegate {
213  public:
DevToolsToolboxDelegate(WebContents * web_contents)214   explicit DevToolsToolboxDelegate(WebContents* web_contents)
215       : WebContentsObserver(web_contents) { }
~DevToolsToolboxDelegate()216   virtual ~DevToolsToolboxDelegate() { }
217 
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)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 
WebContentsDestroyed()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 
~DevToolsWindow()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
GetDevToolsWindowPlacementPrefKey()256 std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() {
257   return std::string(prefs::kBrowserWindowPlacement) + "_" +
258       std::string(kDevToolsApp);
259 }
260 
261 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)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
GetInTabWebContents(WebContents * inspected_web_contents,DevToolsContentsResizingStrategy * out_strategy)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
GetInstanceForInspectedRenderViewHost(content::RenderViewHost * inspected_rvh)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
GetInstanceForInspectedWebContents(WebContents * inspected_web_contents)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
IsDevToolsWindow(content::RenderViewHost * window_rvh)343 bool DevToolsWindow::IsDevToolsWindow(content::RenderViewHost* window_rvh) {
344   return AsDevToolsWindow(window_rvh) != NULL;
345 }
346 
347 // static
OpenDevToolsWindowForWorker(Profile * profile,DevToolsAgentHost * worker_agent)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
CreateDevToolsWindowForWorker(Profile * profile)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
OpenDevToolsWindow(content::RenderViewHost * inspected_rvh)370 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
371     content::RenderViewHost* inspected_rvh) {
372   return ToggleDevToolsWindow(
373       inspected_rvh, true, DevToolsToggleAction::Show());
374 }
375 
376 // static
OpenDevToolsWindow(content::RenderViewHost * inspected_rvh,const DevToolsToggleAction & action)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
OpenDevToolsWindowForTest(content::RenderViewHost * inspected_rvh,bool is_docked)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
OpenDevToolsWindowForTest(Browser * browser,bool is_docked)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
ToggleDevToolsWindow(Browser * browser,const DevToolsToggleAction & action)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
OpenExternalFrontend(Profile * profile,const std::string & frontend_url,content::DevToolsAgentHost * agent_host)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
ToggleDevToolsWindow(content::RenderViewHost * inspected_rvh,bool force_open,const DevToolsToggleAction & action)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
InspectElement(content::RenderViewHost * inspected_rvh,int x,int y)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 
ScheduleShow(const DevToolsToggleAction & action)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 
Show(const DevToolsToggleAction & action)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
HandleBeforeUnload(WebContents * frontend_contents,bool proceed,bool * proceed_to_fire_unload)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
InterceptPageBeforeUnload(WebContents * contents)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
NeedsToInterceptBeforeUnload(WebContents * contents)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
HasFiredBeforeUnloadEventForDevToolsBrowser(Browser * browser)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
OnPageCloseCanceled(WebContents * contents)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 
DevToolsWindow(Profile * profile,const GURL & url,content::RenderViewHost * inspected_rvh,bool can_dock)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
Create(Profile * profile,const GURL & frontend_url,content::RenderViewHost * inspected_rvh,bool shared_worker_frontend,bool external_frontend,bool can_dock)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
GetDevToolsURL(Profile * profile,const GURL & base_url,bool shared_worker_frontend,bool external_frontend,bool can_dock)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
FindDevToolsWindow(DevToolsAgentHost * agent_host)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
AsDevToolsWindow(content::RenderViewHost * window_rvh)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 
OpenURLFromTab(WebContents * source,const content::OpenURLParams & params)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 
ActivateContents(WebContents * contents)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 
AddNewContents(WebContents * source,WebContents * new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_pos,bool user_gesture,bool * was_blocked)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 
WebContentsCreated(WebContents * source_contents,int opener_render_frame_id,const base::string16 & frame_name,const GURL & target_url,WebContents * new_contents)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 
CloseContents(WebContents * source)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 
ContentsZoomChange(bool zoom_in)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 
BeforeUnloadFired(WebContents * tab,bool proceed,bool * proceed_to_fire_unload)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 
PreHandleKeyboardEvent(WebContents * source,const content::NativeWebKeyboardEvent & event,bool * is_keyboard_shortcut)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 
HandleKeyboardEvent(WebContents * source,const content::NativeWebKeyboardEvent & event)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 
GetJavaScriptDialogManager()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 
OpenColorChooser(WebContents * web_contents,SkColor initial_color,const std::vector<content::ColorSuggestion> & suggestions)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 
RunFileChooser(WebContents * web_contents,const content::FileChooserParams & params)904 void DevToolsWindow::RunFileChooser(WebContents* web_contents,
905                                     const content::FileChooserParams& params) {
906   FileSelectHelper::RunFileChooser(web_contents, params);
907 }
908 
WebContentsFocused(WebContents * contents)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 
PreHandleGestureEvent(WebContents * source,const blink::WebGestureEvent & event)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 
ActivateWindow()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 
CloseWindow()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 
SetInspectedPageBounds(const gfx::Rect & rect)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 
SetContentsResizingStrategy(const gfx::Insets & insets,const gfx::Size & min_size)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 
InspectElementCompleted()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 
MoveWindow(int x,int y)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 
SetIsDockedAndShowImmediatelyForTest(bool is_docked)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 
SetIsDocked(bool dock_requested)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 
OpenInNewTab(const std::string & url)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 
SetWhitelistedShortcuts(const std::string & message)1061 void DevToolsWindow::SetWhitelistedShortcuts(
1062     const std::string& message) {
1063   event_forwarder_->SetWhitelistedShortcuts(message);
1064 }
1065 
InspectedContentsClosing()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 
GetInfoBarService()1074 InfoBarService* DevToolsWindow::GetInfoBarService() {
1075   return is_docked_ ?
1076       InfoBarService::FromWebContents(GetInspectedWebContents()) :
1077       InfoBarService::FromWebContents(main_web_contents_);
1078 }
1079 
RenderProcessGone()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 
OnLoadCompleted()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 
CreateDevToolsBrowser()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
FindInspectedBrowserAndTabIndex(WebContents * inspected_web_contents,Browser ** browser,int * tab)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 
GetInspectedBrowserWindow()1154 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
1155   Browser* browser = NULL;
1156   int tab;
1157   return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
1158                                          &browser, &tab) ?
1159       browser->window() : NULL;
1160 }
1161 
DoAction(const DevToolsToggleAction & action)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 
UpdateBrowserToolbar()1197 void DevToolsWindow::UpdateBrowserToolbar() {
1198   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1199   if (inspected_window)
1200     inspected_window->UpdateToolbar(NULL);
1201 }
1202 
UpdateBrowserWindow()1203 void DevToolsWindow::UpdateBrowserWindow() {
1204   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1205   if (inspected_window)
1206     inspected_window->UpdateDevTools();
1207 }
1208 
GetInspectedWebContents()1209 WebContents* DevToolsWindow::GetInspectedWebContents() {
1210   return inspected_contents_observer_ ?
1211       inspected_contents_observer_->web_contents() : NULL;
1212 }
1213 
LoadCompleted()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 
SetLoadCompletedCallback(const base::Closure & closure)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 
ForwardKeyboardEvent(const content::NativeWebKeyboardEvent & event)1232 bool DevToolsWindow::ForwardKeyboardEvent(
1233     const content::NativeWebKeyboardEvent& event) {
1234   return event_forwarder_->ForwardEvent(event);
1235 }
1236