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