Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_window.cc
blobe765988795d2fac970e8bcfd6b416e8ca7a62b96
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.
5 #include "chrome/browser/devtools/devtools_window.h"
6 #include <algorithm>
8 #include "base/command_line.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/lazy_instance.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_system.h"
22 #include "chrome/browser/extensions/extension_web_contents_observer.h"
23 #include "chrome/browser/file_select_helper.h"
24 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
25 #include "chrome/browser/infobars/infobar.h"
26 #include "chrome/browser/prefs/pref_service_syncable.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/sessions/session_tab_helper.h"
29 #include "chrome/browser/themes/theme_properties.h"
30 #include "chrome/browser/themes/theme_service.h"
31 #include "chrome/browser/themes/theme_service_factory.h"
32 #include "chrome/browser/ui/browser.h"
33 #include "chrome/browser/ui/browser_dialogs.h"
34 #include "chrome/browser/ui/browser_iterator.h"
35 #include "chrome/browser/ui/browser_list.h"
36 #include "chrome/browser/ui/browser_window.h"
37 #include "chrome/browser/ui/host_desktop.h"
38 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
39 #include "chrome/browser/ui/tabs/tab_strip_model.h"
40 #include "chrome/browser/ui/webui/devtools_ui.h"
41 #include "chrome/common/chrome_switches.h"
42 #include "chrome/common/extensions/manifest_url_handler.h"
43 #include "chrome/common/pref_names.h"
44 #include "chrome/common/render_messages.h"
45 #include "chrome/common/url_constants.h"
46 #include "components/user_prefs/pref_registry_syncable.h"
47 #include "content/public/browser/browser_thread.h"
48 #include "content/public/browser/child_process_security_policy.h"
49 #include "content/public/browser/devtools_agent_host.h"
50 #include "content/public/browser/devtools_client_host.h"
51 #include "content/public/browser/devtools_manager.h"
52 #include "content/public/browser/favicon_status.h"
53 #include "content/public/browser/load_notification_details.h"
54 #include "content/public/browser/navigation_controller.h"
55 #include "content/public/browser/navigation_entry.h"
56 #include "content/public/browser/notification_source.h"
57 #include "content/public/browser/render_process_host.h"
58 #include "content/public/browser/render_view_host.h"
59 #include "content/public/browser/user_metrics.h"
60 #include "content/public/browser/web_contents.h"
61 #include "content/public/browser/web_contents_observer.h"
62 #include "content/public/browser/web_contents_view.h"
63 #include "content/public/common/bindings_policy.h"
64 #include "content/public/common/content_client.h"
65 #include "content/public/common/page_transition_types.h"
66 #include "content/public/common/url_constants.h"
67 #include "extensions/common/extension_set.h"
68 #include "grit/generated_resources.h"
69 #include "ui/base/l10n/l10n_util.h"
71 using base::DictionaryValue;
72 using content::BrowserThread;
73 using content::DevToolsAgentHost;
76 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
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);
87 private:
88 DevToolsConfirmInfoBarDelegate(
89 const DevToolsWindow::InfoBarCallback& callback,
90 const base::string16& message);
91 virtual ~DevToolsConfirmInfoBarDelegate();
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;
98 DevToolsWindow::InfoBarCallback callback_;
99 const base::string16 message_;
101 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
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;
113 infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
114 scoped_ptr<ConfirmInfoBarDelegate>(
115 new DevToolsConfirmInfoBarDelegate(callback, message))));
118 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
119 const DevToolsWindow::InfoBarCallback& callback,
120 const base::string16& message)
121 : ConfirmInfoBarDelegate(),
122 callback_(callback),
123 message_(message) {
126 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
127 if (!callback_.is_null())
128 callback_.Run(false);
131 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
132 return message_;
135 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);
141 bool DevToolsConfirmInfoBarDelegate::Accept() {
142 callback_.Run(true);
143 callback_.Reset();
144 return true;
147 bool DevToolsConfirmInfoBarDelegate::Cancel() {
148 callback_.Run(false);
149 callback_.Reset();
150 return true;
154 // DevToolsWindow::InspectedWebContentsObserver -------------------------------
156 class DevToolsWindow::InspectedWebContentsObserver
157 : public content::WebContentsObserver {
158 public:
159 explicit InspectedWebContentsObserver(content::WebContents* web_contents);
160 virtual ~InspectedWebContentsObserver();
162 content::WebContents* web_contents() {
163 return WebContentsObserver::web_contents();
166 private:
167 DISALLOW_COPY_AND_ASSIGN(InspectedWebContentsObserver);
170 DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver(
171 content::WebContents* web_contents)
172 : WebContentsObserver(web_contents) {
175 DevToolsWindow::InspectedWebContentsObserver::~InspectedWebContentsObserver() {
179 // DevToolsWindow::FrontendWebContentsObserver --------------------------------
181 class DevToolsWindow::FrontendWebContentsObserver
182 : public content::WebContentsObserver {
183 public:
184 explicit FrontendWebContentsObserver(DevToolsWindow* window);
185 virtual ~FrontendWebContentsObserver();
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;
194 DevToolsWindow* devtools_window_;
195 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
198 DevToolsWindow::FrontendWebContentsObserver::FrontendWebContentsObserver(
199 DevToolsWindow* devtools_window)
200 : WebContentsObserver(devtools_window->web_contents()),
201 devtools_window_(devtools_window) {
204 void DevToolsWindow::FrontendWebContentsObserver::WebContentsDestroyed(
205 content::WebContents* contents) {
206 delete devtools_window_;
209 DevToolsWindow::FrontendWebContentsObserver::~FrontendWebContentsObserver() {
212 void DevToolsWindow::FrontendWebContentsObserver::AboutToNavigateRenderView(
213 content::RenderViewHost* render_view_host) {
214 content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host);
217 void DevToolsWindow::FrontendWebContentsObserver::
218 DocumentOnLoadCompletedInMainFrame(int32 page_id) {
219 devtools_window_->DocumentOnLoadCompletedInMainFrame();
222 // DevToolsWindow -------------------------------------------------------------
224 namespace {
226 typedef std::vector<DevToolsWindow*> DevToolsWindows;
227 base::LazyInstance<DevToolsWindows>::Leaky g_instances =
228 LAZY_INSTANCE_INITIALIZER;
230 // TODO(dgozman): remove after switching to SetIsDocked.
231 const char kDockSideUndocked[] = "undocked";
233 static const char kFrontendHostId[] = "id";
234 static const char kFrontendHostMethod[] = "method";
235 static const char kFrontendHostParams[] = "params";
237 std::string SkColorToRGBAString(SkColor color) {
238 // We avoid StringPrintf because it will use locale specific formatters for
239 // the double (e.g. ',' instead of '.' in German).
240 return "rgba(" + base::IntToString(SkColorGetR(color)) + "," +
241 base::IntToString(SkColorGetG(color)) + "," +
242 base::IntToString(SkColorGetB(color)) + "," +
243 base::DoubleToString(SkColorGetA(color) / 255.0) + ")";
246 base::DictionaryValue* CreateFileSystemValue(
247 DevToolsFileHelper::FileSystem file_system) {
248 base::DictionaryValue* file_system_value = new base::DictionaryValue();
249 file_system_value->SetString("fileSystemName", file_system.file_system_name);
250 file_system_value->SetString("rootURL", file_system.root_url);
251 file_system_value->SetString("fileSystemPath", file_system.file_system_path);
252 return file_system_value;
255 } // namespace
257 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
259 DevToolsWindow::~DevToolsWindow() {
260 content::DevToolsManager::GetInstance()->ClientHostClosing(
261 frontend_host_.get());
262 UpdateBrowserToolbar();
264 DevToolsWindows* instances = &g_instances.Get();
265 DevToolsWindows::iterator it(
266 std::find(instances->begin(), instances->end(), this));
267 DCHECK(it != instances->end());
268 instances->erase(it);
270 for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
271 jobs_it != indexing_jobs_.end(); ++jobs_it) {
272 jobs_it->second->Stop();
274 indexing_jobs_.clear();
277 // static
278 std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() {
279 return std::string(prefs::kBrowserWindowPlacement) + "_" +
280 std::string(kDevToolsApp);
283 // static
284 void DevToolsWindow::RegisterProfilePrefs(
285 user_prefs::PrefRegistrySyncable* registry) {
286 registry->RegisterDictionaryPref(
287 prefs::kDevToolsEditedFiles,
288 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
289 registry->RegisterDictionaryPref(
290 prefs::kDevToolsFileSystemPaths,
291 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
292 registry->RegisterStringPref(
293 prefs::kDevToolsAdbKey, std::string(),
294 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
296 registry->RegisterDictionaryPref(
297 GetDevToolsWindowPlacementPrefKey().c_str(),
298 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
300 registry->RegisterBooleanPref(
301 prefs::kDevToolsDiscoverUsbDevicesEnabled,
302 false,
303 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
304 registry->RegisterBooleanPref(
305 prefs::kDevToolsPortForwardingEnabled,
306 false,
307 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
308 registry->RegisterBooleanPref(
309 prefs::kDevToolsPortForwardingDefaultSet,
310 false,
311 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
312 registry->RegisterDictionaryPref(
313 prefs::kDevToolsPortForwardingConfig,
314 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
317 // static
318 DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab(
319 content::WebContents* inspected_web_contents) {
320 DevToolsWindow* window = GetInstanceForInspectedRenderViewHost(
321 inspected_web_contents->GetRenderViewHost());
322 if (!window)
323 return NULL;
324 // Not yet loaded window is treated as docked, but we should not present it
325 // until we decided on docking.
326 bool is_docked_set = window->load_state_ == kLoadCompleted ||
327 window->load_state_ == kIsDockedSet;
328 return window->is_docked_ && is_docked_set ? window : NULL;
331 // static
332 DevToolsWindow* DevToolsWindow::GetInstanceForInspectedRenderViewHost(
333 content::RenderViewHost* inspected_rvh) {
334 if (!inspected_rvh || !DevToolsAgentHost::HasFor(inspected_rvh))
335 return NULL;
337 scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetOrCreateFor(
338 inspected_rvh));
339 return FindDevToolsWindow(agent.get());
342 // static
343 bool DevToolsWindow::IsDevToolsWindow(content::RenderViewHost* window_rvh) {
344 return AsDevToolsWindow(window_rvh) != NULL;
347 // static
348 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker(
349 Profile* profile,
350 DevToolsAgentHost* worker_agent) {
351 DevToolsWindow* window = FindDevToolsWindow(worker_agent);
352 if (!window) {
353 window = DevToolsWindow::CreateDevToolsWindowForWorker(profile);
354 // Will disconnect the current client host if there is one.
355 content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
356 worker_agent, window->frontend_host_.get());
358 window->ScheduleShow(DevToolsToggleAction::Show());
359 return window;
362 // static
363 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
364 Profile* profile) {
365 content::RecordAction(base::UserMetricsAction("DevTools_InspectWorker"));
366 return Create(profile, GURL(), NULL, true, false, false);
369 // static
370 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
371 content::RenderViewHost* inspected_rvh) {
372 return ToggleDevToolsWindow(
373 inspected_rvh, true, DevToolsToggleAction::Show());
376 // static
377 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
378 content::RenderViewHost* inspected_rvh,
379 const DevToolsToggleAction& action) {
380 return ToggleDevToolsWindow(
381 inspected_rvh, true, action);
384 // static
385 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForTest(
386 content::RenderViewHost* inspected_rvh,
387 bool is_docked) {
388 DevToolsWindow* window = OpenDevToolsWindow(inspected_rvh);
389 window->SetIsDockedAndShowImmediatelyForTest(is_docked);
390 return window;
393 // static
394 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForTest(
395 Browser* browser,
396 bool is_docked) {
397 return OpenDevToolsWindowForTest(
398 browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
399 is_docked);
402 // static
403 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
404 Browser* browser,
405 const DevToolsToggleAction& action) {
406 if (action.type() == DevToolsToggleAction::kToggle &&
407 browser->is_devtools()) {
408 browser->tab_strip_model()->CloseAllTabs();
409 return NULL;
412 return ToggleDevToolsWindow(
413 browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
414 action.type() == DevToolsToggleAction::kInspect, action);
417 // static
418 void DevToolsWindow::OpenExternalFrontend(
419 Profile* profile,
420 const std::string& frontend_url,
421 content::DevToolsAgentHost* agent_host) {
422 DevToolsWindow* window = FindDevToolsWindow(agent_host);
423 if (!window) {
424 window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL,
425 false, true, false);
426 content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
427 agent_host, window->frontend_host_.get());
429 window->ScheduleShow(DevToolsToggleAction::Show());
432 // static
433 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
434 content::RenderViewHost* inspected_rvh,
435 bool force_open,
436 const DevToolsToggleAction& action) {
437 scoped_refptr<DevToolsAgentHost> agent(
438 DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
439 content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
440 DevToolsWindow* window = FindDevToolsWindow(agent.get());
441 bool do_open = force_open;
442 if (!window) {
443 Profile* profile = Profile::FromBrowserContext(
444 inspected_rvh->GetProcess()->GetBrowserContext());
445 content::RecordAction(
446 base::UserMetricsAction("DevTools_InspectRenderer"));
447 window = Create(profile, GURL(), inspected_rvh, false, false, true);
448 manager->RegisterDevToolsClientHostFor(agent.get(),
449 window->frontend_host_.get());
450 do_open = true;
453 // Update toolbar to reflect DevTools changes.
454 window->UpdateBrowserToolbar();
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();
463 return window;
466 // static
467 void DevToolsWindow::InspectElement(content::RenderViewHost* inspected_rvh,
468 int x,
469 int y) {
470 scoped_refptr<DevToolsAgentHost> agent(
471 DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
472 agent->InspectElement(x, y);
473 // TODO(loislo): we should initiate DevTools window opening from within
474 // renderer. Otherwise, we still can hit a race condition here.
475 OpenDevToolsWindow(inspected_rvh);
478 // static
479 int DevToolsWindow::GetMinimizedHeight() {
480 const int kMinimizedDevToolsHeight = 24;
481 return kMinimizedDevToolsHeight;
484 void DevToolsWindow::InspectedContentsClosing() {
485 intercepted_page_beforeunload_ = false;
486 // This will prevent any activity after frontend is loaded.
487 action_on_load_ = DevToolsToggleAction::NoOp();
488 web_contents_->GetRenderViewHost()->ClosePage();
491 content::RenderViewHost* DevToolsWindow::GetRenderViewHost() {
492 return web_contents_->GetRenderViewHost();
495 gfx::Insets DevToolsWindow::GetContentsInsets() const {
496 return contents_insets_;
499 gfx::Size DevToolsWindow::GetMinimumSize() const {
500 const gfx::Size kMinDevToolsSize = gfx::Size(200, 100);
501 return kMinDevToolsSize;
504 void DevToolsWindow::ScheduleShow(const DevToolsToggleAction& action) {
505 if (load_state_ == kLoadCompleted) {
506 Show(action);
507 return;
510 // Action will be done only after load completed.
511 action_on_load_ = action;
513 if (!can_dock_) {
514 // No harm to show always-undocked window right away.
515 is_docked_ = false;
516 Show(DevToolsToggleAction::Show());
520 void DevToolsWindow::Show(const DevToolsToggleAction& action) {
521 if (action.type() == DevToolsToggleAction::kNoOp)
522 return;
524 if (is_docked_) {
525 DCHECK(can_dock_);
526 Browser* inspected_browser = NULL;
527 int inspected_tab_index = -1;
528 FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
529 &inspected_browser,
530 &inspected_tab_index);
531 DCHECK(inspected_browser);
532 DCHECK(inspected_tab_index != -1);
534 // Tell inspected browser to update splitter and switch to inspected panel.
535 BrowserWindow* inspected_window = inspected_browser->window();
536 web_contents_->SetDelegate(this);
537 inspected_window->UpdateDevTools();
538 web_contents_->GetView()->SetInitialFocus();
539 inspected_window->Show();
541 TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
542 tab_strip_model->ActivateTabAt(inspected_tab_index, true);
543 PrefsTabHelper::CreateForWebContents(web_contents_);
544 GetRenderViewHost()->SyncRendererPrefs();
546 DoAction(action);
547 return;
550 // Avoid consecutive window switching if the devtools window has been opened
551 // and the Inspect Element shortcut is pressed in the inspected tab.
552 bool should_show_window =
553 !browser_ || (action.type() != DevToolsToggleAction::kInspect);
555 if (!browser_)
556 CreateDevToolsBrowser();
558 if (should_show_window) {
559 browser_->window()->Show();
560 web_contents_->GetView()->SetInitialFocus();
563 DoAction(action);
566 // static
567 bool DevToolsWindow::HandleBeforeUnload(content::WebContents* frontend_contents,
568 bool proceed, bool* proceed_to_fire_unload) {
569 DevToolsWindow* window = AsDevToolsWindow(
570 frontend_contents->GetRenderViewHost());
571 if (!window)
572 return false;
573 if (!window->intercepted_page_beforeunload_)
574 return false;
575 window->BeforeUnloadFired(frontend_contents, proceed,
576 proceed_to_fire_unload);
577 return true;
580 // static
581 bool DevToolsWindow::InterceptPageBeforeUnload(content::WebContents* contents) {
582 DevToolsWindow* window =
583 DevToolsWindow::GetInstanceForInspectedRenderViewHost(
584 contents->GetRenderViewHost());
585 if (!window || window->intercepted_page_beforeunload_)
586 return false;
588 // Not yet loaded frontend will not handle beforeunload.
589 if (window->load_state_ != kLoadCompleted)
590 return false;
592 window->intercepted_page_beforeunload_ = true;
593 // Handle case of devtools inspecting another devtools instance by passing
594 // the call up to the inspecting devtools instance.
595 if (!DevToolsWindow::InterceptPageBeforeUnload(window->web_contents())) {
596 window->web_contents()->GetRenderViewHost()->FirePageBeforeUnload(false);
598 return true;
601 // static
602 bool DevToolsWindow::NeedsToInterceptBeforeUnload(
603 content::WebContents* contents) {
604 DevToolsWindow* window =
605 DevToolsWindow::GetInstanceForInspectedRenderViewHost(
606 contents->GetRenderViewHost());
607 return window && !window->intercepted_page_beforeunload_;
610 // static
611 bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(
612 Browser* browser) {
613 DCHECK(browser->is_devtools());
614 // When FastUnloadController is used, devtools frontend will be detached
615 // from the browser window at this point which means we've already fired
616 // beforeunload.
617 if (browser->tab_strip_model()->empty())
618 return true;
619 content::WebContents* contents =
620 browser->tab_strip_model()->GetWebContentsAt(0);
621 DevToolsWindow* window = AsDevToolsWindow(contents->GetRenderViewHost());
622 if (!window)
623 return false;
624 return window->intercepted_page_beforeunload_;
627 // static
628 void DevToolsWindow::OnPageCloseCanceled(content::WebContents* contents) {
629 DevToolsWindow *window =
630 DevToolsWindow::GetInstanceForInspectedRenderViewHost(
631 contents->GetRenderViewHost());
632 if (!window)
633 return;
634 window->intercepted_page_beforeunload_ = false;
635 // Propagate to devtools opened on devtools if any.
636 DevToolsWindow::OnPageCloseCanceled(window->web_contents());
639 DevToolsWindow::DevToolsWindow(Profile* profile,
640 const GURL& url,
641 content::RenderViewHost* inspected_rvh,
642 bool can_dock)
643 : profile_(profile),
644 browser_(NULL),
645 is_docked_(true),
646 can_dock_(can_dock),
647 // This initialization allows external front-end to work without changes.
648 // We don't wait for docking call, but instead immediately show undocked.
649 // Passing "dockSide=undocked" parameter ensures proper UI.
650 load_state_(can_dock ? kNotLoaded : kIsDockedSet),
651 action_on_load_(DevToolsToggleAction::NoOp()),
652 ignore_set_is_docked_for_test_(false),
653 intercepted_page_beforeunload_(false),
654 weak_factory_(this) {
655 web_contents_ =
656 content::WebContents::Create(content::WebContents::CreateParams(profile));
657 frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
659 // Set up delegate, so we get fully-functional window immediately.
660 // It will not appear in UI though until |load_state_ == kLoadCompleted|.
661 web_contents_->SetDelegate(this);
663 web_contents_->GetController().LoadURL(url, content::Referrer(),
664 content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
666 frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost(
667 web_contents_, this));
668 file_helper_.reset(new DevToolsFileHelper(web_contents_, profile));
669 file_system_indexer_ = new DevToolsFileSystemIndexer();
670 extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents_);
672 g_instances.Get().push_back(this);
674 // Wipe out page icon so that the default application icon is used.
675 content::NavigationEntry* entry =
676 web_contents_->GetController().GetActiveEntry();
677 entry->GetFavicon().image = gfx::Image();
678 entry->GetFavicon().valid = true;
680 // Register on-load actions.
681 registrar_.Add(
682 this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
683 content::Source<ThemeService>(
684 ThemeServiceFactory::GetForProfile(profile_)));
686 // There is no inspected_rvh in case of shared workers.
687 if (inspected_rvh)
688 inspected_contents_observer_.reset(new InspectedWebContentsObserver(
689 content::WebContents::FromRenderViewHost(inspected_rvh)));
691 embedder_message_dispatcher_.reset(
692 new DevToolsEmbedderMessageDispatcher(this));
695 // static
696 DevToolsWindow* DevToolsWindow::Create(
697 Profile* profile,
698 const GURL& frontend_url,
699 content::RenderViewHost* inspected_rvh,
700 bool shared_worker_frontend,
701 bool external_frontend,
702 bool can_dock) {
703 if (inspected_rvh) {
704 // Check for a place to dock.
705 Browser* browser = NULL;
706 int tab;
707 content::WebContents* inspected_web_contents =
708 content::WebContents::FromRenderViewHost(inspected_rvh);
709 if (!FindInspectedBrowserAndTabIndex(inspected_web_contents,
710 &browser, &tab) ||
711 browser->is_type_popup()) {
712 can_dock = false;
716 // Create WebContents with devtools.
717 GURL url(GetDevToolsURL(profile, frontend_url,
718 shared_worker_frontend,
719 external_frontend,
720 can_dock));
721 return new DevToolsWindow(profile, url, inspected_rvh, can_dock);
724 // static
725 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
726 const GURL& base_url,
727 bool shared_worker_frontend,
728 bool external_frontend,
729 bool can_dock) {
730 if (base_url.SchemeIs("data"))
731 return base_url;
733 std::string frontend_url(
734 base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec());
735 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile);
736 DCHECK(tp);
737 std::string url_string(
738 frontend_url +
739 ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
740 "dockSide=undocked" + // TODO(dgozman): remove this support in M38.
741 "&toolbarColor=" +
742 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
743 "&textColor=" +
744 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)));
745 if (shared_worker_frontend)
746 url_string += "&isSharedWorker=true";
747 if (external_frontend)
748 url_string += "&remoteFrontend=true";
749 if (can_dock)
750 url_string += "&can_dock=true";
751 if (CommandLine::ForCurrentProcess()->HasSwitch(
752 switches::kEnableDevToolsExperiments))
753 url_string += "&experiments=true";
754 url_string += "&updateAppcache";
755 return GURL(url_string);
758 // static
759 DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
760 DevToolsAgentHost* agent_host) {
761 DevToolsWindows* instances = &g_instances.Get();
762 content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
763 for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
764 ++it) {
765 if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) ==
766 agent_host)
767 return *it;
769 return NULL;
772 // static
773 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
774 content::RenderViewHost* window_rvh) {
775 if (g_instances == NULL)
776 return NULL;
777 DevToolsWindows* instances = &g_instances.Get();
778 for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
779 ++it) {
780 if ((*it)->web_contents_->GetRenderViewHost() == window_rvh)
781 return *it;
783 return NULL;
786 // TODO(dgozman): remove after switch to SetIsDocked.
787 // static
788 bool DevToolsWindow::IsDockedFromString(const std::string& dock_side) {
789 return dock_side != kDockSideUndocked;
792 void DevToolsWindow::Observe(int type,
793 const content::NotificationSource& source,
794 const content::NotificationDetails& details) {
795 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
796 UpdateTheme();
799 content::WebContents* DevToolsWindow::OpenURLFromTab(
800 content::WebContents* source,
801 const content::OpenURLParams& params) {
802 if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) {
803 content::WebContents* inspected_web_contents = GetInspectedWebContents();
804 return inspected_web_contents ?
805 inspected_web_contents->OpenURL(params) : NULL;
808 content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
809 scoped_refptr<DevToolsAgentHost> agent_host(
810 manager->GetDevToolsAgentHostFor(frontend_host_.get()));
811 if (!agent_host.get())
812 return NULL;
813 manager->ClientHostClosing(frontend_host_.get());
814 manager->RegisterDevToolsClientHostFor(agent_host.get(),
815 frontend_host_.get());
817 chrome::NavigateParams nav_params(profile_, params.url, params.transition);
818 FillNavigateParamsFromOpenURLParams(&nav_params, params);
819 nav_params.source_contents = source;
820 nav_params.tabstrip_add_types = TabStripModel::ADD_NONE;
821 nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
822 nav_params.user_gesture = params.user_gesture;
823 chrome::Navigate(&nav_params);
824 return nav_params.target_contents;
827 void DevToolsWindow::AddNewContents(content::WebContents* source,
828 content::WebContents* new_contents,
829 WindowOpenDisposition disposition,
830 const gfx::Rect& initial_pos,
831 bool user_gesture,
832 bool* was_blocked) {
833 content::WebContents* inspected_web_contents = GetInspectedWebContents();
834 if (inspected_web_contents) {
835 inspected_web_contents->GetDelegate()->AddNewContents(
836 source, new_contents, disposition, initial_pos, user_gesture,
837 was_blocked);
841 void DevToolsWindow::CloseContents(content::WebContents* source) {
842 CHECK(is_docked_);
843 // This will prevent any activity after frontend is loaded.
844 action_on_load_ = DevToolsToggleAction::NoOp();
845 // Update dev tools to reflect removed dev tools window.
846 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
847 if (inspected_window)
848 inspected_window->UpdateDevTools();
849 // In case of docked web_contents_, we own it so delete here.
850 // Embedding DevTools window will be deleted as a result of
851 // WebContentsDestroyed callback.
852 delete web_contents_;
855 void DevToolsWindow::BeforeUnloadFired(content::WebContents* tab,
856 bool proceed,
857 bool* proceed_to_fire_unload) {
858 if (!intercepted_page_beforeunload_) {
859 // Docked devtools window closed directly.
860 if (proceed) {
861 content::DevToolsManager::GetInstance()->ClientHostClosing(
862 frontend_host_.get());
864 *proceed_to_fire_unload = proceed;
865 } else {
866 // Inspected page is attempting to close.
867 content::WebContents* inspected_web_contents = GetInspectedWebContents();
868 if (proceed) {
869 inspected_web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
870 } else {
871 bool should_proceed;
872 inspected_web_contents->GetDelegate()->BeforeUnloadFired(
873 inspected_web_contents, false, &should_proceed);
874 DCHECK(!should_proceed);
876 *proceed_to_fire_unload = false;
880 bool DevToolsWindow::PreHandleKeyboardEvent(
881 content::WebContents* source,
882 const content::NativeWebKeyboardEvent& event,
883 bool* is_keyboard_shortcut) {
884 if (is_docked_) {
885 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
886 if (inspected_window) {
887 return inspected_window->PreHandleKeyboardEvent(event,
888 is_keyboard_shortcut);
891 return false;
894 void DevToolsWindow::HandleKeyboardEvent(
895 content::WebContents* source,
896 const content::NativeWebKeyboardEvent& event) {
897 if (is_docked_) {
898 if (event.windowsKeyCode == 0x08) {
899 // Do not navigate back in history on Windows (http://crbug.com/74156).
900 return;
902 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
903 if (inspected_window)
904 inspected_window->HandleKeyboardEvent(event);
908 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
909 content::WebContents* inspected_web_contents = GetInspectedWebContents();
910 return (inspected_web_contents && inspected_web_contents->GetDelegate()) ?
911 inspected_web_contents->GetDelegate()->GetJavaScriptDialogManager() :
912 content::WebContentsDelegate::GetJavaScriptDialogManager();
915 content::ColorChooser* DevToolsWindow::OpenColorChooser(
916 content::WebContents* web_contents,
917 SkColor initial_color,
918 const std::vector<content::ColorSuggestion>& suggestions) {
919 return chrome::ShowColorChooser(web_contents, initial_color);
922 void DevToolsWindow::RunFileChooser(content::WebContents* web_contents,
923 const content::FileChooserParams& params) {
924 FileSelectHelper::RunFileChooser(web_contents, params);
927 void DevToolsWindow::WebContentsFocused(content::WebContents* contents) {
928 Browser* inspected_browser = NULL;
929 int inspected_tab_index = -1;
930 if (is_docked_ && FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
931 &inspected_browser,
932 &inspected_tab_index))
933 inspected_browser->window()->WebContentsFocused(contents);
936 void DevToolsWindow::DispatchOnEmbedder(const std::string& message) {
937 std::string method;
938 base::ListValue empty_params;
939 base::ListValue* params = &empty_params;
941 base::DictionaryValue* dict = NULL;
942 scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
943 if (!parsed_message ||
944 !parsed_message->GetAsDictionary(&dict) ||
945 !dict->GetString(kFrontendHostMethod, &method) ||
946 (dict->HasKey(kFrontendHostParams) &&
947 !dict->GetList(kFrontendHostParams, &params))) {
948 LOG(ERROR) << "Invalid message was sent to embedder: " << message;
949 return;
952 int id = 0;
953 dict->GetInteger(kFrontendHostId, &id);
955 std::string error = embedder_message_dispatcher_->Dispatch(method, params);
956 if (id) {
957 scoped_ptr<base::Value> id_value(base::Value::CreateIntegerValue(id));
958 scoped_ptr<base::Value> error_value(base::Value::CreateStringValue(error));
959 CallClientFunction("InspectorFrontendAPI.embedderMessageAck",
960 id_value.get(), error_value.get(), NULL);
964 void DevToolsWindow::ActivateWindow() {
965 if (is_docked_ && GetInspectedBrowserWindow())
966 web_contents_->GetView()->Focus();
967 else if (!is_docked_ && !browser_->window()->IsActive())
968 browser_->window()->Activate();
971 void DevToolsWindow::ActivateContents(content::WebContents* contents) {
972 if (is_docked_) {
973 content::WebContents* inspected_tab = this->GetInspectedWebContents();
974 inspected_tab->GetDelegate()->ActivateContents(inspected_tab);
975 } else {
976 browser_->window()->Activate();
980 void DevToolsWindow::CloseWindow() {
981 DCHECK(is_docked_);
982 // This will prevent any activity after frontend is loaded.
983 action_on_load_ = DevToolsToggleAction::NoOp();
984 web_contents_->GetRenderViewHost()->FirePageBeforeUnload(false);
987 void DevToolsWindow::SetContentsInsets(
988 int top, int left, int bottom, int right) {
989 if (contents_insets_.top() == top &&
990 contents_insets_.left() == left &&
991 contents_insets_.bottom() == bottom &&
992 contents_insets_.right() == right) {
993 return;
996 contents_insets_ = gfx::Insets(top, left, bottom, right);
997 if (is_docked_) {
998 // Update inspected window.
999 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1000 if (inspected_window)
1001 inspected_window->UpdateDevTools();
1005 void DevToolsWindow::MoveWindow(int x, int y) {
1006 if (!is_docked_) {
1007 gfx::Rect bounds = browser_->window()->GetBounds();
1008 bounds.Offset(x, y);
1009 browser_->window()->SetBounds(bounds);
1013 void DevToolsWindow::SetIsDockedAndShowImmediatelyForTest(bool is_docked) {
1014 DCHECK(!is_docked || can_dock_);
1015 if (load_state_ == kLoadCompleted) {
1016 SetIsDocked(is_docked);
1017 } else {
1018 is_docked_ = is_docked;
1019 load_state_ = load_state_ == kNotLoaded ? kIsDockedSet : kLoadCompleted;
1020 // Note that action_on_load_ will be performed after the load is actually
1021 // completed. For now, just show the window.
1022 Show(DevToolsToggleAction::Show());
1023 if (load_state_ == kLoadCompleted)
1024 LoadCompleted();
1026 ignore_set_is_docked_for_test_ = true;
1029 void DevToolsWindow::SetDockSide(const std::string& side) {
1030 // TODO(dgozman): remove this method after frontend switches to SetIsDocked.
1031 SetIsDocked(IsDockedFromString(side));
1034 void DevToolsWindow::SetIsDocked(bool dock_requested) {
1035 if (ignore_set_is_docked_for_test_)
1036 return;
1038 DCHECK(can_dock_ || !dock_requested);
1039 if (!can_dock_)
1040 dock_requested = false;
1042 bool was_docked = is_docked_;
1043 is_docked_ = dock_requested;
1045 if (load_state_ != kLoadCompleted) {
1046 // This is a first time call we waited for to initialize.
1047 load_state_ = load_state_ == kNotLoaded ? kIsDockedSet : kLoadCompleted;
1048 if (load_state_ == kLoadCompleted)
1049 LoadCompleted();
1050 return;
1053 if (dock_requested == was_docked)
1054 return;
1056 if (dock_requested && !was_docked) {
1057 // Detach window from the external devtools browser. It will lead to
1058 // the browser object's close and delete. Remove observer first.
1059 TabStripModel* tab_strip_model = browser_->tab_strip_model();
1060 tab_strip_model->DetachWebContentsAt(
1061 tab_strip_model->GetIndexOfWebContents(web_contents_));
1062 browser_ = NULL;
1063 } else if (!dock_requested && was_docked) {
1064 // Update inspected window to hide split and reset it.
1065 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1066 if (inspected_window)
1067 inspected_window->UpdateDevTools();
1070 Show(DevToolsToggleAction::Show());
1073 void DevToolsWindow::OpenInNewTab(const std::string& url) {
1074 content::OpenURLParams params(
1075 GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
1076 content::PAGE_TRANSITION_LINK, false);
1077 content::WebContents* inspected_web_contents = GetInspectedWebContents();
1078 if (inspected_web_contents) {
1079 inspected_web_contents->OpenURL(params);
1080 } else {
1081 chrome::HostDesktopType host_desktop_type;
1082 if (browser_) {
1083 host_desktop_type = browser_->host_desktop_type();
1084 } else {
1085 // There should always be a browser when there are no inspected web
1086 // contents.
1087 NOTREACHED();
1088 host_desktop_type = chrome::GetActiveDesktop();
1091 const BrowserList* browser_list =
1092 BrowserList::GetInstance(host_desktop_type);
1093 for (BrowserList::const_iterator it = browser_list->begin();
1094 it != browser_list->end(); ++it) {
1095 if ((*it)->type() == Browser::TYPE_TABBED) {
1096 (*it)->OpenURL(params);
1097 break;
1103 void DevToolsWindow::SaveToFile(const std::string& url,
1104 const std::string& content,
1105 bool save_as) {
1106 file_helper_->Save(url, content, save_as,
1107 base::Bind(&DevToolsWindow::FileSavedAs,
1108 weak_factory_.GetWeakPtr(), url),
1109 base::Bind(&DevToolsWindow::CanceledFileSaveAs,
1110 weak_factory_.GetWeakPtr(), url));
1113 void DevToolsWindow::AppendToFile(const std::string& url,
1114 const std::string& content) {
1115 file_helper_->Append(url, content,
1116 base::Bind(&DevToolsWindow::AppendedTo,
1117 weak_factory_.GetWeakPtr(), url));
1120 void DevToolsWindow::RequestFileSystems() {
1121 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1122 file_helper_->RequestFileSystems(base::Bind(
1123 &DevToolsWindow::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
1126 void DevToolsWindow::AddFileSystem() {
1127 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1128 file_helper_->AddFileSystem(
1129 base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
1130 base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
1131 weak_factory_.GetWeakPtr()));
1134 void DevToolsWindow::RemoveFileSystem(const std::string& file_system_path) {
1135 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1136 file_helper_->RemoveFileSystem(file_system_path);
1137 base::StringValue file_system_path_value(file_system_path);
1138 CallClientFunction("InspectorFrontendAPI.fileSystemRemoved",
1139 &file_system_path_value, NULL, NULL);
1142 void DevToolsWindow::UpgradeDraggedFileSystemPermissions(
1143 const std::string& file_system_url) {
1144 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1145 file_helper_->UpgradeDraggedFileSystemPermissions(
1146 file_system_url,
1147 base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
1148 base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
1149 weak_factory_.GetWeakPtr()));
1152 void DevToolsWindow::IndexPath(int request_id,
1153 const std::string& file_system_path) {
1154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1155 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1156 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
1157 IndexingDone(request_id, file_system_path);
1158 return;
1160 indexing_jobs_[request_id] =
1161 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
1162 file_system_indexer_->IndexPath(
1163 file_system_path,
1164 Bind(&DevToolsWindow::IndexingTotalWorkCalculated,
1165 weak_factory_.GetWeakPtr(),
1166 request_id,
1167 file_system_path),
1168 Bind(&DevToolsWindow::IndexingWorked,
1169 weak_factory_.GetWeakPtr(),
1170 request_id,
1171 file_system_path),
1172 Bind(&DevToolsWindow::IndexingDone,
1173 weak_factory_.GetWeakPtr(),
1174 request_id,
1175 file_system_path)));
1178 void DevToolsWindow::StopIndexing(int request_id) {
1179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1180 IndexingJobsMap::iterator it = indexing_jobs_.find(request_id);
1181 if (it == indexing_jobs_.end())
1182 return;
1183 it->second->Stop();
1184 indexing_jobs_.erase(it);
1187 void DevToolsWindow::SearchInPath(int request_id,
1188 const std::string& file_system_path,
1189 const std::string& query) {
1190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1191 CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1192 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
1193 SearchCompleted(request_id, file_system_path, std::vector<std::string>());
1194 return;
1196 file_system_indexer_->SearchInPath(file_system_path,
1197 query,
1198 Bind(&DevToolsWindow::SearchCompleted,
1199 weak_factory_.GetWeakPtr(),
1200 request_id,
1201 file_system_path));
1204 void DevToolsWindow::FileSavedAs(const std::string& url) {
1205 base::StringValue url_value(url);
1206 CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL);
1209 void DevToolsWindow::CanceledFileSaveAs(const std::string& url) {
1210 base::StringValue url_value(url);
1211 CallClientFunction("InspectorFrontendAPI.canceledSaveURL",
1212 &url_value, NULL, NULL);
1215 void DevToolsWindow::AppendedTo(const std::string& url) {
1216 base::StringValue url_value(url);
1217 CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL,
1218 NULL);
1221 void DevToolsWindow::FileSystemsLoaded(
1222 const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
1223 base::ListValue file_systems_value;
1224 for (size_t i = 0; i < file_systems.size(); ++i)
1225 file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
1226 CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded",
1227 &file_systems_value, NULL, NULL);
1230 void DevToolsWindow::FileSystemAdded(
1231 const DevToolsFileHelper::FileSystem& file_system) {
1232 scoped_ptr<base::StringValue> error_string_value(
1233 new base::StringValue(std::string()));
1234 scoped_ptr<base::DictionaryValue> file_system_value;
1235 if (!file_system.file_system_path.empty())
1236 file_system_value.reset(CreateFileSystemValue(file_system));
1237 CallClientFunction("InspectorFrontendAPI.fileSystemAdded",
1238 error_string_value.get(), file_system_value.get(), NULL);
1241 void DevToolsWindow::IndexingTotalWorkCalculated(
1242 int request_id,
1243 const std::string& file_system_path,
1244 int total_work) {
1245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1246 base::FundamentalValue request_id_value(request_id);
1247 base::StringValue file_system_path_value(file_system_path);
1248 base::FundamentalValue total_work_value(total_work);
1249 CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated",
1250 &request_id_value, &file_system_path_value,
1251 &total_work_value);
1254 void DevToolsWindow::IndexingWorked(int request_id,
1255 const std::string& file_system_path,
1256 int worked) {
1257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1258 base::FundamentalValue request_id_value(request_id);
1259 base::StringValue file_system_path_value(file_system_path);
1260 base::FundamentalValue worked_value(worked);
1261 CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value,
1262 &file_system_path_value, &worked_value);
1265 void DevToolsWindow::IndexingDone(int request_id,
1266 const std::string& file_system_path) {
1267 indexing_jobs_.erase(request_id);
1268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1269 base::FundamentalValue request_id_value(request_id);
1270 base::StringValue file_system_path_value(file_system_path);
1271 CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value,
1272 &file_system_path_value, NULL);
1275 void DevToolsWindow::SearchCompleted(
1276 int request_id,
1277 const std::string& file_system_path,
1278 const std::vector<std::string>& file_paths) {
1279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1280 base::ListValue file_paths_value;
1281 for (std::vector<std::string>::const_iterator it(file_paths.begin());
1282 it != file_paths.end(); ++it) {
1283 file_paths_value.AppendString(*it);
1285 base::FundamentalValue request_id_value(request_id);
1286 base::StringValue file_system_path_value(file_system_path);
1287 CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value,
1288 &file_system_path_value, &file_paths_value);
1291 void DevToolsWindow::ShowDevToolsConfirmInfoBar(
1292 const base::string16& message,
1293 const InfoBarCallback& callback) {
1294 DevToolsConfirmInfoBarDelegate::Create(
1295 is_docked_ ?
1296 InfoBarService::FromWebContents(GetInspectedWebContents()) :
1297 InfoBarService::FromWebContents(web_contents_),
1298 callback, message);
1301 void DevToolsWindow::CreateDevToolsBrowser() {
1302 std::string wp_key = GetDevToolsWindowPlacementPrefKey();
1303 PrefService* prefs = profile_->GetPrefs();
1304 const base::DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
1305 if (!wp_pref || wp_pref->empty()) {
1306 DictionaryPrefUpdate update(prefs, wp_key.c_str());
1307 base::DictionaryValue* defaults = update.Get();
1308 defaults->SetInteger("left", 100);
1309 defaults->SetInteger("top", 100);
1310 defaults->SetInteger("right", 740);
1311 defaults->SetInteger("bottom", 740);
1312 defaults->SetBoolean("maximized", false);
1313 defaults->SetBoolean("always_on_top", false);
1316 browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
1317 profile_,
1318 chrome::GetHostDesktopTypeForNativeView(
1319 web_contents_->GetView()->GetNativeView())));
1320 browser_->tab_strip_model()->AddWebContents(
1321 web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1322 TabStripModel::ADD_ACTIVE);
1323 GetRenderViewHost()->SyncRendererPrefs();
1326 // static
1327 bool DevToolsWindow::FindInspectedBrowserAndTabIndex(
1328 content::WebContents* inspected_web_contents, Browser** browser, int* tab) {
1329 if (!inspected_web_contents)
1330 return false;
1332 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1333 int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
1334 inspected_web_contents);
1335 if (tab_index != TabStripModel::kNoTab) {
1336 *browser = *it;
1337 *tab = tab_index;
1338 return true;
1341 return false;
1344 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
1345 Browser* browser = NULL;
1346 int tab;
1347 return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
1348 &browser, &tab) ?
1349 browser->window() : NULL;
1352 void DevToolsWindow::DoAction(const DevToolsToggleAction& action) {
1353 switch (action.type()) {
1354 case DevToolsToggleAction::kShowConsole:
1355 CallClientFunction("InspectorFrontendAPI.showConsole", NULL, NULL, NULL);
1356 break;
1358 case DevToolsToggleAction::kInspect:
1359 CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL,
1360 NULL, NULL);
1361 break;
1363 case DevToolsToggleAction::kShow:
1364 case DevToolsToggleAction::kToggle:
1365 // Do nothing.
1366 break;
1368 case DevToolsToggleAction::kReveal: {
1369 const DevToolsToggleAction::RevealParams* params =
1370 action.params();
1371 CHECK(params);
1372 base::StringValue url_value(params->url);
1373 base::FundamentalValue line_value(static_cast<int>(params->line_number));
1374 base::FundamentalValue column_value(
1375 static_cast<int>(params->column_number));
1376 CallClientFunction("InspectorFrontendAPI.revealSourceLine",
1377 &url_value,
1378 &line_value,
1379 &column_value);
1380 break;
1382 default:
1383 NOTREACHED();
1384 break;
1388 void DevToolsWindow::UpdateTheme() {
1389 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
1390 DCHECK(tp);
1392 std::string command("InspectorFrontendAPI.setToolbarColors(\"" +
1393 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
1394 "\", \"" +
1395 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) +
1396 "\")");
1397 web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
1398 base::string16(), base::ASCIIToUTF16(command));
1401 void DevToolsWindow::AddDevToolsExtensionsToClient() {
1402 content::WebContents* inspected_web_contents = GetInspectedWebContents();
1403 if (inspected_web_contents) {
1404 SessionTabHelper* session_tab_helper =
1405 SessionTabHelper::FromWebContents(inspected_web_contents);
1406 if (session_tab_helper) {
1407 base::FundamentalValue tabId(session_tab_helper->session_id().id());
1408 CallClientFunction("WebInspector.setInspectedTabId", &tabId, NULL, NULL);
1412 Profile* profile =
1413 Profile::FromBrowserContext(web_contents_->GetBrowserContext());
1414 const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
1415 profile->GetOriginalProfile())->extension_service();
1416 if (!extension_service)
1417 return;
1418 const extensions::ExtensionSet* extensions = extension_service->extensions();
1420 base::ListValue results;
1421 for (extensions::ExtensionSet::const_iterator extension(extensions->begin());
1422 extension != extensions->end(); ++extension) {
1423 if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
1424 continue;
1425 base::DictionaryValue* extension_info = new base::DictionaryValue();
1426 extension_info->Set(
1427 "startPage",
1428 new base::StringValue(
1429 extensions::ManifestURL::GetDevToolsPage(extension->get()).spec()));
1430 extension_info->Set("name", new base::StringValue((*extension)->name()));
1431 extension_info->Set(
1432 "exposeExperimentalAPIs",
1433 new base::FundamentalValue((*extension)->HasAPIPermission(
1434 extensions::APIPermission::kExperimental)));
1435 results.Append(extension_info);
1437 CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL);
1440 void DevToolsWindow::CallClientFunction(const std::string& function_name,
1441 const base::Value* arg1,
1442 const base::Value* arg2,
1443 const base::Value* arg3) {
1444 std::string params;
1445 if (arg1) {
1446 std::string json;
1447 base::JSONWriter::Write(arg1, &json);
1448 params.append(json);
1449 if (arg2) {
1450 base::JSONWriter::Write(arg2, &json);
1451 params.append(", " + json);
1452 if (arg3) {
1453 base::JSONWriter::Write(arg3, &json);
1454 params.append(", " + json);
1458 base::string16 javascript =
1459 base::ASCIIToUTF16(function_name + "(" + params + ");");
1460 web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
1461 base::string16(), javascript);
1464 void DevToolsWindow::UpdateBrowserToolbar() {
1465 BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1466 if (inspected_window)
1467 inspected_window->UpdateToolbar(NULL);
1470 content::WebContents* DevToolsWindow::GetInspectedWebContents() {
1471 return inspected_contents_observer_ ?
1472 inspected_contents_observer_->web_contents() : NULL;
1475 void DevToolsWindow::DocumentOnLoadCompletedInMainFrame() {
1476 load_state_ = load_state_ == kNotLoaded ? kOnLoadFired : kLoadCompleted;
1477 if (load_state_ == kLoadCompleted)
1478 LoadCompleted();
1481 void DevToolsWindow::LoadCompleted() {
1482 Show(action_on_load_);
1483 action_on_load_ = DevToolsToggleAction::NoOp();
1484 UpdateTheme();
1485 AddDevToolsExtensionsToClient();