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