// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/devtools/devtools_window.h" #include <algorithm> #include "base/command_line.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/prefs/scoped_user_pref_update.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/debugger/debugger_api.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_web_contents_observer.h" #include "chrome/browser/file_select_helper.h" #include "chrome/browser/infobars/confirm_infobar_delegate.h" #include "chrome/browser/infobars/infobar.h" #include "chrome/browser/prefs/pref_service_syncable.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/prefs/prefs_tab_helper.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/webui/devtools_ui.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "chrome/common/pref_names.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "components/user_prefs/pref_registry_syncable.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_client_host.h" #include "content/public/browser/devtools_manager.h" #include "content/public/browser/favicon_status.h" #include "content/public/browser/load_notification_details.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_view.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/content_client.h" #include "content/public/common/page_transition_types.h" #include "content/public/common/url_constants.h" #include "extensions/common/extension_set.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" using base::DictionaryValue; using content::BrowserThread; using content::DevToolsAgentHost; // DevToolsConfirmInfoBarDelegate --------------------------------------------- class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate { public: // If |infobar_service| is NULL, runs |callback| with a single argument with // value "false". Otherwise, creates a dev tools confirm infobar and delegate // and adds the inofbar to |infobar_service|. static void Create(InfoBarService* infobar_service, const DevToolsWindow::InfoBarCallback& callback, const base::string16& message); private: DevToolsConfirmInfoBarDelegate( const DevToolsWindow::InfoBarCallback& callback, const base::string16& message); virtual ~DevToolsConfirmInfoBarDelegate(); virtual base::string16 GetMessageText() const OVERRIDE; virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; virtual bool Accept() OVERRIDE; virtual bool Cancel() OVERRIDE; DevToolsWindow::InfoBarCallback callback_; const base::string16 message_; DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate); }; void DevToolsConfirmInfoBarDelegate::Create( InfoBarService* infobar_service, const DevToolsWindow::InfoBarCallback& callback, const base::string16& message) { if (!infobar_service) { callback.Run(false); return; } infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( scoped_ptr<ConfirmInfoBarDelegate>( new DevToolsConfirmInfoBarDelegate(callback, message)))); } DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate( const DevToolsWindow::InfoBarCallback& callback, const base::string16& message) : ConfirmInfoBarDelegate(), callback_(callback), message_(message) { } DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() { if (!callback_.is_null()) callback_.Run(false); } base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const { return message_; } base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel( InfoBarButton button) const { return l10n_util::GetStringUTF16((button == BUTTON_OK) ? IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON); } bool DevToolsConfirmInfoBarDelegate::Accept() { callback_.Run(true); callback_.Reset(); return true; } bool DevToolsConfirmInfoBarDelegate::Cancel() { callback_.Run(false); callback_.Reset(); return true; } // DevToolsWindow::InspectedWebContentsObserver ------------------------------- class DevToolsWindow::InspectedWebContentsObserver : public content::WebContentsObserver { public: explicit InspectedWebContentsObserver(content::WebContents* web_contents); virtual ~InspectedWebContentsObserver(); content::WebContents* web_contents() { return WebContentsObserver::web_contents(); } private: DISALLOW_COPY_AND_ASSIGN(InspectedWebContentsObserver); }; DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver( content::WebContents* web_contents) : WebContentsObserver(web_contents) { } DevToolsWindow::InspectedWebContentsObserver::~InspectedWebContentsObserver() { } // DevToolsWindow::FrontendWebContentsObserver -------------------------------- class DevToolsWindow::FrontendWebContentsObserver : public content::WebContentsObserver { public: explicit FrontendWebContentsObserver(DevToolsWindow* window); virtual ~FrontendWebContentsObserver(); private: // contents::WebContentsObserver: virtual void AboutToNavigateRenderView( content::RenderViewHost* render_view_host) OVERRIDE; virtual void DocumentOnLoadCompletedInMainFrame(int32 page_id) OVERRIDE; virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE; DevToolsWindow* devtools_window_; DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver); }; DevToolsWindow::FrontendWebContentsObserver::FrontendWebContentsObserver( DevToolsWindow* devtools_window) : WebContentsObserver(devtools_window->web_contents()), devtools_window_(devtools_window) { } void DevToolsWindow::FrontendWebContentsObserver::WebContentsDestroyed( content::WebContents* contents) { delete devtools_window_; } DevToolsWindow::FrontendWebContentsObserver::~FrontendWebContentsObserver() { } void DevToolsWindow::FrontendWebContentsObserver::AboutToNavigateRenderView( content::RenderViewHost* render_view_host) { content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host); } void DevToolsWindow::FrontendWebContentsObserver:: DocumentOnLoadCompletedInMainFrame(int32 page_id) { devtools_window_->DocumentOnLoadCompletedInMainFrame(); } // DevToolsWindow ------------------------------------------------------------- namespace { typedef std::vector<DevToolsWindow*> DevToolsWindows; base::LazyInstance<DevToolsWindows>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; const char kPrefBottom[] = "dock_bottom"; const char kPrefRight[] = "dock_right"; const char kPrefUndocked[] = "undocked"; const char kDockSideBottom[] = "bottom"; const char kDockSideRight[] = "right"; const char kDockSideUndocked[] = "undocked"; const char kDockSideMinimized[] = "minimized"; static const char kFrontendHostId[] = "id"; static const char kFrontendHostMethod[] = "method"; static const char kFrontendHostParams[] = "params"; std::string SkColorToRGBAString(SkColor color) { // We avoid StringPrintf because it will use locale specific formatters for // the double (e.g. ',' instead of '.' in German). return "rgba(" + base::IntToString(SkColorGetR(color)) + "," + base::IntToString(SkColorGetG(color)) + "," + base::IntToString(SkColorGetB(color)) + "," + base::DoubleToString(SkColorGetA(color) / 255.0) + ")"; } base::DictionaryValue* CreateFileSystemValue( DevToolsFileHelper::FileSystem file_system) { base::DictionaryValue* file_system_value = new base::DictionaryValue(); file_system_value->SetString("fileSystemName", file_system.file_system_name); file_system_value->SetString("rootURL", file_system.root_url); file_system_value->SetString("fileSystemPath", file_system.file_system_path); return file_system_value; } } // namespace const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp"; DevToolsWindow::~DevToolsWindow() { content::DevToolsManager::GetInstance()->ClientHostClosing( frontend_host_.get()); UpdateBrowserToolbar(); DevToolsWindows* instances = &g_instances.Get(); DevToolsWindows::iterator it( std::find(instances->begin(), instances->end(), this)); DCHECK(it != instances->end()); instances->erase(it); for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin()); jobs_it != indexing_jobs_.end(); ++jobs_it) { jobs_it->second->Stop(); } indexing_jobs_.clear(); } // static std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() { return std::string(prefs::kBrowserWindowPlacement) + "_" + std::string(kDevToolsApp); } // static void DevToolsWindow::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( prefs::kDevToolsOpenDocked, true, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterStringPref( prefs::kDevToolsDockSide, kDockSideBottom, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterDictionaryPref( prefs::kDevToolsEditedFiles, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterDictionaryPref( prefs::kDevToolsFileSystemPaths, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterStringPref( prefs::kDevToolsAdbKey, std::string(), user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterDictionaryPref( GetDevToolsWindowPlacementPrefKey().c_str(), user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterBooleanPref( prefs::kDevToolsDiscoverUsbDevicesEnabled, false, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterBooleanPref( prefs::kDevToolsPortForwardingEnabled, false, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterBooleanPref( prefs::kDevToolsPortForwardingDefaultSet, false, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterDictionaryPref( prefs::kDevToolsPortForwardingConfig, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); } // static DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab( content::WebContents* inspected_web_contents) { DevToolsWindow* window = GetInstanceForInspectedRenderViewHost( inspected_web_contents->GetRenderViewHost()); return (window && window->IsDocked()) ? window : NULL; } // static DevToolsWindow* DevToolsWindow::GetInstanceForInspectedRenderViewHost( content::RenderViewHost* inspected_rvh) { if (!inspected_rvh || !DevToolsAgentHost::HasFor(inspected_rvh)) return NULL; scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetOrCreateFor( inspected_rvh)); return FindDevToolsWindow(agent.get()); } // static bool DevToolsWindow::IsDevToolsWindow(content::RenderViewHost* window_rvh) { return AsDevToolsWindow(window_rvh) != NULL; } // static DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker( Profile* profile, DevToolsAgentHost* worker_agent) { DevToolsWindow* window = FindDevToolsWindow(worker_agent); if (!window) { window = DevToolsWindow::CreateDevToolsWindowForWorker(profile); // Will disconnect the current client host if there is one. content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( worker_agent, window->frontend_host_.get()); } window->Show(DevToolsToggleAction::Show()); return window; } // static DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker( Profile* profile) { content::RecordAction(base::UserMetricsAction("DevTools_InspectWorker")); return Create(profile, GURL(), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true, false, false); } // static DevToolsWindow* DevToolsWindow::OpenDevToolsWindow( content::RenderViewHost* inspected_rvh) { return ToggleDevToolsWindow( inspected_rvh, true, DevToolsToggleAction::Show()); } // static DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow( Browser* browser, const DevToolsToggleAction& action) { if (action.type() == DevToolsToggleAction::kToggle && browser->is_devtools()) { browser->tab_strip_model()->CloseAllTabs(); return NULL; } return ToggleDevToolsWindow( browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(), action.type() == DevToolsToggleAction::kInspect, action); } // static void DevToolsWindow::OpenExternalFrontend( Profile* profile, const std::string& frontend_url, content::DevToolsAgentHost* agent_host) { DevToolsWindow* window = FindDevToolsWindow(agent_host); if (!window) { window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, false, true, false); content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( agent_host, window->frontend_host_.get()); } window->Show(DevToolsToggleAction::Show()); } // static DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow( content::RenderViewHost* inspected_rvh, bool force_open, const DevToolsToggleAction& action) { scoped_refptr<DevToolsAgentHost> agent( DevToolsAgentHost::GetOrCreateFor(inspected_rvh)); content::DevToolsManager* manager = content::DevToolsManager::GetInstance(); DevToolsWindow* window = FindDevToolsWindow(agent.get()); bool do_open = force_open; if (!window) { Profile* profile = Profile::FromBrowserContext( inspected_rvh->GetProcess()->GetBrowserContext()); DevToolsDockSide dock_side = GetDockSideFromPrefs(profile); content::RecordAction( base::UserMetricsAction("DevTools_InspectRenderer")); window = Create(profile, GURL(), inspected_rvh, dock_side, false, false, true); manager->RegisterDevToolsClientHostFor(agent.get(), window->frontend_host_.get()); do_open = true; } // Update toolbar to reflect DevTools changes. window->UpdateBrowserToolbar(); // If window is docked and visible, we hide it on toggle. If window is // undocked, we show (activate) it. If window is minimized, we maximize it. if (window->dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED) window->Restore(); else if (!window->IsDocked() || do_open) window->Show(action); else window->CloseWindow(); return window; } // static void DevToolsWindow::InspectElement(content::RenderViewHost* inspected_rvh, int x, int y) { scoped_refptr<DevToolsAgentHost> agent( DevToolsAgentHost::GetOrCreateFor(inspected_rvh)); agent->InspectElement(x, y); // TODO(loislo): we should initiate DevTools window opening from within // renderer. Otherwise, we still can hit a race condition here. OpenDevToolsWindow(inspected_rvh); } // static int DevToolsWindow::GetMinimizedHeight() { const int kMinimizedDevToolsHeight = 24; return kMinimizedDevToolsHeight; } void DevToolsWindow::InspectedContentsClosing() { intercepted_page_beforeunload_ = false; web_contents_->GetRenderViewHost()->ClosePage(); } content::RenderViewHost* DevToolsWindow::GetRenderViewHost() { return web_contents_->GetRenderViewHost(); } content::DevToolsClientHost* DevToolsWindow::GetDevToolsClientHostForTest() { return frontend_host_.get(); } gfx::Insets DevToolsWindow::GetContentsInsets() const { return contents_insets_; } gfx::Size DevToolsWindow::GetMinimumSize() const { const gfx::Size kMinDevToolsSize = gfx::Size(200, 100); return kMinDevToolsSize; } void DevToolsWindow::Show(const DevToolsToggleAction& action) { if (IsDocked()) { Browser* inspected_browser = NULL; int inspected_tab_index = -1; // Tell inspected browser to update splitter and switch to inspected panel. if (!IsInspectedBrowserPopup() && FindInspectedBrowserAndTabIndex(GetInspectedWebContents(), &inspected_browser, &inspected_tab_index)) { BrowserWindow* inspected_window = inspected_browser->window(); web_contents_->SetDelegate(this); inspected_window->UpdateDevTools(); web_contents_->GetView()->SetInitialFocus(); inspected_window->Show(); TabStripModel* tab_strip_model = inspected_browser->tab_strip_model(); tab_strip_model->ActivateTabAt(inspected_tab_index, true); PrefsTabHelper::CreateForWebContents(web_contents_); GetRenderViewHost()->SyncRendererPrefs(); ScheduleAction(action); return; } // Sometimes we don't know where to dock. Stay undocked. dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED; } // Avoid consecutive window switching if the devtools window has been opened // and the Inspect Element shortcut is pressed in the inspected tab. bool should_show_window = !browser_ || (action.type() != DevToolsToggleAction::kInspect); if (!browser_) CreateDevToolsBrowser(); if (should_show_window) { browser_->window()->Show(); web_contents_->GetView()->SetInitialFocus(); } ScheduleAction(action); } // static bool DevToolsWindow::HandleBeforeUnload(content::WebContents* frontend_contents, bool proceed, bool* proceed_to_fire_unload) { DevToolsWindow* window = AsDevToolsWindow( frontend_contents->GetRenderViewHost()); if (!window) return false; if (!window->intercepted_page_beforeunload_) return false; window->BeforeUnloadFired(frontend_contents, proceed, proceed_to_fire_unload); return true; } // static bool DevToolsWindow::InterceptPageBeforeUnload(content::WebContents* contents) { DevToolsWindow* window = DevToolsWindow::GetInstanceForInspectedRenderViewHost( contents->GetRenderViewHost()); if (!window || window->intercepted_page_beforeunload_) return false; window->intercepted_page_beforeunload_ = true; // Handle case of devtools inspecting another devtools instance by passing // the call up to the inspecting devtools instance. if (!DevToolsWindow::InterceptPageBeforeUnload(window->web_contents())) { window->web_contents()->GetRenderViewHost()->FirePageBeforeUnload(false); } return true; } // static bool DevToolsWindow::NeedsToInterceptBeforeUnload( content::WebContents* contents) { DevToolsWindow* window = DevToolsWindow::GetInstanceForInspectedRenderViewHost( contents->GetRenderViewHost()); return window && !window->intercepted_page_beforeunload_; } // static bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser( Browser* browser) { DCHECK(browser->is_devtools()); // When FastUnloadController is used, devtools frontend will be detached // from the browser window at this point which means we've already fired // beforeunload. if (browser->tab_strip_model()->empty()) return true; content::WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(0); DevToolsWindow* window = AsDevToolsWindow(contents->GetRenderViewHost()); if (!window) return false; return window->intercepted_page_beforeunload_; } // static void DevToolsWindow::OnPageCloseCanceled(content::WebContents* contents) { DevToolsWindow *window = DevToolsWindow::GetInstanceForInspectedRenderViewHost( contents->GetRenderViewHost()); if (!window) return; window->intercepted_page_beforeunload_ = false; // Propagate to devtools opened on devtools if any. DevToolsWindow::OnPageCloseCanceled(window->web_contents()); } void DevToolsWindow::SetDockSideForTest(DevToolsDockSide dock_side) { SetDockSide(SideToString(dock_side)); } DevToolsWindow::DevToolsWindow(Profile* profile, const GURL& url, content::RenderViewHost* inspected_rvh, DevToolsDockSide dock_side) : profile_(profile), browser_(NULL), dock_side_(dock_side), is_loaded_(false), action_on_load_(DevToolsToggleAction::Show()), dock_side_before_minimized_(dock_side), intercepted_page_beforeunload_(false), weak_factory_(this) { web_contents_ = content::WebContents::Create(content::WebContents::CreateParams(profile)); frontend_contents_observer_.reset(new FrontendWebContentsObserver(this)); web_contents_->GetController().LoadURL(url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost( web_contents_, this)); file_helper_.reset(new DevToolsFileHelper(web_contents_, profile)); file_system_indexer_ = new DevToolsFileSystemIndexer(); extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents_); g_instances.Get().push_back(this); // Wipe out page icon so that the default application icon is used. content::NavigationEntry* entry = web_contents_->GetController().GetActiveEntry(); entry->GetFavicon().image = gfx::Image(); entry->GetFavicon().valid = true; // Register on-load actions. registrar_.Add( this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, content::Source<ThemeService>( ThemeServiceFactory::GetForProfile(profile_))); // There is no inspected_rvh in case of shared workers. if (inspected_rvh) inspected_contents_observer_.reset(new InspectedWebContentsObserver( content::WebContents::FromRenderViewHost(inspected_rvh))); embedder_message_dispatcher_.reset( new DevToolsEmbedderMessageDispatcher(this)); } // static DevToolsWindow* DevToolsWindow::Create( Profile* profile, const GURL& frontend_url, content::RenderViewHost* inspected_rvh, DevToolsDockSide dock_side, bool shared_worker_frontend, bool external_frontend, bool can_dock) { if (inspected_rvh) { // Check for a place to dock. Browser* browser = NULL; int tab; content::WebContents* inspected_web_contents = content::WebContents::FromRenderViewHost(inspected_rvh); if (!FindInspectedBrowserAndTabIndex(inspected_web_contents, &browser, &tab) || browser->is_type_popup()) { can_dock = false; } } // Create WebContents with devtools. GURL url(GetDevToolsURL(profile, frontend_url, dock_side, shared_worker_frontend, external_frontend, can_dock)); return new DevToolsWindow(profile, url, inspected_rvh, dock_side); } // static GURL DevToolsWindow::GetDevToolsURL(Profile* profile, const GURL& base_url, DevToolsDockSide dock_side, bool shared_worker_frontend, bool external_frontend, bool can_dock) { if (base_url.SchemeIs("data")) return base_url; std::string frontend_url( base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec()); ThemeService* tp = ThemeServiceFactory::GetForProfile(profile); DCHECK(tp); std::string url_string( frontend_url + ((frontend_url.find("?") == std::string::npos) ? "?" : "&") + "dockSide=" + SideToString(dock_side) + "&toolbarColor=" + SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) + "&textColor=" + SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT))); if (shared_worker_frontend) url_string += "&isSharedWorker=true"; if (external_frontend) url_string += "&remoteFrontend=true"; if (can_dock) url_string += "&can_dock=true"; if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableDevToolsExperiments)) url_string += "&experiments=true"; url_string += "&updateAppcache"; return GURL(url_string); } // static DevToolsWindow* DevToolsWindow::FindDevToolsWindow( DevToolsAgentHost* agent_host) { DevToolsWindows* instances = &g_instances.Get(); content::DevToolsManager* manager = content::DevToolsManager::GetInstance(); for (DevToolsWindows::iterator it(instances->begin()); it != instances->end(); ++it) { if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) == agent_host) return *it; } return NULL; } // static DevToolsWindow* DevToolsWindow::AsDevToolsWindow( content::RenderViewHost* window_rvh) { if (g_instances == NULL) return NULL; DevToolsWindows* instances = &g_instances.Get(); for (DevToolsWindows::iterator it(instances->begin()); it != instances->end(); ++it) { if ((*it)->web_contents_->GetRenderViewHost() == window_rvh) return *it; } return NULL; } // static DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) { std::string dock_side = profile->GetPrefs()->GetString(prefs::kDevToolsDockSide); // Migrate prefs. const char kOldPrefBottom[] = "bottom"; const char kOldPrefRight[] = "right"; if ((dock_side == kOldPrefBottom) || (dock_side == kOldPrefRight)) { if (!profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked)) return DEVTOOLS_DOCK_SIDE_UNDOCKED; return (dock_side == kOldPrefBottom) ? DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_RIGHT; } if (dock_side == kPrefUndocked) return DEVTOOLS_DOCK_SIDE_UNDOCKED; if (dock_side == kPrefRight) return DEVTOOLS_DOCK_SIDE_RIGHT; // Default to docked to bottom. return DEVTOOLS_DOCK_SIDE_BOTTOM; } // static std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) { switch (dock_side) { case DEVTOOLS_DOCK_SIDE_UNDOCKED: return kDockSideUndocked; case DEVTOOLS_DOCK_SIDE_RIGHT: return kDockSideRight; case DEVTOOLS_DOCK_SIDE_BOTTOM: return kDockSideBottom; case DEVTOOLS_DOCK_SIDE_MINIMIZED: return kDockSideMinimized; default: return kDockSideUndocked; } } // static DevToolsDockSide DevToolsWindow::SideFromString( const std::string& dock_side) { if (dock_side == kDockSideRight) return DEVTOOLS_DOCK_SIDE_RIGHT; if (dock_side == kDockSideBottom) return DEVTOOLS_DOCK_SIDE_BOTTOM; return (dock_side == kDockSideMinimized) ? DEVTOOLS_DOCK_SIDE_MINIMIZED : DEVTOOLS_DOCK_SIDE_UNDOCKED; } void DevToolsWindow::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type); UpdateTheme(); } content::WebContents* DevToolsWindow::OpenURLFromTab( content::WebContents* source, const content::OpenURLParams& params) { if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) { content::WebContents* inspected_web_contents = GetInspectedWebContents(); return inspected_web_contents ? inspected_web_contents->OpenURL(params) : NULL; } content::DevToolsManager* manager = content::DevToolsManager::GetInstance(); scoped_refptr<DevToolsAgentHost> agent_host( manager->GetDevToolsAgentHostFor(frontend_host_.get())); if (!agent_host.get()) return NULL; manager->ClientHostClosing(frontend_host_.get()); manager->RegisterDevToolsClientHostFor(agent_host.get(), frontend_host_.get()); chrome::NavigateParams nav_params(profile_, params.url, params.transition); FillNavigateParamsFromOpenURLParams(&nav_params, params); nav_params.source_contents = source; nav_params.tabstrip_add_types = TabStripModel::ADD_NONE; nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW; nav_params.user_gesture = params.user_gesture; chrome::Navigate(&nav_params); return nav_params.target_contents; } void DevToolsWindow::AddNewContents(content::WebContents* source, content::WebContents* new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture, bool* was_blocked) { content::WebContents* inspected_web_contents = GetInspectedWebContents(); if (inspected_web_contents) { inspected_web_contents->GetDelegate()->AddNewContents( source, new_contents, disposition, initial_pos, user_gesture, was_blocked); } } void DevToolsWindow::CloseContents(content::WebContents* source) { CHECK(IsDocked()); // Update dev tools to reflect removed dev tools window. BrowserWindow* inspected_window = GetInspectedBrowserWindow(); if (inspected_window) inspected_window->UpdateDevTools(); // In case of docked web_contents_, we own it so delete here. // Embedding DevTools window will be deleted as a result of // WebContentsDestroyed callback. delete web_contents_; } void DevToolsWindow::BeforeUnloadFired(content::WebContents* tab, bool proceed, bool* proceed_to_fire_unload) { if (!intercepted_page_beforeunload_) { // Docked devtools window closed directly. if (proceed) { content::DevToolsManager::GetInstance()->ClientHostClosing( frontend_host_.get()); } *proceed_to_fire_unload = proceed; } else { // Inspected page is attempting to close. content::WebContents* inspected_web_contents = GetInspectedWebContents(); if (proceed) { inspected_web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); } else { bool should_proceed; inspected_web_contents->GetDelegate()->BeforeUnloadFired( inspected_web_contents, false, &should_proceed); DCHECK(!should_proceed); } *proceed_to_fire_unload = false; } } bool DevToolsWindow::PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { if (IsDocked()) { BrowserWindow* inspected_window = GetInspectedBrowserWindow(); if (inspected_window) { return inspected_window->PreHandleKeyboardEvent(event, is_keyboard_shortcut); } } return false; } void DevToolsWindow::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { if (IsDocked()) { if (event.windowsKeyCode == 0x08) { // Do not navigate back in history on Windows (http://crbug.com/74156). return; } BrowserWindow* inspected_window = GetInspectedBrowserWindow(); if (inspected_window) inspected_window->HandleKeyboardEvent(event); } } content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() { content::WebContents* inspected_web_contents = GetInspectedWebContents(); return (inspected_web_contents && inspected_web_contents->GetDelegate()) ? inspected_web_contents->GetDelegate()->GetJavaScriptDialogManager() : content::WebContentsDelegate::GetJavaScriptDialogManager(); } content::ColorChooser* DevToolsWindow::OpenColorChooser( content::WebContents* web_contents, SkColor initial_color, const std::vector<content::ColorSuggestion>& suggestions) { return chrome::ShowColorChooser(web_contents, initial_color); } void DevToolsWindow::RunFileChooser(content::WebContents* web_contents, const content::FileChooserParams& params) { FileSelectHelper::RunFileChooser(web_contents, params); } void DevToolsWindow::WebContentsFocused(content::WebContents* contents) { Browser* inspected_browser = NULL; int inspected_tab_index = -1; if (IsDocked() && FindInspectedBrowserAndTabIndex(GetInspectedWebContents(), &inspected_browser, &inspected_tab_index)) inspected_browser->window()->WebContentsFocused(contents); } void DevToolsWindow::DispatchOnEmbedder(const std::string& message) { std::string method; base::ListValue empty_params; base::ListValue* params = &empty_params; base::DictionaryValue* dict = NULL; scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message)); if (!parsed_message || !parsed_message->GetAsDictionary(&dict) || !dict->GetString(kFrontendHostMethod, &method) || (dict->HasKey(kFrontendHostParams) && !dict->GetList(kFrontendHostParams, ¶ms))) { LOG(ERROR) << "Invalid message was sent to embedder: " << message; return; } int id = 0; dict->GetInteger(kFrontendHostId, &id); std::string error = embedder_message_dispatcher_->Dispatch(method, params); if (id) { scoped_ptr<base::Value> id_value(base::Value::CreateIntegerValue(id)); scoped_ptr<base::Value> error_value(base::Value::CreateStringValue(error)); CallClientFunction("InspectorFrontendAPI.embedderMessageAck", id_value.get(), error_value.get(), NULL); } } void DevToolsWindow::ActivateWindow() { if (IsDocked() && GetInspectedBrowserWindow()) web_contents_->GetView()->Focus(); else if (!IsDocked() && !browser_->window()->IsActive()) browser_->window()->Activate(); } void DevToolsWindow::ActivateContents(content::WebContents* contents) { if (IsDocked()) { content::WebContents* inspected_tab = this->GetInspectedWebContents(); inspected_tab->GetDelegate()->ActivateContents(inspected_tab); } else { browser_->window()->Activate(); } } void DevToolsWindow::CloseWindow() { DCHECK(IsDocked()); web_contents_->GetRenderViewHost()->FirePageBeforeUnload(false); } void DevToolsWindow::SetContentsInsets( int top, int left, int bottom, int right) { if (contents_insets_.top() == top && contents_insets_.left() == left && contents_insets_.bottom() == bottom && contents_insets_.right() == right) { return; } contents_insets_ = gfx::Insets(top, left, bottom, right); if (IsDocked()) { // Update inspected window. BrowserWindow* inspected_window = GetInspectedBrowserWindow(); if (inspected_window) inspected_window->UpdateDevTools(); } } void DevToolsWindow::MoveWindow(int x, int y) { if (!IsDocked()) { gfx::Rect bounds = browser_->window()->GetBounds(); bounds.Offset(x, y); browser_->window()->SetBounds(bounds); } } void DevToolsWindow::SetDockSide(const std::string& side) { DevToolsDockSide requested_side = SideFromString(side); bool dock_requested = requested_side != DEVTOOLS_DOCK_SIDE_UNDOCKED; bool is_docked = IsDocked(); if (dock_requested && (!GetInspectedWebContents() || !GetInspectedBrowserWindow() || IsInspectedBrowserPopup())) { // Cannot dock, avoid window flashing due to close-reopen cycle. return; } if ((dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) && (requested_side == DEVTOOLS_DOCK_SIDE_MINIMIZED)) dock_side_before_minimized_ = dock_side_; dock_side_ = requested_side; if (dock_requested && !is_docked) { // Detach window from the external devtools browser. It will lead to // the browser object's close and delete. Remove observer first. TabStripModel* tab_strip_model = browser_->tab_strip_model(); tab_strip_model->DetachWebContentsAt( tab_strip_model->GetIndexOfWebContents(web_contents_)); browser_ = NULL; } else if (!dock_requested && is_docked) { // Update inspected window to hide split and reset it. BrowserWindow* inspected_window = GetInspectedBrowserWindow(); if (inspected_window) inspected_window->UpdateDevTools(); } if (dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) { std::string pref_value = kPrefBottom; switch (dock_side_) { case DEVTOOLS_DOCK_SIDE_UNDOCKED: pref_value = kPrefUndocked; break; case DEVTOOLS_DOCK_SIDE_RIGHT: pref_value = kPrefRight; break; case DEVTOOLS_DOCK_SIDE_BOTTOM: pref_value = kPrefBottom; break; case DEVTOOLS_DOCK_SIDE_MINIMIZED: // We don't persist minimized state. break; } profile_->GetPrefs()->SetString(prefs::kDevToolsDockSide, pref_value); } Show(DevToolsToggleAction::Show()); } void DevToolsWindow::OpenInNewTab(const std::string& url) { content::OpenURLParams params( GURL(url), content::Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false); content::WebContents* inspected_web_contents = GetInspectedWebContents(); if (inspected_web_contents) { inspected_web_contents->OpenURL(params); } else { chrome::HostDesktopType host_desktop_type; if (browser_) { host_desktop_type = browser_->host_desktop_type(); } else { // There should always be a browser when there are no inspected web // contents. NOTREACHED(); host_desktop_type = chrome::GetActiveDesktop(); } const BrowserList* browser_list = BrowserList::GetInstance(host_desktop_type); for (BrowserList::const_iterator it = browser_list->begin(); it != browser_list->end(); ++it) { if ((*it)->type() == Browser::TYPE_TABBED) { (*it)->OpenURL(params); break; } } } } void DevToolsWindow::SaveToFile(const std::string& url, const std::string& content, bool save_as) { file_helper_->Save(url, content, save_as, base::Bind(&DevToolsWindow::FileSavedAs, weak_factory_.GetWeakPtr(), url), base::Bind(&DevToolsWindow::CanceledFileSaveAs, weak_factory_.GetWeakPtr(), url)); } void DevToolsWindow::AppendToFile(const std::string& url, const std::string& content) { file_helper_->Append(url, content, base::Bind(&DevToolsWindow::AppendedTo, weak_factory_.GetWeakPtr(), url)); } void DevToolsWindow::RequestFileSystems() { CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme)); file_helper_->RequestFileSystems(base::Bind( &DevToolsWindow::FileSystemsLoaded, weak_factory_.GetWeakPtr())); } void DevToolsWindow::AddFileSystem() { CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme)); file_helper_->AddFileSystem( base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()), base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar, weak_factory_.GetWeakPtr())); } void DevToolsWindow::RemoveFileSystem(const std::string& file_system_path) { CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme)); file_helper_->RemoveFileSystem(file_system_path); base::StringValue file_system_path_value(file_system_path); CallClientFunction("InspectorFrontendAPI.fileSystemRemoved", &file_system_path_value, NULL, NULL); } void DevToolsWindow::UpgradeDraggedFileSystemPermissions( const std::string& file_system_url) { CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme)); file_helper_->UpgradeDraggedFileSystemPermissions( file_system_url, base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()), base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar, weak_factory_.GetWeakPtr())); } void DevToolsWindow::IndexPath(int request_id, const std::string& file_system_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme)); if (!file_helper_->IsFileSystemAdded(file_system_path)) { IndexingDone(request_id, file_system_path); return; } indexing_jobs_[request_id] = scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>( file_system_indexer_->IndexPath( file_system_path, Bind(&DevToolsWindow::IndexingTotalWorkCalculated, weak_factory_.GetWeakPtr(), request_id, file_system_path), Bind(&DevToolsWindow::IndexingWorked, weak_factory_.GetWeakPtr(), request_id, file_system_path), Bind(&DevToolsWindow::IndexingDone, weak_factory_.GetWeakPtr(), request_id, file_system_path))); } void DevToolsWindow::StopIndexing(int request_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); IndexingJobsMap::iterator it = indexing_jobs_.find(request_id); if (it == indexing_jobs_.end()) return; it->second->Stop(); indexing_jobs_.erase(it); } void DevToolsWindow::SearchInPath(int request_id, const std::string& file_system_path, const std::string& query) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme)); if (!file_helper_->IsFileSystemAdded(file_system_path)) { SearchCompleted(request_id, file_system_path, std::vector<std::string>()); return; } file_system_indexer_->SearchInPath(file_system_path, query, Bind(&DevToolsWindow::SearchCompleted, weak_factory_.GetWeakPtr(), request_id, file_system_path)); } void DevToolsWindow::FileSavedAs(const std::string& url) { base::StringValue url_value(url); CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL); } void DevToolsWindow::CanceledFileSaveAs(const std::string& url) { base::StringValue url_value(url); CallClientFunction("InspectorFrontendAPI.canceledSaveURL", &url_value, NULL, NULL); } void DevToolsWindow::AppendedTo(const std::string& url) { base::StringValue url_value(url); CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL, NULL); } void DevToolsWindow::FileSystemsLoaded( const std::vector<DevToolsFileHelper::FileSystem>& file_systems) { base::ListValue file_systems_value; for (size_t i = 0; i < file_systems.size(); ++i) file_systems_value.Append(CreateFileSystemValue(file_systems[i])); CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded", &file_systems_value, NULL, NULL); } void DevToolsWindow::FileSystemAdded( const DevToolsFileHelper::FileSystem& file_system) { scoped_ptr<base::StringValue> error_string_value( new base::StringValue(std::string())); scoped_ptr<base::DictionaryValue> file_system_value; if (!file_system.file_system_path.empty()) file_system_value.reset(CreateFileSystemValue(file_system)); CallClientFunction("InspectorFrontendAPI.fileSystemAdded", error_string_value.get(), file_system_value.get(), NULL); } void DevToolsWindow::IndexingTotalWorkCalculated( int request_id, const std::string& file_system_path, int total_work) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FundamentalValue request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); base::FundamentalValue total_work_value(total_work); CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated", &request_id_value, &file_system_path_value, &total_work_value); } void DevToolsWindow::IndexingWorked(int request_id, const std::string& file_system_path, int worked) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FundamentalValue request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); base::FundamentalValue worked_value(worked); CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value, &file_system_path_value, &worked_value); } void DevToolsWindow::IndexingDone(int request_id, const std::string& file_system_path) { indexing_jobs_.erase(request_id); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FundamentalValue request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value, &file_system_path_value, NULL); } void DevToolsWindow::SearchCompleted( int request_id, const std::string& file_system_path, const std::vector<std::string>& file_paths) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::ListValue file_paths_value; for (std::vector<std::string>::const_iterator it(file_paths.begin()); it != file_paths.end(); ++it) { file_paths_value.AppendString(*it); } base::FundamentalValue request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value, &file_system_path_value, &file_paths_value); } void DevToolsWindow::ShowDevToolsConfirmInfoBar( const base::string16& message, const InfoBarCallback& callback) { DevToolsConfirmInfoBarDelegate::Create( IsDocked() ? InfoBarService::FromWebContents(GetInspectedWebContents()) : InfoBarService::FromWebContents(web_contents_), callback, message); } void DevToolsWindow::CreateDevToolsBrowser() { std::string wp_key = GetDevToolsWindowPlacementPrefKey(); PrefService* prefs = profile_->GetPrefs(); const base::DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str()); if (!wp_pref || wp_pref->empty()) { DictionaryPrefUpdate update(prefs, wp_key.c_str()); base::DictionaryValue* defaults = update.Get(); defaults->SetInteger("left", 100); defaults->SetInteger("top", 100); defaults->SetInteger("right", 740); defaults->SetInteger("bottom", 740); defaults->SetBoolean("maximized", false); defaults->SetBoolean("always_on_top", false); } browser_ = new Browser(Browser::CreateParams::CreateForDevTools( profile_, chrome::GetHostDesktopTypeForNativeView( web_contents_->GetView()->GetNativeView()))); browser_->tab_strip_model()->AddWebContents( web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL, TabStripModel::ADD_ACTIVE); GetRenderViewHost()->SyncRendererPrefs(); } // static bool DevToolsWindow::FindInspectedBrowserAndTabIndex( content::WebContents* inspected_web_contents, Browser** browser, int* tab) { if (!inspected_web_contents) return false; for (chrome::BrowserIterator it; !it.done(); it.Next()) { int tab_index = it->tab_strip_model()->GetIndexOfWebContents( inspected_web_contents); if (tab_index != TabStripModel::kNoTab) { *browser = *it; *tab = tab_index; return true; } } return false; } BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() { Browser* browser = NULL; int tab; return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(), &browser, &tab) ? browser->window() : NULL; } bool DevToolsWindow::IsInspectedBrowserPopup() { Browser* browser = NULL; int tab; return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(), &browser, &tab) && browser->is_type_popup(); } void DevToolsWindow::UpdateFrontendDockSide() { base::StringValue dock_side(SideToString(dock_side_)); CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side, NULL, NULL); base::FundamentalValue docked(IsDocked()); CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked, NULL, NULL); } void DevToolsWindow::ScheduleAction(const DevToolsToggleAction& action) { action_on_load_ = action; if (is_loaded_) DoAction(); } void DevToolsWindow::DoAction() { UpdateFrontendDockSide(); switch (action_on_load_.type()) { case DevToolsToggleAction::kShowConsole: CallClientFunction("InspectorFrontendAPI.showConsole", NULL, NULL, NULL); break; case DevToolsToggleAction::kInspect: CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL, NULL, NULL); break; case DevToolsToggleAction::kShow: case DevToolsToggleAction::kToggle: // Do nothing. break; case DevToolsToggleAction::kReveal: { const DevToolsToggleAction::RevealParams* params = action_on_load_.params(); CHECK(params); base::StringValue url_value(params->url); base::FundamentalValue line_value(static_cast<int>(params->line_number)); base::FundamentalValue column_value( static_cast<int>(params->column_number)); CallClientFunction("InspectorFrontendAPI.revealSourceLine", &url_value, &line_value, &column_value); break; } default: NOTREACHED(); break; } action_on_load_ = DevToolsToggleAction::Show(); } void DevToolsWindow::UpdateTheme() { ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_); DCHECK(tp); std::string command("InspectorFrontendAPI.setToolbarColors(\"" + SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) + "\", \"" + SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) + "\")"); web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame( base::string16(), base::ASCIIToUTF16(command)); } void DevToolsWindow::AddDevToolsExtensionsToClient() { content::WebContents* inspected_web_contents = GetInspectedWebContents(); if (inspected_web_contents) { SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(inspected_web_contents); if (session_tab_helper) { base::FundamentalValue tabId(session_tab_helper->session_id().id()); CallClientFunction("WebInspector.setInspectedTabId", &tabId, NULL, NULL); } } Profile* profile = Profile::FromBrowserContext(web_contents_->GetBrowserContext()); const ExtensionService* extension_service = extensions::ExtensionSystem::Get( profile->GetOriginalProfile())->extension_service(); if (!extension_service) return; const extensions::ExtensionSet* extensions = extension_service->extensions(); base::ListValue results; for (extensions::ExtensionSet::const_iterator extension(extensions->begin()); extension != extensions->end(); ++extension) { if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty()) continue; base::DictionaryValue* extension_info = new base::DictionaryValue(); extension_info->Set( "startPage", new base::StringValue( extensions::ManifestURL::GetDevToolsPage(extension->get()).spec())); extension_info->Set("name", new base::StringValue((*extension)->name())); extension_info->Set( "exposeExperimentalAPIs", new base::FundamentalValue((*extension)->HasAPIPermission( extensions::APIPermission::kExperimental))); results.Append(extension_info); } CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL); } void DevToolsWindow::CallClientFunction(const std::string& function_name, const base::Value* arg1, const base::Value* arg2, const base::Value* arg3) { std::string params; if (arg1) { std::string json; base::JSONWriter::Write(arg1, &json); params.append(json); if (arg2) { base::JSONWriter::Write(arg2, &json); params.append(", " + json); if (arg3) { base::JSONWriter::Write(arg3, &json); params.append(", " + json); } } } base::string16 javascript = base::ASCIIToUTF16(function_name + "(" + params + ");"); web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame( base::string16(), javascript); } void DevToolsWindow::UpdateBrowserToolbar() { BrowserWindow* inspected_window = GetInspectedBrowserWindow(); if (inspected_window) inspected_window->UpdateToolbar(NULL); } bool DevToolsWindow::IsDocked() { return dock_side_ != DEVTOOLS_DOCK_SIDE_UNDOCKED; } void DevToolsWindow::Restore() { if (dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED) SetDockSide(SideToString(dock_side_before_minimized_)); } content::WebContents* DevToolsWindow::GetInspectedWebContents() { return inspected_contents_observer_ ? inspected_contents_observer_->web_contents() : NULL; } void DevToolsWindow::DocumentOnLoadCompletedInMainFrame() { is_loaded_ = true; UpdateTheme(); DoAction(); AddDevToolsExtensionsToClient(); }