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/command_line.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/lazy_instance.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/extension_system.h"
23 #include "chrome/browser/extensions/extension_web_contents_observer.h"
24 #include "chrome/browser/file_select_helper.h"
25 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
26 #include "chrome/browser/infobars/infobar.h"
27 #include "chrome/browser/prefs/pref_service_syncable.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/sessions/session_tab_helper.h"
30 #include "chrome/browser/themes/theme_properties.h"
31 #include "chrome/browser/themes/theme_service.h"
32 #include "chrome/browser/themes/theme_service_factory.h"
33 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/browser_dialogs.h"
35 #include "chrome/browser/ui/browser_iterator.h"
36 #include "chrome/browser/ui/browser_list.h"
37 #include "chrome/browser/ui/browser_window.h"
38 #include "chrome/browser/ui/host_desktop.h"
39 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
40 #include "chrome/browser/ui/tabs/tab_strip_model.h"
41 #include "chrome/browser/ui/webui/devtools_ui.h"
42 #include "chrome/common/chrome_switches.h"
43 #include "chrome/common/extensions/manifest_url_handler.h"
44 #include "chrome/common/pref_names.h"
45 #include "chrome/common/render_messages.h"
46 #include "chrome/common/url_constants.h"
47 #include "components/user_prefs/pref_registry_syncable.h"
48 #include "content/public/browser/browser_thread.h"
49 #include "content/public/browser/child_process_security_policy.h"
50 #include "content/public/browser/devtools_agent_host.h"
51 #include "content/public/browser/devtools_client_host.h"
52 #include "content/public/browser/devtools_manager.h"
53 #include "content/public/browser/favicon_status.h"
54 #include "content/public/browser/load_notification_details.h"
55 #include "content/public/browser/navigation_controller.h"
56 #include "content/public/browser/navigation_entry.h"
57 #include "content/public/browser/notification_source.h"
58 #include "content/public/browser/render_process_host.h"
59 #include "content/public/browser/render_view_host.h"
60 #include "content/public/browser/user_metrics.h"
61 #include "content/public/browser/web_contents.h"
62 #include "content/public/browser/web_contents_observer.h"
63 #include "content/public/browser/web_contents_view.h"
64 #include "content/public/common/bindings_policy.h"
65 #include "content/public/common/content_client.h"
66 #include "content/public/common/page_transition_types.h"
67 #include "content/public/common/url_constants.h"
68 #include "grit/generated_resources.h"
69 #include "ui/base/l10n/l10n_util.h"
70
71 using base::DictionaryValue;
72 using content::BrowserThread;
73 using content::DevToolsAgentHost;
74
75
76 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
77
78 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
79 public:
80 // If |infobar_service| is NULL, runs |callback| with a single argument with
81 // value "false". Otherwise, creates a dev tools confirm infobar and delegate
82 // and adds the inofbar to |infobar_service|.
83 static void Create(InfoBarService* infobar_service,
84 const DevToolsWindow::InfoBarCallback& callback,
85 const base::string16& message);
86
87 private:
88 DevToolsConfirmInfoBarDelegate(
89 const DevToolsWindow::InfoBarCallback& callback,
90 const base::string16& message);
91 virtual ~DevToolsConfirmInfoBarDelegate();
92
93 virtual base::string16 GetMessageText() const OVERRIDE;
94 virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
95 virtual bool Accept() OVERRIDE;
96 virtual bool Cancel() OVERRIDE;
97
98 DevToolsWindow::InfoBarCallback callback_;
99 const base::string16 message_;
100
101 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
102 };
103
Create(InfoBarService * infobar_service,const DevToolsWindow::InfoBarCallback & callback,const base::string16 & message)104 void DevToolsConfirmInfoBarDelegate::Create(
105 InfoBarService* infobar_service,
106 const DevToolsWindow::InfoBarCallback& callback,
107 const base::string16& message) {
108 if (!infobar_service) {
109 callback.Run(false);
110 return;
111 }
112
113 infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
114 scoped_ptr<ConfirmInfoBarDelegate>(
115 new DevToolsConfirmInfoBarDelegate(callback, message))));
116 }
117
DevToolsConfirmInfoBarDelegate(const DevToolsWindow::InfoBarCallback & callback,const base::string16 & message)118 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
119 const DevToolsWindow::InfoBarCallback& callback,
120 const base::string16& message)
121 : ConfirmInfoBarDelegate(),
122 callback_(callback),
123 message_(message) {
124 }
125
~DevToolsConfirmInfoBarDelegate()126 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
127 if (!callback_.is_null())
128 callback_.Run(false);
129 }
130
GetMessageText() const131 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
132 return message_;
133 }
134
GetButtonLabel(InfoBarButton button) const135 base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
136 InfoBarButton button) const {
137 return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
138 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
139 }
140
Accept()141 bool DevToolsConfirmInfoBarDelegate::Accept() {
142 callback_.Run(true);
143 callback_.Reset();
144 return true;
145 }
146
Cancel()147 bool DevToolsConfirmInfoBarDelegate::Cancel() {
148 callback_.Run(false);
149 callback_.Reset();
150 return true;
151 }
152
153
154 // DevToolsWindow::InspectedWebContentsObserver -------------------------------
155
156 class DevToolsWindow::InspectedWebContentsObserver
157 : public content::WebContentsObserver {
158 public:
159 explicit InspectedWebContentsObserver(content::WebContents* web_contents);
160 virtual ~InspectedWebContentsObserver();
161
web_contents()162 content::WebContents* web_contents() {
163 return WebContentsObserver::web_contents();
164 }
165
166 private:
167 DISALLOW_COPY_AND_ASSIGN(InspectedWebContentsObserver);
168 };
169
InspectedWebContentsObserver(content::WebContents * web_contents)170 DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver(
171 content::WebContents* web_contents)
172 : WebContentsObserver(web_contents) {
173 }
174
~InspectedWebContentsObserver()175 DevToolsWindow::InspectedWebContentsObserver::~InspectedWebContentsObserver() {
176 }
177
178
179 // DevToolsWindow::FrontendWebContentsObserver --------------------------------
180
181 class DevToolsWindow::FrontendWebContentsObserver
182 : public content::WebContentsObserver {
183 public:
184 explicit FrontendWebContentsObserver(DevToolsWindow* window);
185 virtual ~FrontendWebContentsObserver();
186
187 private:
188 // contents::WebContentsObserver:
189 virtual void AboutToNavigateRenderView(
190 content::RenderViewHost* render_view_host) OVERRIDE;
191 virtual void DocumentOnLoadCompletedInMainFrame(int32 page_id) OVERRIDE;
192 virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE;
193
194 DevToolsWindow* devtools_window_;
195 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
196 };
197
FrontendWebContentsObserver(DevToolsWindow * devtools_window)198 DevToolsWindow::FrontendWebContentsObserver::FrontendWebContentsObserver(
199 DevToolsWindow* devtools_window)
200 : WebContentsObserver(devtools_window->web_contents()),
201 devtools_window_(devtools_window) {
202 }
203
WebContentsDestroyed(content::WebContents * contents)204 void DevToolsWindow::FrontendWebContentsObserver::WebContentsDestroyed(
205 content::WebContents* contents) {
206 delete devtools_window_;
207 }
208
~FrontendWebContentsObserver()209 DevToolsWindow::FrontendWebContentsObserver::~FrontendWebContentsObserver() {
210 }
211
AboutToNavigateRenderView(content::RenderViewHost * render_view_host)212 void DevToolsWindow::FrontendWebContentsObserver::AboutToNavigateRenderView(
213 content::RenderViewHost* render_view_host) {
214 content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host);
215 }
216
217 void DevToolsWindow::FrontendWebContentsObserver::
DocumentOnLoadCompletedInMainFrame(int32 page_id)218 DocumentOnLoadCompletedInMainFrame(int32 page_id) {
219 devtools_window_->DocumentOnLoadCompletedInMainFrame();
220 }
221
222 // DevToolsWindow -------------------------------------------------------------
223
224 namespace {
225
226 typedef std::vector<DevToolsWindow*> DevToolsWindows;
227 base::LazyInstance<DevToolsWindows>::Leaky g_instances =
228 LAZY_INSTANCE_INITIALIZER;
229
230 const char kPrefBottom[] = "dock_bottom";
231 const char kPrefRight[] = "dock_right";
232 const char kPrefUndocked[] = "undocked";
233
234 const char kDockSideBottom[] = "bottom";
235 const char kDockSideRight[] = "right";
236 const char kDockSideUndocked[] = "undocked";
237 const char kDockSideMinimized[] = "minimized";
238
239 static const char kFrontendHostId[] = "id";
240 static const char kFrontendHostMethod[] = "method";
241 static const char kFrontendHostParams[] = "params";
242
243 const int kMinContentsSize = 50;
244
SkColorToRGBAString(SkColor color)245 std::string SkColorToRGBAString(SkColor color) {
246 // We avoid StringPrintf because it will use locale specific formatters for
247 // the double (e.g. ',' instead of '.' in German).
248 return "rgba(" + base::IntToString(SkColorGetR(color)) + "," +
249 base::IntToString(SkColorGetG(color)) + "," +
250 base::IntToString(SkColorGetB(color)) + "," +
251 base::DoubleToString(SkColorGetA(color) / 255.0) + ")";
252 }
253
CreateFileSystemValue(DevToolsFileHelper::FileSystem file_system)254 DictionaryValue* CreateFileSystemValue(
255 DevToolsFileHelper::FileSystem file_system) {
256 DictionaryValue* file_system_value = new DictionaryValue();
257 file_system_value->SetString("fileSystemName", file_system.file_system_name);
258 file_system_value->SetString("rootURL", file_system.root_url);
259 file_system_value->SetString("fileSystemPath", file_system.file_system_path);
260 return file_system_value;
261 }
262
263 } // namespace
264
265 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
266
~DevToolsWindow()267 DevToolsWindow::~DevToolsWindow() {
268 content::DevToolsManager::GetInstance()->ClientHostClosing(
269 frontend_host_.get());
270 UpdateBrowserToolbar();
271
272 DevToolsWindows* instances = &g_instances.Get();
273 DevToolsWindows::iterator it(
274 std::find(instances->begin(), instances->end(), this));
275 DCHECK(it != instances->end());
276 instances->erase(it);
277
278 for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
279 jobs_it != indexing_jobs_.end(); ++jobs_it) {
280 jobs_it->second->Stop();
281 }
282 indexing_jobs_.clear();
283 }
284
285 // static
GetDevToolsWindowPlacementPrefKey()286 std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() {
287 return std::string(prefs::kBrowserWindowPlacement) + "_" +
288 std::string(kDevToolsApp);
289 }
290
291 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)292 void DevToolsWindow::RegisterProfilePrefs(
293 user_prefs::PrefRegistrySyncable* registry) {
294 registry->RegisterBooleanPref(
295 prefs::kDevToolsOpenDocked, true,
296 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
297 registry->RegisterStringPref(
298 prefs::kDevToolsDockSide, kDockSideBottom,
299 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
300 registry->RegisterDictionaryPref(
301 prefs::kDevToolsEditedFiles,
302 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
303 registry->RegisterDictionaryPref(
304 prefs::kDevToolsFileSystemPaths,
305 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
306 registry->RegisterStringPref(
307 prefs::kDevToolsAdbKey, std::string(),
308 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
309
310 registry->RegisterDictionaryPref(
311 GetDevToolsWindowPlacementPrefKey().c_str(),
312 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
313
314 registry->RegisterBooleanPref(
315 prefs::kDevToolsDiscoverUsbDevicesEnabled,
316 false,
317 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
318 registry->RegisterBooleanPref(
319 prefs::kDevToolsPortForwardingEnabled,
320 false,
321 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
322 registry->RegisterBooleanPref(
323 prefs::kDevToolsPortForwardingDefaultSet,
324 false,
325 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
326 registry->RegisterDictionaryPref(
327 prefs::kDevToolsPortForwardingConfig,
328 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
329 }
330
331 // static
GetDockedInstanceForInspectedTab(content::WebContents * inspected_web_contents)332 DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab(
333 content::WebContents* inspected_web_contents) {
334 DevToolsWindow* window = GetInstanceForInspectedRenderViewHost(
335 inspected_web_contents->GetRenderViewHost());
336 return (window && window->IsDocked()) ? window : NULL;
337 }
338
339 // static
GetInstanceForInspectedRenderViewHost(content::RenderViewHost * inspected_rvh)340 DevToolsWindow* DevToolsWindow::GetInstanceForInspectedRenderViewHost(
341 content::RenderViewHost* inspected_rvh) {
342 if (!inspected_rvh || !DevToolsAgentHost::HasFor(inspected_rvh))
343 return NULL;
344
345 scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetOrCreateFor(
346 inspected_rvh));
347 return FindDevToolsWindow(agent.get());
348 }
349
350 // static
IsDevToolsWindow(content::RenderViewHost * window_rvh)351 bool DevToolsWindow::IsDevToolsWindow(content::RenderViewHost* window_rvh) {
352 return AsDevToolsWindow(window_rvh) != NULL;
353 }
354
355 // static
OpenDevToolsWindowForWorker(Profile * profile,DevToolsAgentHost * worker_agent)356 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker(
357 Profile* profile,
358 DevToolsAgentHost* worker_agent) {
359 DevToolsWindow* window = FindDevToolsWindow(worker_agent);
360 if (!window) {
361 window = DevToolsWindow::CreateDevToolsWindowForWorker(profile);
362 // Will disconnect the current client host if there is one.
363 content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
364 worker_agent, window->frontend_host_.get());
365 }
366 window->Show(DevToolsToggleAction::Show());
367 return window;
368 }
369
370 // static
CreateDevToolsWindowForWorker(Profile * profile)371 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
372 Profile* profile) {
373 content::RecordAction(content::UserMetricsAction("DevTools_InspectWorker"));
374 return Create(profile, GURL(), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true,
375 false, false);
376 }
377
378 // static
OpenDevToolsWindow(content::RenderViewHost * inspected_rvh)379 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
380 content::RenderViewHost* inspected_rvh) {
381 return ToggleDevToolsWindow(
382 inspected_rvh, true, DevToolsToggleAction::Show());
383 }
384
385 // static
ToggleDevToolsWindow(Browser * browser,const DevToolsToggleAction & action)386 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
387 Browser* browser,
388 const DevToolsToggleAction& action) {
389 if (action.type() == DevToolsToggleAction::kToggle &&
390 browser->is_devtools()) {
391 browser->tab_strip_model()->CloseAllTabs();
392 return NULL;
393 }
394
395 return ToggleDevToolsWindow(
396 browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
397 action.type() == DevToolsToggleAction::kInspect, action);
398 }
399
400 // static
OpenExternalFrontend(Profile * profile,const std::string & frontend_url,content::DevToolsAgentHost * agent_host)401 void DevToolsWindow::OpenExternalFrontend(
402 Profile* profile,
403 const std::string& frontend_url,
404 content::DevToolsAgentHost* agent_host) {
405 DevToolsWindow* window = FindDevToolsWindow(agent_host);
406 if (!window) {
407 window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL,
408 DEVTOOLS_DOCK_SIDE_UNDOCKED, false, true, false);
409 content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
410 agent_host, window->frontend_host_.get());
411 }
412 window->Show(DevToolsToggleAction::Show());
413 }
414
415 // static
ToggleDevToolsWindow(content::RenderViewHost * inspected_rvh,bool force_open,const DevToolsToggleAction & action)416 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
417 content::RenderViewHost* inspected_rvh,
418 bool force_open,
419 const DevToolsToggleAction& action) {
420 scoped_refptr<DevToolsAgentHost> agent(
421 DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
422 content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
423 DevToolsWindow* window = FindDevToolsWindow(agent.get());
424 bool do_open = force_open;
425 if (!window) {
426 Profile* profile = Profile::FromBrowserContext(
427 inspected_rvh->GetProcess()->GetBrowserContext());
428 DevToolsDockSide dock_side = GetDockSideFromPrefs(profile);
429 content::RecordAction(
430 content::UserMetricsAction("DevTools_InspectRenderer"));
431 window = Create(profile, GURL(), inspected_rvh, dock_side, false, false,
432 true);
433 manager->RegisterDevToolsClientHostFor(agent.get(),
434 window->frontend_host_.get());
435 do_open = true;
436 }
437
438 // Update toolbar to reflect DevTools changes.
439 window->UpdateBrowserToolbar();
440
441 // If window is docked and visible, we hide it on toggle. If window is
442 // undocked, we show (activate) it. If window is minimized, we maximize it.
443 if (window->dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
444 window->Restore();
445 else if (!window->IsDocked() || do_open)
446 window->Show(action);
447 else
448 window->CloseWindow();
449
450 return window;
451 }
452
453 // static
InspectElement(content::RenderViewHost * inspected_rvh,int x,int y)454 void DevToolsWindow::InspectElement(content::RenderViewHost* inspected_rvh,
455 int x,
456 int y) {
457 scoped_refptr<DevToolsAgentHost> agent(
458 DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
459 agent->InspectElement(x, y);
460 // TODO(loislo): we should initiate DevTools window opening from within
461 // renderer. Otherwise, we still can hit a race condition here.
462 OpenDevToolsWindow(inspected_rvh);
463 }
464
465 // static
GetMinimumWidth()466 int DevToolsWindow::GetMinimumWidth() {
467 const int kMinDevToolsWidth = 150;
468 return kMinDevToolsWidth;
469 }
470
471 // static
GetMinimumHeight()472 int DevToolsWindow::GetMinimumHeight() {
473 // Minimal height of devtools pane or content pane when devtools are docked
474 // to the browser window.
475 const int kMinDevToolsHeight = 50;
476 return kMinDevToolsHeight;
477 }
478
479 // static
GetMinimizedHeight()480 int DevToolsWindow::GetMinimizedHeight() {
481 const int kMinimizedDevToolsHeight = 24;
482 return kMinimizedDevToolsHeight;
483 }
484
InspectedContentsClosing()485 void DevToolsWindow::InspectedContentsClosing() {
486 intercepted_page_beforeunload_ = false;
487 web_contents_->GetRenderViewHost()->ClosePage();
488 }
489
GetRenderViewHost()490 content::RenderViewHost* DevToolsWindow::GetRenderViewHost() {
491 return web_contents_->GetRenderViewHost();
492 }
493
GetDevToolsClientHostForTest()494 content::DevToolsClientHost* DevToolsWindow::GetDevToolsClientHostForTest() {
495 return frontend_host_.get();
496 }
497
GetWidth(int container_width)498 int DevToolsWindow::GetWidth(int container_width) {
499 if (width_ == -1) {
500 width_ = profile_->GetPrefs()->
501 GetInteger(prefs::kDevToolsVSplitLocation);
502 }
503
504 // By default, size devtools as 1/3 of the browser window.
505 if (width_ == -1)
506 width_ = container_width / 3;
507
508 // Respect the minimum devtools width preset.
509 width_ = std::max(GetMinimumWidth(), width_);
510
511 // But it should never compromise the content window size unless the entire
512 // window is tiny.
513 width_ = std::min(container_width - kMinContentsSize, width_);
514 return width_;
515 }
516
GetHeight(int container_height)517 int DevToolsWindow::GetHeight(int container_height) {
518 if (height_ == -1) {
519 height_ = profile_->GetPrefs()->
520 GetInteger(prefs::kDevToolsHSplitLocation);
521 }
522
523 // By default, size devtools as 1/3 of the browser window.
524 if (height_ == -1)
525 height_ = container_height / 3;
526
527 // Respect the minimum devtools width preset.
528 height_ = std::max(GetMinimumHeight(), height_);
529
530 // But it should never compromise the content window size.
531 height_ = std::min(container_height - kMinContentsSize, height_);
532 return height_;
533 }
534
SetWidth(int width)535 void DevToolsWindow::SetWidth(int width) {
536 width_ = width;
537 profile_->GetPrefs()->SetInteger(prefs::kDevToolsVSplitLocation, width);
538 }
539
SetHeight(int height)540 void DevToolsWindow::SetHeight(int height) {
541 height_ = height;
542 profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height);
543 }
544
Show(const DevToolsToggleAction & action)545 void DevToolsWindow::Show(const DevToolsToggleAction& action) {
546 if (IsDocked()) {
547 Browser* inspected_browser = NULL;
548 int inspected_tab_index = -1;
549 // Tell inspected browser to update splitter and switch to inspected panel.
550 if (!IsInspectedBrowserPopup() &&
551 FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
552 &inspected_browser,
553 &inspected_tab_index)) {
554 BrowserWindow* inspected_window = inspected_browser->window();
555 web_contents_->SetDelegate(this);
556 inspected_window->UpdateDevTools();
557 web_contents_->GetView()->SetInitialFocus();
558 inspected_window->Show();
559 TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
560 tab_strip_model->ActivateTabAt(inspected_tab_index, true);
561 PrefsTabHelper::CreateForWebContents(web_contents_);
562 GetRenderViewHost()->SyncRendererPrefs();
563 ScheduleAction(action);
564 return;
565 }
566
567 // Sometimes we don't know where to dock. Stay undocked.
568 dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED;
569 }
570
571 // Avoid consecutive window switching if the devtools window has been opened
572 // and the Inspect Element shortcut is pressed in the inspected tab.
573 bool should_show_window =
574 !browser_ || (action.type() != DevToolsToggleAction::kInspect);
575
576 if (!browser_)
577 CreateDevToolsBrowser();
578
579 if (should_show_window) {
580 browser_->window()->Show();
581 web_contents_->GetView()->SetInitialFocus();
582 }
583
584 ScheduleAction(action);
585 }
586
587 // static
HandleBeforeUnload(content::WebContents * frontend_contents,bool proceed,bool * proceed_to_fire_unload)588 bool DevToolsWindow::HandleBeforeUnload(content::WebContents* frontend_contents,
589 bool proceed, bool* proceed_to_fire_unload) {
590 DevToolsWindow* window = AsDevToolsWindow(
591 frontend_contents->GetRenderViewHost());
592 if (!window)
593 return false;
594 if (!window->intercepted_page_beforeunload_)
595 return false;
596 window->BeforeUnloadFired(frontend_contents, proceed,
597 proceed_to_fire_unload);
598 return true;
599 }
600
601 // static
InterceptPageBeforeUnload(content::WebContents * contents)602 bool DevToolsWindow::InterceptPageBeforeUnload(content::WebContents* contents) {
603 DevToolsWindow* window =
604 DevToolsWindow::GetInstanceForInspectedRenderViewHost(
605 contents->GetRenderViewHost());
606 if (!window || window->intercepted_page_beforeunload_)
607 return false;
608
609 window->intercepted_page_beforeunload_ = true;
610 // Handle case of devtools inspecting another devtools instance by passing
611 // the call up to the inspecting devtools instance.
612 if (!DevToolsWindow::InterceptPageBeforeUnload(window->web_contents())) {
613 window->web_contents()->GetRenderViewHost()->FirePageBeforeUnload(false);
614 }
615 return true;
616 }
617
618 // static
NeedsToInterceptBeforeUnload(content::WebContents * contents)619 bool DevToolsWindow::NeedsToInterceptBeforeUnload(
620 content::WebContents* contents) {
621 DevToolsWindow* window =
622 DevToolsWindow::GetInstanceForInspectedRenderViewHost(
623 contents->GetRenderViewHost());
624 return window && !window->intercepted_page_beforeunload_;
625 }
626
627 // static
HasFiredBeforeUnloadEventForDevToolsBrowser(Browser * browser)628 bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(
629 Browser* browser) {
630 DCHECK(browser->is_devtools());
631 // When FastUnloadController is used, devtools frontend will be detached
632 // from the browser window at this point which means we've already fired
633 // beforeunload.
634 if (browser->tab_strip_model()->empty())
635 return true;
636 content::WebContents* contents =
637 browser->tab_strip_model()->GetWebContentsAt(0);
638 DevToolsWindow* window = AsDevToolsWindow(contents->GetRenderViewHost());
639 if (!window)
640 return false;
641 return window->intercepted_page_beforeunload_;
642 }
643
644 // static
OnPageCloseCanceled(content::WebContents * contents)645 void DevToolsWindow::OnPageCloseCanceled(content::WebContents* contents) {
646 DevToolsWindow *window =
647 DevToolsWindow::GetInstanceForInspectedRenderViewHost(
648 contents->GetRenderViewHost());
649 if (!window)
650 return;
651 window->intercepted_page_beforeunload_ = false;
652 // Propagate to devtools opened on devtools if any.
653 DevToolsWindow::OnPageCloseCanceled(window->web_contents());
654 }
655
SetDockSideForTest(DevToolsDockSide dock_side)656 void DevToolsWindow::SetDockSideForTest(DevToolsDockSide dock_side) {
657 SetDockSide(SideToString(dock_side));
658 }
659
DevToolsWindow(Profile * profile,const GURL & url,content::RenderViewHost * inspected_rvh,DevToolsDockSide dock_side)660 DevToolsWindow::DevToolsWindow(Profile* profile,
661 const GURL& url,
662 content::RenderViewHost* inspected_rvh,
663 DevToolsDockSide dock_side)
664 : profile_(profile),
665 browser_(NULL),
666 dock_side_(dock_side),
667 is_loaded_(false),
668 action_on_load_(DevToolsToggleAction::Show()),
669 width_(-1),
670 height_(-1),
671 dock_side_before_minimized_(dock_side),
672 intercepted_page_beforeunload_(false),
673 weak_factory_(this) {
674 web_contents_ =
675 content::WebContents::Create(content::WebContents::CreateParams(profile));
676 frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
677
678 web_contents_->GetController().LoadURL(url, content::Referrer(),
679 content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
680
681 frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost(
682 web_contents_, this));
683 file_helper_.reset(new DevToolsFileHelper(web_contents_, profile));
684 file_system_indexer_ = new DevToolsFileSystemIndexer();
685 extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents_);
686
687 g_instances.Get().push_back(this);
688
689 // Wipe out page icon so that the default application icon is used.
690 content::NavigationEntry* entry =
691 web_contents_->GetController().GetActiveEntry();
692 entry->GetFavicon().image = gfx::Image();
693 entry->GetFavicon().valid = true;
694
695 // Register on-load actions.
696 registrar_.Add(
697 this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
698 content::Source<ThemeService>(
699 ThemeServiceFactory::GetForProfile(profile_)));
700
701 // There is no inspected_rvh in case of shared workers.
702 if (inspected_rvh)
703 inspected_contents_observer_.reset(new InspectedWebContentsObserver(
704 content::WebContents::FromRenderViewHost(inspected_rvh)));
705
706 embedder_message_dispatcher_.reset(
707 new DevToolsEmbedderMessageDispatcher(this));
708 }
709
710 // static
Create(Profile * profile,const GURL & frontend_url,content::RenderViewHost * inspected_rvh,DevToolsDockSide dock_side,bool shared_worker_frontend,bool external_frontend,bool can_dock)711 DevToolsWindow* DevToolsWindow::Create(
712 Profile* profile,
713 const GURL& frontend_url,
714 content::RenderViewHost* inspected_rvh,
715 DevToolsDockSide dock_side,
716 bool shared_worker_frontend,
717 bool external_frontend,
718 bool can_dock) {
719 if (inspected_rvh) {
720 // Check for a place to dock.
721 Browser* browser = NULL;
722 int tab;
723 content::WebContents* inspected_web_contents =
724 content::WebContents::FromRenderViewHost(inspected_rvh);
725 if (!FindInspectedBrowserAndTabIndex(inspected_web_contents,
726 &browser, &tab) ||
727 browser->is_type_popup()) {
728 can_dock = false;
729 }
730 }
731
732 // Create WebContents with devtools.
733 GURL url(GetDevToolsURL(profile, frontend_url, dock_side,
734 shared_worker_frontend,
735 external_frontend,
736 can_dock));
737 return new DevToolsWindow(profile, url, inspected_rvh, dock_side);
738 }
739
740 // static
GetDevToolsURL(Profile * profile,const GURL & base_url,DevToolsDockSide dock_side,bool shared_worker_frontend,bool external_frontend,bool can_dock)741 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
742 const GURL& base_url,
743 DevToolsDockSide dock_side,
744 bool shared_worker_frontend,
745 bool external_frontend,
746 bool can_dock) {
747 if (base_url.SchemeIs("data"))
748 return base_url;
749
750 std::string frontend_url(
751 base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec());
752 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile);
753 DCHECK(tp);
754 std::string url_string(
755 frontend_url +
756 ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
757 "dockSide=" + SideToString(dock_side) +
758 "&toolbarColor=" +
759 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
760 "&textColor=" +
761 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)));
762 if (shared_worker_frontend)
763 url_string += "&isSharedWorker=true";
764 if (external_frontend)
765 url_string += "&remoteFrontend=true";
766 if (can_dock)
767 url_string += "&can_dock=true";
768 if (CommandLine::ForCurrentProcess()->HasSwitch(
769 switches::kEnableDevToolsExperiments))
770 url_string += "&experiments=true";
771 url_string += "&updateAppcache";
772 return GURL(url_string);
773 }
774
775 // static
FindDevToolsWindow(DevToolsAgentHost * agent_host)776 DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
777 DevToolsAgentHost* agent_host) {
778 DevToolsWindows* instances = &g_instances.Get();
779 content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
780 for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
781 ++it) {
782 if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) ==
783 agent_host)
784 return *it;
785 }
786 return NULL;
787 }
788
789 // static
AsDevToolsWindow(content::RenderViewHost * window_rvh)790 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
791 content::RenderViewHost* window_rvh) {
792 if (g_instances == NULL)
793 return NULL;
794 DevToolsWindows* instances = &g_instances.Get();
795 for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
796 ++it) {
797 if ((*it)->web_contents_->GetRenderViewHost() == window_rvh)
798 return *it;
799 }
800 return NULL;
801 }
802
803 // static
GetDockSideFromPrefs(Profile * profile)804 DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) {
805 std::string dock_side =
806 profile->GetPrefs()->GetString(prefs::kDevToolsDockSide);
807
808 // Migrate prefs.
809 const char kOldPrefBottom[] = "bottom";
810 const char kOldPrefRight[] = "right";
811 if ((dock_side == kOldPrefBottom) || (dock_side == kOldPrefRight)) {
812 if (!profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked))
813 return DEVTOOLS_DOCK_SIDE_UNDOCKED;
814 return (dock_side == kOldPrefBottom) ?
815 DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_RIGHT;
816 }
817
818 if (dock_side == kPrefUndocked)
819 return DEVTOOLS_DOCK_SIDE_UNDOCKED;
820 if (dock_side == kPrefRight)
821 return DEVTOOLS_DOCK_SIDE_RIGHT;
822 // Default to docked to bottom.
823 return DEVTOOLS_DOCK_SIDE_BOTTOM;
824 }
825
826 // static
SideToString(DevToolsDockSide dock_side)827 std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) {
828 switch (dock_side) {
829 case DEVTOOLS_DOCK_SIDE_UNDOCKED: return kDockSideUndocked;
830 case DEVTOOLS_DOCK_SIDE_RIGHT: return kDockSideRight;
831 case DEVTOOLS_DOCK_SIDE_BOTTOM: return kDockSideBottom;
832 case DEVTOOLS_DOCK_SIDE_MINIMIZED: return kDockSideMinimized;
833 default: return kDockSideUndocked;
834 }
835 }
836
837 // static
SideFromString(const std::string & dock_side)838 DevToolsDockSide DevToolsWindow::SideFromString(
839 const std::string& dock_side) {
840 if (dock_side == kDockSideRight)
841 return DEVTOOLS_DOCK_SIDE_RIGHT;
842 if (dock_side == kDockSideBottom)
843 return DEVTOOLS_DOCK_SIDE_BOTTOM;
844 return (dock_side == kDockSideMinimized) ?
845 DEVTOOLS_DOCK_SIDE_MINIMIZED : DEVTOOLS_DOCK_SIDE_UNDOCKED;
846 }
847
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)848 void DevToolsWindow::Observe(int type,
849 const content::NotificationSource& source,
850 const content::NotificationDetails& details) {
851 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
852 UpdateTheme();
853 }
854
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)855 content::WebContents* DevToolsWindow::OpenURLFromTab(
856 content::WebContents* source,
857 const content::OpenURLParams& params) {
858 if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) {
859 content::WebContents* inspected_web_contents = GetInspectedWebContents();
860 return inspected_web_contents ?
861 inspected_web_contents->OpenURL(params) : NULL;
862 }
863
864 content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
865 scoped_refptr<DevToolsAgentHost> agent_host(
866 manager->GetDevToolsAgentHostFor(frontend_host_.get()));
867 if (!agent_host.get())
868 return NULL;
869 manager->ClientHostClosing(frontend_host_.get());
870 manager->RegisterDevToolsClientHostFor(agent_host.get(),
871 frontend_host_.get());
872
873 chrome::NavigateParams nav_params(profile_, params.url, params.transition);
874 FillNavigateParamsFromOpenURLParams(&nav_params, params);
875 nav_params.source_contents = source;
876 nav_params.tabstrip_add_types = TabStripModel::ADD_NONE;
877 nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
878 nav_params.user_gesture = params.user_gesture;
879 chrome::Navigate(&nav_params);
880 return nav_params.target_contents;
881 }
882
AddNewContents(content::WebContents * source,content::WebContents * new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_pos,bool user_gesture,bool * was_blocked)883 void DevToolsWindow::AddNewContents(content::WebContents* source,
884 content::WebContents* new_contents,
885 WindowOpenDisposition disposition,
886 const gfx::Rect& initial_pos,
887 bool user_gesture,
888 bool* was_blocked) {
889 content::WebContents* inspected_web_contents = GetInspectedWebContents();
890 if (inspected_web_contents) {
891 inspected_web_contents->GetDelegate()->AddNewContents(
892 source, new_contents, disposition, initial_pos, user_gesture,
893 was_blocked);
894 }
895 }
896
CloseContents(content::WebContents * source)897 void DevToolsWindow::CloseContents(content::WebContents* source) {
898 CHECK(IsDocked());
899 // Update dev tools to reflect removed dev tools window.
900 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
901 if (inspected_window)
902 inspected_window->UpdateDevTools();
903 // In case of docked web_contents_, we own it so delete here.
904 // Embedding DevTools window will be deleted as a result of
905 // WebContentsDestroyed callback.
906 delete web_contents_;
907 }
908
BeforeUnloadFired(content::WebContents * tab,bool proceed,bool * proceed_to_fire_unload)909 void DevToolsWindow::BeforeUnloadFired(content::WebContents* tab,
910 bool proceed,
911 bool* proceed_to_fire_unload) {
912 if (!intercepted_page_beforeunload_) {
913 // Docked devtools window closed directly.
914 if (proceed) {
915 content::DevToolsManager::GetInstance()->ClientHostClosing(
916 frontend_host_.get());
917 }
918 *proceed_to_fire_unload = proceed;
919 } else {
920 // Inspected page is attempting to close.
921 content::WebContents* inspected_web_contents = GetInspectedWebContents();
922 if (proceed) {
923 inspected_web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
924 } else {
925 bool should_proceed;
926 inspected_web_contents->GetDelegate()->BeforeUnloadFired(
927 inspected_web_contents, false, &should_proceed);
928 DCHECK(!should_proceed);
929 }
930 *proceed_to_fire_unload = false;
931 }
932 }
933
PreHandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event,bool * is_keyboard_shortcut)934 bool DevToolsWindow::PreHandleKeyboardEvent(
935 content::WebContents* source,
936 const content::NativeWebKeyboardEvent& event,
937 bool* is_keyboard_shortcut) {
938 if (IsDocked()) {
939 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
940 if (inspected_window) {
941 return inspected_window->PreHandleKeyboardEvent(event,
942 is_keyboard_shortcut);
943 }
944 }
945 return false;
946 }
947
HandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)948 void DevToolsWindow::HandleKeyboardEvent(
949 content::WebContents* source,
950 const content::NativeWebKeyboardEvent& event) {
951 if (IsDocked()) {
952 if (event.windowsKeyCode == 0x08) {
953 // Do not navigate back in history on Windows (http://crbug.com/74156).
954 return;
955 }
956 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
957 if (inspected_window)
958 inspected_window->HandleKeyboardEvent(event);
959 }
960 }
961
GetJavaScriptDialogManager()962 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
963 content::WebContents* inspected_web_contents = GetInspectedWebContents();
964 return (inspected_web_contents && inspected_web_contents->GetDelegate()) ?
965 inspected_web_contents->GetDelegate()->GetJavaScriptDialogManager() :
966 content::WebContentsDelegate::GetJavaScriptDialogManager();
967 }
968
OpenColorChooser(content::WebContents * web_contents,SkColor initial_color,const std::vector<content::ColorSuggestion> & suggestions)969 content::ColorChooser* DevToolsWindow::OpenColorChooser(
970 content::WebContents* web_contents,
971 SkColor initial_color,
972 const std::vector<content::ColorSuggestion>& suggestions) {
973 return chrome::ShowColorChooser(web_contents, initial_color);
974 }
975
RunFileChooser(content::WebContents * web_contents,const content::FileChooserParams & params)976 void DevToolsWindow::RunFileChooser(content::WebContents* web_contents,
977 const content::FileChooserParams& params) {
978 FileSelectHelper::RunFileChooser(web_contents, params);
979 }
980
WebContentsFocused(content::WebContents * contents)981 void DevToolsWindow::WebContentsFocused(content::WebContents* contents) {
982 Browser* inspected_browser = NULL;
983 int inspected_tab_index = -1;
984 if (IsDocked() && FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
985 &inspected_browser,
986 &inspected_tab_index))
987 inspected_browser->window()->WebContentsFocused(contents);
988 }
989
DispatchOnEmbedder(const std::string & message)990 void DevToolsWindow::DispatchOnEmbedder(const std::string& message) {
991 std::string method;
992 base::ListValue empty_params;
993 base::ListValue* params = &empty_params;
994
995 base::DictionaryValue* dict = NULL;
996 scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
997 if (!parsed_message ||
998 !parsed_message->GetAsDictionary(&dict) ||
999 !dict->GetString(kFrontendHostMethod, &method) ||
1000 (dict->HasKey(kFrontendHostParams) &&
1001 !dict->GetList(kFrontendHostParams, ¶ms))) {
1002 LOG(ERROR) << "Invalid message was sent to embedder: " << message;
1003 return;
1004 }
1005
1006 int id = 0;
1007 dict->GetInteger(kFrontendHostId, &id);
1008
1009 std::string error = embedder_message_dispatcher_->Dispatch(method, params);
1010 if (id) {
1011 scoped_ptr<base::Value> id_value(base::Value::CreateIntegerValue(id));
1012 scoped_ptr<base::Value> error_value(base::Value::CreateStringValue(error));
1013 CallClientFunction("InspectorFrontendAPI.embedderMessageAck",
1014 id_value.get(), error_value.get(), NULL);
1015 }
1016 }
1017
ActivateWindow()1018 void DevToolsWindow::ActivateWindow() {
1019 if (IsDocked() && GetInspectedBrowserWindow())
1020 web_contents_->GetView()->Focus();
1021 else if (!IsDocked() && !browser_->window()->IsActive())
1022 browser_->window()->Activate();
1023 }
1024
ActivateContents(content::WebContents * contents)1025 void DevToolsWindow::ActivateContents(content::WebContents* contents) {
1026 if (IsDocked()) {
1027 content::WebContents* inspected_tab = this->GetInspectedWebContents();
1028 inspected_tab->GetDelegate()->ActivateContents(inspected_tab);
1029 } else {
1030 browser_->window()->Activate();
1031 }
1032 }
1033
CloseWindow()1034 void DevToolsWindow::CloseWindow() {
1035 DCHECK(IsDocked());
1036 web_contents_->GetRenderViewHost()->FirePageBeforeUnload(false);
1037 }
1038
SetWindowBounds(int x,int y,int width,int height)1039 void DevToolsWindow::SetWindowBounds(int x, int y, int width, int height) {
1040 if (!IsDocked())
1041 browser_->window()->SetBounds(gfx::Rect(x, y, width, height));
1042 }
1043
MoveWindow(int x,int y)1044 void DevToolsWindow::MoveWindow(int x, int y) {
1045 if (!IsDocked()) {
1046 gfx::Rect bounds = browser_->window()->GetBounds();
1047 bounds.Offset(x, y);
1048 browser_->window()->SetBounds(bounds);
1049 }
1050 }
1051
SetDockSide(const std::string & side)1052 void DevToolsWindow::SetDockSide(const std::string& side) {
1053 DevToolsDockSide requested_side = SideFromString(side);
1054 bool dock_requested = requested_side != DEVTOOLS_DOCK_SIDE_UNDOCKED;
1055 bool is_docked = IsDocked();
1056
1057 if (dock_requested &&
1058 (!GetInspectedWebContents() || !GetInspectedBrowserWindow() ||
1059 IsInspectedBrowserPopup())) {
1060 // Cannot dock, avoid window flashing due to close-reopen cycle.
1061 return;
1062 }
1063
1064 if ((dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) &&
1065 (requested_side == DEVTOOLS_DOCK_SIDE_MINIMIZED))
1066 dock_side_before_minimized_ = dock_side_;
1067
1068 dock_side_ = requested_side;
1069 if (dock_requested && !is_docked) {
1070 // Detach window from the external devtools browser. It will lead to
1071 // the browser object's close and delete. Remove observer first.
1072 TabStripModel* tab_strip_model = browser_->tab_strip_model();
1073 tab_strip_model->DetachWebContentsAt(
1074 tab_strip_model->GetIndexOfWebContents(web_contents_));
1075 browser_ = NULL;
1076 } else if (!dock_requested && is_docked) {
1077 // Update inspected window to hide split and reset it.
1078 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1079 if (inspected_window)
1080 inspected_window->UpdateDevTools();
1081 }
1082
1083 if (dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) {
1084 std::string pref_value = kPrefBottom;
1085 switch (dock_side_) {
1086 case DEVTOOLS_DOCK_SIDE_UNDOCKED:
1087 pref_value = kPrefUndocked;
1088 break;
1089 case DEVTOOLS_DOCK_SIDE_RIGHT:
1090 pref_value = kPrefRight;
1091 break;
1092 case DEVTOOLS_DOCK_SIDE_BOTTOM:
1093 pref_value = kPrefBottom;
1094 break;
1095 case DEVTOOLS_DOCK_SIDE_MINIMIZED:
1096 // We don't persist minimized state.
1097 break;
1098 }
1099 profile_->GetPrefs()->SetString(prefs::kDevToolsDockSide, pref_value);
1100 }
1101
1102 Show(DevToolsToggleAction::Show());
1103 }
1104
OpenInNewTab(const std::string & url)1105 void DevToolsWindow::OpenInNewTab(const std::string& url) {
1106 content::OpenURLParams params(
1107 GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
1108 content::PAGE_TRANSITION_LINK, false);
1109 content::WebContents* inspected_web_contents = GetInspectedWebContents();
1110 if (inspected_web_contents) {
1111 inspected_web_contents->OpenURL(params);
1112 } else {
1113 chrome::HostDesktopType host_desktop_type;
1114 if (browser_) {
1115 host_desktop_type = browser_->host_desktop_type();
1116 } else {
1117 // There should always be a browser when there are no inspected web
1118 // contents.
1119 NOTREACHED();
1120 host_desktop_type = chrome::GetActiveDesktop();
1121 }
1122
1123 const BrowserList* browser_list =
1124 BrowserList::GetInstance(host_desktop_type);
1125 for (BrowserList::const_iterator it = browser_list->begin();
1126 it != browser_list->end(); ++it) {
1127 if ((*it)->type() == Browser::TYPE_TABBED) {
1128 (*it)->OpenURL(params);
1129 break;
1130 }
1131 }
1132 }
1133 }
1134
SaveToFile(const std::string & url,const std::string & content,bool save_as)1135 void DevToolsWindow::SaveToFile(const std::string& url,
1136 const std::string& content,
1137 bool save_as) {
1138 file_helper_->Save(url, content, save_as,
1139 base::Bind(&DevToolsWindow::FileSavedAs,
1140 weak_factory_.GetWeakPtr(), url),
1141 base::Bind(&DevToolsWindow::CanceledFileSaveAs,
1142 weak_factory_.GetWeakPtr(), url));
1143 }
1144
AppendToFile(const std::string & url,const std::string & content)1145 void DevToolsWindow::AppendToFile(const std::string& url,
1146 const std::string& content) {
1147 file_helper_->Append(url, content,
1148 base::Bind(&DevToolsWindow::AppendedTo,
1149 weak_factory_.GetWeakPtr(), url));
1150 }
1151
RequestFileSystems()1152 void DevToolsWindow::RequestFileSystems() {
1153 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1154 file_helper_->RequestFileSystems(base::Bind(
1155 &DevToolsWindow::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
1156 }
1157
AddFileSystem()1158 void DevToolsWindow::AddFileSystem() {
1159 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1160 file_helper_->AddFileSystem(
1161 base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
1162 base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
1163 weak_factory_.GetWeakPtr()));
1164 }
1165
RemoveFileSystem(const std::string & file_system_path)1166 void DevToolsWindow::RemoveFileSystem(const std::string& file_system_path) {
1167 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1168 file_helper_->RemoveFileSystem(file_system_path);
1169 StringValue file_system_path_value(file_system_path);
1170 CallClientFunction("InspectorFrontendAPI.fileSystemRemoved",
1171 &file_system_path_value, NULL, NULL);
1172 }
1173
UpgradeDraggedFileSystemPermissions(const std::string & file_system_url)1174 void DevToolsWindow::UpgradeDraggedFileSystemPermissions(
1175 const std::string& file_system_url) {
1176 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1177 file_helper_->UpgradeDraggedFileSystemPermissions(
1178 file_system_url,
1179 base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
1180 base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
1181 weak_factory_.GetWeakPtr()));
1182 }
1183
IndexPath(int request_id,const std::string & file_system_path)1184 void DevToolsWindow::IndexPath(int request_id,
1185 const std::string& file_system_path) {
1186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1187 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1188 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
1189 IndexingDone(request_id, file_system_path);
1190 return;
1191 }
1192 indexing_jobs_[request_id] =
1193 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
1194 file_system_indexer_->IndexPath(
1195 file_system_path,
1196 Bind(&DevToolsWindow::IndexingTotalWorkCalculated,
1197 weak_factory_.GetWeakPtr(),
1198 request_id,
1199 file_system_path),
1200 Bind(&DevToolsWindow::IndexingWorked,
1201 weak_factory_.GetWeakPtr(),
1202 request_id,
1203 file_system_path),
1204 Bind(&DevToolsWindow::IndexingDone,
1205 weak_factory_.GetWeakPtr(),
1206 request_id,
1207 file_system_path)));
1208 }
1209
StopIndexing(int request_id)1210 void DevToolsWindow::StopIndexing(int request_id) {
1211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1212 IndexingJobsMap::iterator it = indexing_jobs_.find(request_id);
1213 if (it == indexing_jobs_.end())
1214 return;
1215 it->second->Stop();
1216 indexing_jobs_.erase(it);
1217 }
1218
SearchInPath(int request_id,const std::string & file_system_path,const std::string & query)1219 void DevToolsWindow::SearchInPath(int request_id,
1220 const std::string& file_system_path,
1221 const std::string& query) {
1222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1223 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1224 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
1225 SearchCompleted(request_id, file_system_path, std::vector<std::string>());
1226 return;
1227 }
1228 file_system_indexer_->SearchInPath(file_system_path,
1229 query,
1230 Bind(&DevToolsWindow::SearchCompleted,
1231 weak_factory_.GetWeakPtr(),
1232 request_id,
1233 file_system_path));
1234 }
1235
FileSavedAs(const std::string & url)1236 void DevToolsWindow::FileSavedAs(const std::string& url) {
1237 StringValue url_value(url);
1238 CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL);
1239 }
1240
CanceledFileSaveAs(const std::string & url)1241 void DevToolsWindow::CanceledFileSaveAs(const std::string& url) {
1242 StringValue url_value(url);
1243 CallClientFunction("InspectorFrontendAPI.canceledSaveURL",
1244 &url_value, NULL, NULL);
1245 }
1246
AppendedTo(const std::string & url)1247 void DevToolsWindow::AppendedTo(const std::string& url) {
1248 StringValue url_value(url);
1249 CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL,
1250 NULL);
1251 }
1252
FileSystemsLoaded(const std::vector<DevToolsFileHelper::FileSystem> & file_systems)1253 void DevToolsWindow::FileSystemsLoaded(
1254 const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
1255 ListValue file_systems_value;
1256 for (size_t i = 0; i < file_systems.size(); ++i)
1257 file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
1258 CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded",
1259 &file_systems_value, NULL, NULL);
1260 }
1261
FileSystemAdded(const DevToolsFileHelper::FileSystem & file_system)1262 void DevToolsWindow::FileSystemAdded(
1263 const DevToolsFileHelper::FileSystem& file_system) {
1264 scoped_ptr<base::StringValue> error_string_value(
1265 new base::StringValue(std::string()));
1266 scoped_ptr<base::DictionaryValue> file_system_value;
1267 if (!file_system.file_system_path.empty())
1268 file_system_value.reset(CreateFileSystemValue(file_system));
1269 CallClientFunction("InspectorFrontendAPI.fileSystemAdded",
1270 error_string_value.get(), file_system_value.get(), NULL);
1271 }
1272
IndexingTotalWorkCalculated(int request_id,const std::string & file_system_path,int total_work)1273 void DevToolsWindow::IndexingTotalWorkCalculated(
1274 int request_id,
1275 const std::string& file_system_path,
1276 int total_work) {
1277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1278 base::FundamentalValue request_id_value(request_id);
1279 StringValue file_system_path_value(file_system_path);
1280 base::FundamentalValue total_work_value(total_work);
1281 CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated",
1282 &request_id_value, &file_system_path_value,
1283 &total_work_value);
1284 }
1285
IndexingWorked(int request_id,const std::string & file_system_path,int worked)1286 void DevToolsWindow::IndexingWorked(int request_id,
1287 const std::string& file_system_path,
1288 int worked) {
1289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1290 base::FundamentalValue request_id_value(request_id);
1291 StringValue file_system_path_value(file_system_path);
1292 base::FundamentalValue worked_value(worked);
1293 CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value,
1294 &file_system_path_value, &worked_value);
1295 }
1296
IndexingDone(int request_id,const std::string & file_system_path)1297 void DevToolsWindow::IndexingDone(int request_id,
1298 const std::string& file_system_path) {
1299 indexing_jobs_.erase(request_id);
1300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1301 base::FundamentalValue request_id_value(request_id);
1302 StringValue file_system_path_value(file_system_path);
1303 CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value,
1304 &file_system_path_value, NULL);
1305 }
1306
SearchCompleted(int request_id,const std::string & file_system_path,const std::vector<std::string> & file_paths)1307 void DevToolsWindow::SearchCompleted(
1308 int request_id,
1309 const std::string& file_system_path,
1310 const std::vector<std::string>& file_paths) {
1311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1312 ListValue file_paths_value;
1313 for (std::vector<std::string>::const_iterator it(file_paths.begin());
1314 it != file_paths.end(); ++it) {
1315 file_paths_value.AppendString(*it);
1316 }
1317 base::FundamentalValue request_id_value(request_id);
1318 StringValue file_system_path_value(file_system_path);
1319 CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value,
1320 &file_system_path_value, &file_paths_value);
1321 }
1322
ShowDevToolsConfirmInfoBar(const base::string16 & message,const InfoBarCallback & callback)1323 void DevToolsWindow::ShowDevToolsConfirmInfoBar(
1324 const base::string16& message,
1325 const InfoBarCallback& callback) {
1326 DevToolsConfirmInfoBarDelegate::Create(
1327 IsDocked() ?
1328 InfoBarService::FromWebContents(GetInspectedWebContents()) :
1329 InfoBarService::FromWebContents(web_contents_),
1330 callback, message);
1331 }
1332
CreateDevToolsBrowser()1333 void DevToolsWindow::CreateDevToolsBrowser() {
1334 std::string wp_key = GetDevToolsWindowPlacementPrefKey();
1335 PrefService* prefs = profile_->GetPrefs();
1336 const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
1337 if (!wp_pref || wp_pref->empty()) {
1338 DictionaryPrefUpdate update(prefs, wp_key.c_str());
1339 DictionaryValue* defaults = update.Get();
1340 defaults->SetInteger("left", 100);
1341 defaults->SetInteger("top", 100);
1342 defaults->SetInteger("right", 740);
1343 defaults->SetInteger("bottom", 740);
1344 defaults->SetBoolean("maximized", false);
1345 defaults->SetBoolean("always_on_top", false);
1346 }
1347
1348 browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
1349 profile_,
1350 chrome::GetHostDesktopTypeForNativeView(
1351 web_contents_->GetView()->GetNativeView())));
1352 browser_->tab_strip_model()->AddWebContents(
1353 web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1354 TabStripModel::ADD_ACTIVE);
1355 GetRenderViewHost()->SyncRendererPrefs();
1356 }
1357
1358 // static
FindInspectedBrowserAndTabIndex(content::WebContents * inspected_web_contents,Browser ** browser,int * tab)1359 bool DevToolsWindow::FindInspectedBrowserAndTabIndex(
1360 content::WebContents* inspected_web_contents, Browser** browser, int* tab) {
1361 if (!inspected_web_contents)
1362 return false;
1363
1364 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1365 int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
1366 inspected_web_contents);
1367 if (tab_index != TabStripModel::kNoTab) {
1368 *browser = *it;
1369 *tab = tab_index;
1370 return true;
1371 }
1372 }
1373 return false;
1374 }
1375
GetInspectedBrowserWindow()1376 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
1377 Browser* browser = NULL;
1378 int tab;
1379 return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
1380 &browser, &tab) ?
1381 browser->window() : NULL;
1382 }
1383
IsInspectedBrowserPopup()1384 bool DevToolsWindow::IsInspectedBrowserPopup() {
1385 Browser* browser = NULL;
1386 int tab;
1387 return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
1388 &browser, &tab) &&
1389 browser->is_type_popup();
1390 }
1391
UpdateFrontendDockSide()1392 void DevToolsWindow::UpdateFrontendDockSide() {
1393 base::StringValue dock_side(SideToString(dock_side_));
1394 CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side, NULL,
1395 NULL);
1396 base::FundamentalValue docked(IsDocked());
1397 CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked, NULL,
1398 NULL);
1399 }
1400
ScheduleAction(const DevToolsToggleAction & action)1401 void DevToolsWindow::ScheduleAction(const DevToolsToggleAction& action) {
1402 action_on_load_ = action;
1403 if (is_loaded_)
1404 DoAction();
1405 }
1406
DoAction()1407 void DevToolsWindow::DoAction() {
1408 UpdateFrontendDockSide();
1409 switch (action_on_load_.type()) {
1410 case DevToolsToggleAction::kShowConsole:
1411 CallClientFunction("InspectorFrontendAPI.showConsole", NULL, NULL, NULL);
1412 break;
1413
1414 case DevToolsToggleAction::kInspect:
1415 CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL,
1416 NULL, NULL);
1417 break;
1418
1419 case DevToolsToggleAction::kShow:
1420 case DevToolsToggleAction::kToggle:
1421 // Do nothing.
1422 break;
1423
1424 case DevToolsToggleAction::kReveal: {
1425 const DevToolsToggleAction::RevealParams* params =
1426 action_on_load_.params();
1427 CHECK(params);
1428 base::StringValue url_value(params->url);
1429 base::FundamentalValue line_value(static_cast<int>(params->line_number));
1430 base::FundamentalValue column_value(
1431 static_cast<int>(params->column_number));
1432 CallClientFunction("InspectorFrontendAPI.revealSourceLine",
1433 &url_value,
1434 &line_value,
1435 &column_value);
1436 break;
1437 }
1438 default:
1439 NOTREACHED();
1440 break;
1441 }
1442 action_on_load_ = DevToolsToggleAction::Show();
1443 }
1444
UpdateTheme()1445 void DevToolsWindow::UpdateTheme() {
1446 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
1447 DCHECK(tp);
1448
1449 std::string command("InspectorFrontendAPI.setToolbarColors(\"" +
1450 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
1451 "\", \"" +
1452 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) +
1453 "\")");
1454 web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
1455 base::string16(), base::ASCIIToUTF16(command));
1456 }
1457
AddDevToolsExtensionsToClient()1458 void DevToolsWindow::AddDevToolsExtensionsToClient() {
1459 content::WebContents* inspected_web_contents = GetInspectedWebContents();
1460 if (inspected_web_contents) {
1461 SessionTabHelper* session_tab_helper =
1462 SessionTabHelper::FromWebContents(inspected_web_contents);
1463 if (session_tab_helper) {
1464 base::FundamentalValue tabId(session_tab_helper->session_id().id());
1465 CallClientFunction("WebInspector.setInspectedTabId", &tabId, NULL, NULL);
1466 }
1467 }
1468
1469 Profile* profile =
1470 Profile::FromBrowserContext(web_contents_->GetBrowserContext());
1471 const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
1472 profile->GetOriginalProfile())->extension_service();
1473 if (!extension_service)
1474 return;
1475 const ExtensionSet* extensions = extension_service->extensions();
1476
1477 ListValue results;
1478 for (ExtensionSet::const_iterator extension(extensions->begin());
1479 extension != extensions->end(); ++extension) {
1480 if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
1481 continue;
1482 DictionaryValue* extension_info = new DictionaryValue();
1483 extension_info->Set(
1484 "startPage",
1485 new StringValue(
1486 extensions::ManifestURL::GetDevToolsPage(extension->get()).spec()));
1487 extension_info->Set("name", new StringValue((*extension)->name()));
1488 extension_info->Set(
1489 "exposeExperimentalAPIs",
1490 new base::FundamentalValue((*extension)->HasAPIPermission(
1491 extensions::APIPermission::kExperimental)));
1492 results.Append(extension_info);
1493 }
1494 CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL);
1495 }
1496
CallClientFunction(const std::string & function_name,const Value * arg1,const Value * arg2,const Value * arg3)1497 void DevToolsWindow::CallClientFunction(const std::string& function_name,
1498 const Value* arg1,
1499 const Value* arg2,
1500 const Value* arg3) {
1501 std::string params;
1502 if (arg1) {
1503 std::string json;
1504 base::JSONWriter::Write(arg1, &json);
1505 params.append(json);
1506 if (arg2) {
1507 base::JSONWriter::Write(arg2, &json);
1508 params.append(", " + json);
1509 if (arg3) {
1510 base::JSONWriter::Write(arg3, &json);
1511 params.append(", " + json);
1512 }
1513 }
1514 }
1515 base::string16 javascript = ASCIIToUTF16(function_name + "(" + params + ");");
1516 web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
1517 base::string16(), javascript);
1518 }
1519
UpdateBrowserToolbar()1520 void DevToolsWindow::UpdateBrowserToolbar() {
1521 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1522 if (inspected_window)
1523 inspected_window->UpdateToolbar(NULL);
1524 }
1525
IsDocked()1526 bool DevToolsWindow::IsDocked() {
1527 return dock_side_ != DEVTOOLS_DOCK_SIDE_UNDOCKED;
1528 }
1529
Restore()1530 void DevToolsWindow::Restore() {
1531 if (dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
1532 SetDockSide(SideToString(dock_side_before_minimized_));
1533 }
1534
GetInspectedWebContents()1535 content::WebContents* DevToolsWindow::GetInspectedWebContents() {
1536 return inspected_contents_observer_ ?
1537 inspected_contents_observer_->web_contents() : NULL;
1538 }
1539
DocumentOnLoadCompletedInMainFrame()1540 void DevToolsWindow::DocumentOnLoadCompletedInMainFrame() {
1541 is_loaded_ = true;
1542 UpdateTheme();
1543 DoAction();
1544 AddDevToolsExtensionsToClient();
1545 }
1546