diff options
29 files changed, 484 insertions, 114 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 615406a..3b211ec 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4765,6 +4765,9 @@ Make sure you do not expose any sensitive information. <message name="IDS_EXTENSIONS_ERROR_VIEW_SOURCE" desc="The link to view the source code for a file relating to an extension error."> View source </message> + <message name="IDS_EXTENSIONS_ERROR_INSPECT" desc="The link to open the inspector (developer tools) for the location of a particular error."> + Inspect + </message> <message name="IDS_EXTENSIONS_ERROR_CONTEXT" desc="The label for the context of an extension's error."> Context: </message> diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc index d426741..e25d5b7 100644 --- a/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc @@ -137,7 +137,7 @@ class DevToolsSanityTest : public InProcessBrowserTest { content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>(window_->web_contents())); DevToolsWindow::ToggleDevToolsWindow(inspected_rvh_, false, - DEVTOOLS_TOGGLE_ACTION_TOGGLE); + DevToolsToggleAction::Toggle()); close_observer.Wait(); } @@ -403,7 +403,7 @@ class WorkerDevToolsSanityTest : public InProcessBrowserTest { void OpenDevToolsWindowForSharedWorker(WorkerData* worker_data) { Profile* profile = browser()->profile(); window_ = DevToolsWindow::CreateDevToolsWindowForWorker(profile); - window_->Show(DEVTOOLS_TOGGLE_ACTION_SHOW); + window_->Show(DevToolsToggleAction::Show()); scoped_refptr<DevToolsAgentHost> agent_host( DevToolsAgentHost::GetForWorker( worker_data->worker_process_id, diff --git a/chrome/browser/devtools/devtools_toggle_action.cc b/chrome/browser/devtools/devtools_toggle_action.cc new file mode 100644 index 0000000..779d5bc --- /dev/null +++ b/chrome/browser/devtools/devtools_toggle_action.cc @@ -0,0 +1,64 @@ +// Copyright 2013 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_toggle_action.h" + +DevToolsToggleAction::RevealParams::RevealParams(const base::string16& url, + size_t line_number, + size_t column_number) + : url(url), line_number(line_number), column_number(column_number) { +} + +DevToolsToggleAction::RevealParams::~RevealParams() { +} + +DevToolsToggleAction::DevToolsToggleAction(Type type) : type_(type) { +} + +DevToolsToggleAction::DevToolsToggleAction(RevealParams* params) + : type_(kReveal), params_(params) { +} + +DevToolsToggleAction::DevToolsToggleAction(const DevToolsToggleAction& rhs) + : type_(rhs.type_), + params_(rhs.params_.get() ? new RevealParams(*rhs.params_) : NULL) { +} + +void DevToolsToggleAction::operator=(const DevToolsToggleAction& rhs) { + type_ = rhs.type_; + if (rhs.params_.get()) + params_.reset(new RevealParams(*rhs.params_)); +} + +DevToolsToggleAction::~DevToolsToggleAction() { +} + +// static +DevToolsToggleAction DevToolsToggleAction::Show() { + return DevToolsToggleAction(kShow); +} + +// static +DevToolsToggleAction DevToolsToggleAction::ShowConsole() { + return DevToolsToggleAction(kShowConsole); +} + +// static +DevToolsToggleAction DevToolsToggleAction::Inspect() { + return DevToolsToggleAction(kInspect); +} + +// static +DevToolsToggleAction DevToolsToggleAction::Toggle() { + return DevToolsToggleAction(kToggle); +} + +// static +DevToolsToggleAction DevToolsToggleAction::Reveal( + const base::string16& url, + size_t line_number, + size_t column_number) { + return DevToolsToggleAction( + new RevealParams(url, line_number, column_number)); +} diff --git a/chrome/browser/devtools/devtools_toggle_action.h b/chrome/browser/devtools/devtools_toggle_action.h index ff5c94c..0fd5d36 100644 --- a/chrome/browser/devtools/devtools_toggle_action.h +++ b/chrome/browser/devtools/devtools_toggle_action.h @@ -5,11 +5,55 @@ #ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TOGGLE_ACTION_H_ #define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TOGGLE_ACTION_H_ -enum DevToolsToggleAction { - DEVTOOLS_TOGGLE_ACTION_SHOW, - DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE, - DEVTOOLS_TOGGLE_ACTION_INSPECT, - DEVTOOLS_TOGGLE_ACTION_TOGGLE +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" + +struct DevToolsToggleAction { + public: + enum Type { + kShow, + kShowConsole, + kInspect, + kToggle, + kReveal + }; + + struct RevealParams { + RevealParams(const base::string16& url, + size_t line_number, + size_t column_number); + ~RevealParams(); + + base::string16 url; + size_t line_number; + size_t column_number; + }; + + void operator=(const DevToolsToggleAction& rhs); + DevToolsToggleAction(const DevToolsToggleAction& rhs); + ~DevToolsToggleAction(); + + static DevToolsToggleAction Show(); + static DevToolsToggleAction ShowConsole(); + static DevToolsToggleAction Inspect(); + static DevToolsToggleAction Toggle(); + static DevToolsToggleAction Reveal(const base::string16& url, + size_t line_number, + size_t column_number); + + Type type() const { return type_; } + const RevealParams* params() const { return params_.get(); } + + private: + explicit DevToolsToggleAction(Type type); + explicit DevToolsToggleAction(RevealParams* reveal_params); + + // The type of action. + Type type_; + + // Additional parameters for the Reveal action; NULL if of any other type. + scoped_ptr<RevealParams> params_; }; #endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TOGGLE_ACTION_H_ diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc index e5af37e..0b757d9 100644 --- a/chrome/browser/devtools/devtools_window.cc +++ b/chrome/browser/devtools/devtools_window.cc @@ -11,6 +11,7 @@ #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" @@ -64,6 +65,8 @@ #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" +using base::DictionaryValue; +using content::BrowserThread; using content::DevToolsAgentHost; @@ -289,7 +292,7 @@ void DevToolsWindow::RegisterProfilePrefs( prefs::kDevToolsFileSystemPaths, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterStringPref( - prefs::kDevToolsAdbKey, std::string(), + prefs::kDevToolsAdbKey, EmptyString(), user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); registry->RegisterDictionaryPref( @@ -316,13 +319,20 @@ void DevToolsWindow::RegisterProfilePrefs( // static DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab( content::WebContents* inspected_web_contents) { - if (!inspected_web_contents || - !DevToolsAgentHost::HasFor(inspected_web_contents->GetRenderViewHost())) + 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_web_contents->GetRenderViewHost())); - DevToolsWindow* window = FindDevToolsWindow(agent.get()); - return (window && window->IsDocked()) ? window : NULL; + inspected_rvh)); + return FindDevToolsWindow(agent.get()); } // static @@ -341,7 +351,7 @@ DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker( content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( worker_agent, window->frontend_host_.get()); } - window->Show(DEVTOOLS_TOGGLE_ACTION_SHOW); + window->Show(DevToolsToggleAction::Show()); return window; } @@ -355,22 +365,23 @@ DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker( // static DevToolsWindow* DevToolsWindow::OpenDevToolsWindow( content::RenderViewHost* inspected_rvh) { - return ToggleDevToolsWindow(inspected_rvh, true, - DEVTOOLS_TOGGLE_ACTION_SHOW); + return ToggleDevToolsWindow( + inspected_rvh, true, DevToolsToggleAction::Show()); } // static DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow( Browser* browser, - DevToolsToggleAction action) { - if (action == DEVTOOLS_TOGGLE_ACTION_TOGGLE && browser->is_devtools()) { + 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 == DEVTOOLS_TOGGLE_ACTION_INSPECT, action); + action.type() == DevToolsToggleAction::kInspect, action); } // static @@ -385,14 +396,14 @@ void DevToolsWindow::OpenExternalFrontend( content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( agent_host, window->frontend_host_.get()); } - window->Show(DEVTOOLS_TOGGLE_ACTION_SHOW); + window->Show(DevToolsToggleAction::Show()); } // static DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow( content::RenderViewHost* inspected_rvh, bool force_open, - DevToolsToggleAction action) { + const DevToolsToggleAction& action) { scoped_refptr<DevToolsAgentHost> agent( DevToolsAgentHost::GetOrCreateFor(inspected_rvh)); content::DevToolsManager* manager = content::DevToolsManager::GetInstance(); @@ -514,7 +525,7 @@ void DevToolsWindow::SetHeight(int height) { profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height); } -void DevToolsWindow::Show(DevToolsToggleAction action) { +void DevToolsWindow::Show(const DevToolsToggleAction& action) { if (IsDocked()) { Browser* inspected_browser = NULL; int inspected_tab_index = -1; @@ -542,7 +553,7 @@ void DevToolsWindow::Show(DevToolsToggleAction action) { // 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 != DEVTOOLS_TOGGLE_ACTION_INSPECT); + !browser_ || (action.type() != DevToolsToggleAction::kInspect); if (!browser_) CreateDevToolsBrowser(); @@ -563,7 +574,7 @@ DevToolsWindow::DevToolsWindow(Profile* profile, browser_(NULL), dock_side_(dock_side), is_loaded_(false), - action_on_load_(DEVTOOLS_TOGGLE_ACTION_SHOW), + action_on_load_(DevToolsToggleAction::Show()), width_(-1), height_(-1), dock_side_before_minimized_(dock_side), @@ -573,7 +584,7 @@ DevToolsWindow::DevToolsWindow(Profile* profile, frontend_contents_observer_.reset(new FrontendWebContentsObserver(this)); web_contents_->GetController().LoadURL(url, content::Referrer(), - content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); + content::PAGE_TRANSITION_AUTO_TOPLEVEL, EmptyString()); frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost( web_contents_, this)); @@ -977,7 +988,7 @@ void DevToolsWindow::SetDockSide(const std::string& side) { profile_->GetPrefs()->SetString(prefs::kDevToolsDockSide, pref_value); } - Show(DEVTOOLS_TOGGLE_ACTION_SHOW); + Show(DevToolsToggleAction::Show()); } void DevToolsWindow::OpenInNewTab(const std::string& url) { @@ -1049,7 +1060,7 @@ void DevToolsWindow::RemoveFileSystem(const std::string& file_system_path) { void DevToolsWindow::IndexPath(int request_id, const std::string& file_system_path) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + 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); @@ -1074,7 +1085,7 @@ void DevToolsWindow::IndexPath(int request_id, } void DevToolsWindow::StopIndexing(int request_id) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); IndexingJobsMap::iterator it = indexing_jobs_.find(request_id); if (it == indexing_jobs_.end()) return; @@ -1085,7 +1096,7 @@ void DevToolsWindow::StopIndexing(int request_id) { void DevToolsWindow::SearchInPath(int request_id, const std::string& file_system_path, const std::string& query) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + 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>()); @@ -1121,7 +1132,7 @@ void DevToolsWindow::FileSystemsLoaded( void DevToolsWindow::FileSystemAdded( const DevToolsFileHelper::FileSystem& file_system) { - StringValue error_string_value((std::string())); + StringValue error_string_value(EmptyString()); DictionaryValue* file_system_value = NULL; if (!file_system.file_system_path.empty()) file_system_value = CreateFileSystemValue(file_system); @@ -1135,7 +1146,7 @@ void DevToolsWindow::IndexingTotalWorkCalculated( int request_id, const std::string& file_system_path, int total_work) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FundamentalValue request_id_value(request_id); StringValue file_system_path_value(file_system_path); base::FundamentalValue total_work_value(total_work); @@ -1147,7 +1158,7 @@ void DevToolsWindow::IndexingTotalWorkCalculated( void DevToolsWindow::IndexingWorked(int request_id, const std::string& file_system_path, int worked) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FundamentalValue request_id_value(request_id); StringValue file_system_path_value(file_system_path); base::FundamentalValue worked_value(worked); @@ -1158,7 +1169,7 @@ void DevToolsWindow::IndexingWorked(int request_id, void DevToolsWindow::IndexingDone(int request_id, const std::string& file_system_path) { indexing_jobs_.erase(request_id); - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::FundamentalValue request_id_value(request_id); StringValue file_system_path_value(file_system_path); CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value, @@ -1169,7 +1180,7 @@ void DevToolsWindow::SearchCompleted( int request_id, const std::string& file_system_path, const std::vector<std::string>& file_paths) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ListValue file_paths_value; for (std::vector<std::string>::const_iterator it(file_paths.begin()); it != file_paths.end(); ++it) { @@ -1257,7 +1268,7 @@ void DevToolsWindow::UpdateFrontendDockSide() { NULL); } -void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) { +void DevToolsWindow::ScheduleAction(const DevToolsToggleAction& action) { action_on_load_ = action; if (is_loaded_) DoAction(); @@ -1265,26 +1276,40 @@ void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) { void DevToolsWindow::DoAction() { UpdateFrontendDockSide(); - switch (action_on_load_) { - case DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE: + switch (action_on_load_.type()) { + case DevToolsToggleAction::kShowConsole: CallClientFunction("InspectorFrontendAPI.showConsole", NULL, NULL, NULL); break; - case DEVTOOLS_TOGGLE_ACTION_INSPECT: + case DevToolsToggleAction::kInspect: CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL, NULL, NULL); break; - case DEVTOOLS_TOGGLE_ACTION_SHOW: - case DEVTOOLS_TOGGLE_ACTION_TOGGLE: + 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_ = DEVTOOLS_TOGGLE_ACTION_SHOW; + action_on_load_ = DevToolsToggleAction::Show(); } void DevToolsWindow::UpdateTheme() { @@ -1297,7 +1322,7 @@ void DevToolsWindow::UpdateTheme() { SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) + "\")"); web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame( - string16(), ASCIIToUTF16(command)); + EmptyString16(), ASCIIToUTF16(command)); } void DevToolsWindow::AddDevToolsExtensionsToClient() { @@ -1359,7 +1384,7 @@ void DevToolsWindow::CallClientFunction(const std::string& function_name, } string16 javascript = ASCIIToUTF16(function_name + "(" + params + ");"); web_contents_->GetRenderViewHost()-> - ExecuteJavascriptInWebFrame(string16(), javascript); + ExecuteJavascriptInWebFrame(EmptyString16(), javascript); } void DevToolsWindow::UpdateBrowserToolbar() { diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h index fcdaadb..0f1bdd6 100644 --- a/chrome/browser/devtools/devtools_window.h +++ b/chrome/browser/devtools/devtools_window.h @@ -67,6 +67,10 @@ class DevToolsWindow : private content::NotificationObserver, static std::string GetDevToolsWindowPlacementPrefKey(); static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + // Return the DevToolsWindow for the given RenderViewHost if one exists, + // otherwise NULL. + static DevToolsWindow* GetInstanceForInspectedRenderViewHost( + content::RenderViewHost* inspected_rvh); static DevToolsWindow* GetDockedInstanceForInspectedTab( content::WebContents* inspected_tab); static bool IsDevToolsWindow(content::RenderViewHost* window_rvh); @@ -78,7 +82,7 @@ class DevToolsWindow : private content::NotificationObserver, content::RenderViewHost* inspected_rvh); static DevToolsWindow* ToggleDevToolsWindow( Browser* browser, - DevToolsToggleAction action); + const DevToolsToggleAction& action); static void OpenExternalFrontend(Profile* profile, const std::string& frontend_uri, content::DevToolsAgentHost* agent_host); @@ -87,7 +91,8 @@ class DevToolsWindow : private content::NotificationObserver, static DevToolsWindow* ToggleDevToolsWindow( content::RenderViewHost* inspected_rvh, bool force_open, - DevToolsToggleAction action); + const DevToolsToggleAction& action); + static void InspectElement( content::RenderViewHost* inspected_rvh, int x, int y); @@ -123,7 +128,7 @@ class DevToolsWindow : private content::NotificationObserver, // Stores preferred devtools window height for this instance. void SetHeight(int height); - void Show(DevToolsToggleAction action); + void Show(const DevToolsToggleAction& action); private: friend class DevToolsControllerTest; @@ -236,7 +241,7 @@ class DevToolsWindow : private content::NotificationObserver, BrowserWindow* GetInspectedBrowserWindow(); bool IsInspectedBrowserPopup(); void UpdateFrontendDockSide(); - void ScheduleAction(DevToolsToggleAction action); + void ScheduleAction(const DevToolsToggleAction& action); void DoAction(); void UpdateTheme(); void AddDevToolsExtensionsToClient(); diff --git a/chrome/browser/extensions/api/app_window/app_window_api.cc b/chrome/browser/extensions/api/app_window/app_window_api.cc index b31b776..0eaaaf5 100644 --- a/chrome/browser/extensions/api/app_window/app_window_api.cc +++ b/chrome/browser/extensions/api/app_window/app_window_api.cc @@ -66,7 +66,7 @@ class DevToolsRestorer : public content::NotificationObserver { DevToolsWindow::ToggleDevToolsWindow( created_view, true /* force_open */, - DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); + DevToolsToggleAction::ShowConsole()); registrar_.Add( this, diff --git a/chrome/browser/extensions/error_console/error_console_unittest.cc b/chrome/browser/extensions/error_console/error_console_unittest.cc index e36c93a..fb78528 100644 --- a/chrome/browser/extensions/error_console/error_console_unittest.cc +++ b/chrome/browser/extensions/error_console/error_console_unittest.cc @@ -57,7 +57,8 @@ scoped_ptr<ExtensionError> CreateNewRuntimeError( message, GetDefaultStackTrace(), GURL::EmptyGURL(), // no context url - logging::LOG_INFO)); + logging::LOG_INFO, + 0, 0 /* Render [View|Process] ID */ )); } } // namespace diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index e8da09d..19f075d 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -606,7 +606,9 @@ void ExtensionHost::OnDetailedConsoleMessageAdded( message, stack_trace, context_url, - static_cast<logging::LogSeverity>(severity_level)))); + static_cast<logging::LogSeverity>(severity_level), + render_view_host_->GetRoutingID(), + render_view_host_->GetProcess()->GetID()))); } } diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc index 63ce4be..b8ede76 100644 --- a/chrome/browser/extensions/tab_helper.cc +++ b/chrome/browser/extensions/tab_helper.cc @@ -363,6 +363,7 @@ void TabHelper::OnDetailedConsoleMessageAdded( const StackTrace& stack_trace, int32 severity_level) { if (IsSourceFromAnExtension(source)) { + content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); ErrorConsole::Get(profile_)->ReportError( scoped_ptr<ExtensionError>(new RuntimeError( extension_app_ ? extension_app_->id() : EmptyString(), @@ -372,7 +373,9 @@ void TabHelper::OnDetailedConsoleMessageAdded( stack_trace, web_contents() ? web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(), - static_cast<logging::LogSeverity>(severity_level)))); + static_cast<logging::LogSeverity>(severity_level), + rvh->GetRoutingID(), + rvh->GetProcess()->GetID()))); } } diff --git a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc index f574710..a17ec42 100644 --- a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc +++ b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc @@ -315,7 +315,7 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, EXPECT_EQ(host_count, RenderProcessHostCount()); // DevTools start in docked mode (no new tab), in a separate process. - chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT); + chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); host_count++; EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); EXPECT_EQ(host_count, RenderProcessHostCount()); @@ -325,7 +325,7 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, // DevTools start in a separate process. DevToolsWindow::ToggleDevToolsWindow( - devtools, true, DEVTOOLS_TOGGLE_ACTION_INSPECT); + devtools, true, DevToolsToggleAction::Inspect()); host_count++; EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); EXPECT_EQ(host_count, RenderProcessHostCount()); @@ -335,7 +335,7 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<WebContents>(WebContents::FromRenderViewHost(devtools))); - chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_TOGGLE); + chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle()); close_observer.Wait(); } @@ -363,7 +363,7 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, EXPECT_EQ(host_count, RenderProcessHostCount()); // DevTools start in docked mode (no new tab), in a separate process. - chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT); + chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); host_count++; EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); EXPECT_EQ(host_count, RenderProcessHostCount()); @@ -373,7 +373,7 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, // DevTools start in a separate process. DevToolsWindow::ToggleDevToolsWindow( - devtools, true, DEVTOOLS_TOGGLE_ACTION_INSPECT); + devtools, true, DevToolsToggleAction::Inspect()); host_count++; EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); EXPECT_EQ(host_count, RenderProcessHostCount()); @@ -383,7 +383,7 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>( WebContents::FromRenderViewHost(devtools))); - chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_TOGGLE); + chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle()); close_observer.Wait(); } diff --git a/chrome/browser/resources/extensions/extension_error.css b/chrome/browser/resources/extensions/extension_error.css index 6cdd717..4bb50c7 100644 --- a/chrome/browser/resources/extensions/extension_error.css +++ b/chrome/browser/resources/extensions/extension_error.css @@ -71,10 +71,31 @@ } .extension-error-view-source { - -webkit-margin-start: 0 !important; + display: inline-block; +} + +a.extension-error-view-source { + -webkit-margin-start: 0; /* Needed to override rule 'extension-list-item' */ +} + +.extension-error-links { + display: inline-block; float: right; } +.extension-error-inspect { + content: url('extension_error_inspect.png'); + width: 10px; +} + +.extension-error-links .extension-error-inspect { + -webkit-margin-end: 10px; +} + +.extension-error-links > .extension-error-view-source:first-child { + -webkit-margin-start: 20px; +} + .extension-error-details { -webkit-margin-start: 30px; } @@ -83,6 +104,10 @@ -webkit-padding-start: 20px; } -.extension-error-stack-trace-list .extension-error-view-source { +.extension-error-context-wrapper .extension-error-inspect { + -webkit-margin-start: 10px; +} + +.extension-error-stack-trace-list .extension-error-links { float: none; } diff --git a/chrome/browser/resources/extensions/extension_error.js b/chrome/browser/resources/extensions/extension_error.js index 774c9bb..0b2efef4 100644 --- a/chrome/browser/resources/extensions/extension_error.js +++ b/chrome/browser/resources/extensions/extension_error.js @@ -63,11 +63,11 @@ cr.define('extensions', function() { // Add an additional class for the severity level. if (this.error_.level == 0) - metadata.className += ' extension-error-severity-info'; + metadata.classList.add('extension-error-severity-info'); else if (this.error_.level == 1) - metadata.className += ' extension-error-severity-warning'; + metadata.classList.add('extension-error-severity-warning'); else - metadata.className += ' extension-error-severity-fatal'; + metadata.classList.add('extension-error-severity-fatal'); var iconNode = document.createElement('img'); iconNode.className = 'extension-error-icon'; @@ -81,7 +81,7 @@ cr.define('extensions', function() { metadata.querySelector('.extension-error-message').textContent = this.error_.message; - metadata.appendChild(this.getViewSourceOrPlain_( + metadata.appendChild(this.createViewSourceAndInspect_( getRelativeUrl(this.error_.source, this.extensionUrl_), this.error_.source)); @@ -92,29 +92,41 @@ cr.define('extensions', function() { var detailsNode = this.querySelector('.extension-error-details'); if (detailsNode && this.error_.contextUrl) - detailsNode.appendChild(this.getContextNode_()); - if (detailsNode && this.error_.stackTrace) - detailsNode.appendChild(this.getStackNode_()); + detailsNode.appendChild(this.createContextNode_()); + if (detailsNode && this.error_.stackTrace) { + var stackNode = this.createStackNode_(); + if (stackNode) + detailsNode.appendChild(this.createStackNode_()); + } }, /** * Return a div with text |description|. If it's possible to view the source - * for |url|, linkify the div to do so. + * for |url|, linkify the div to do so. Attach an inspect button if it's + * possible to open the inspector for |url|. * @param {string} description a human-friendly description the location * (e.g., filename, line). * @param {string} url The url of the resource to view. * @param {?number} line An optional line number of the resource. + * @param {?number} column An optional column number of the resource. * @return {HTMLElement} The created node, either a link or plaintext. * @private */ - getViewSourceOrPlain_: function(description, url, line) { + createViewSourceAndInspect_: function(description, url, line, column) { + var errorLinks = document.createElement('div'); + errorLinks.className = 'extension-error-links'; + + if (this.error_.canInspect) + errorLinks.appendChild(this.createInspectLink_(url, line, column)); + if (this.canViewSource_(url)) - var node = this.getViewSourceLink_(url, line); + var viewSource = this.createViewSourceLink_(url, line); else - var node = document.createElement('div'); - node.className = 'extension-error-view-source'; - node.textContent = description; - return node; + var viewSource = document.createElement('div'); + viewSource.className = 'extension-error-view-source'; + viewSource.textContent = description; + errorLinks.appendChild(viewSource); + return errorLinks; }, /** @@ -128,6 +140,19 @@ cr.define('extensions', function() { }, /** + * Determine whether or not we should display the url to the user. We don't + * want to include any of our own code in stack traces. + * @param {string} url The url in question. + * @return {boolean} True if the url should be displayed, and false + * otherwise (i.e., if it is an internal script). + */ + shouldDisplayForUrl_: function(url) { + var extensionsNamespace = 'extensions::'; + // All our internal scripts are in the 'extensions::' namespace. + return url.substr(0, extensionsNamespace.length) != extensionsNamespace; + }, + + /** * Create a clickable node to view the source for the given url. * @param {string} url The url to the resource to view. * @param {?number} line An optional line number of the resource (for @@ -135,8 +160,9 @@ cr.define('extensions', function() { * @return {HTMLElement} The clickable node to view the source. * @private */ - getViewSourceLink_: function(url, line) { - var node = document.createElement('a'); + createViewSourceLink_: function(url, line) { + var viewSource = document.createElement('a'); + viewSource.href = 'javascript:void(0)'; var relativeUrl = getRelativeUrl(url, this.extensionUrl_); var requestFileSourceArgs = { 'extensionId': this.error_.extensionId, 'message': this.error_.message, @@ -147,19 +173,57 @@ cr.define('extensions', function() { } else { // Prefer |line| if available, or default to the line of the last stack // frame. - if (line) { - requestFileSourceArgs.lineNumber = line; - } else if (this.error_.stackTrace) { - requestFileSourceArgs.lineNumber = - this.error_.stackTrace[0].lineNumber; - } + requestFileSourceArgs.lineNumber = + line ? line : this.getLastPosition_('lineNumber'); } - node.addEventListener('click', function(e) { + viewSource.addEventListener('click', function(e) { chrome.send('extensionErrorRequestFileSource', [requestFileSourceArgs]); }); - node.title = loadTimeData.getString('extensionErrorViewSource'); - return node; + viewSource.title = loadTimeData.getString('extensionErrorViewSource'); + return viewSource; + }, + + /** + * Check the most recent stack frame to get the last position in the code. + * @param {string} type The position type, i.e. '[line|column]Number'. + * @return {?number} The last position of the given |type|, or undefined if + * there is no stack trace to check. + * @private + */ + getLastPosition_: function(type) { + var stackTrace = this.error_.stackTrace; + return stackTrace && stackTrace[0] ? stackTrace[0][type] : undefined; + }, + + /** + * Create an "Inspect" link, in the form of an icon. + * @param {?string} url The url of the resource to inspect; if absent, the + * render view (and no particular resource) is inspected. + * @param {?number} line An optional line number of the resource. + * @param {?number} column An optional column number of the resource. + * @return {HTMLImageElement} The created "Inspect" link for the resource. + * @private + */ + createInspectLink_: function(url, line, column) { + var linkWrapper = document.createElement('a'); + linkWrapper.href = 'javascript:void(0)'; + var inspectIcon = document.createElement('img'); + inspectIcon.className = 'extension-error-inspect'; + inspectIcon.title = loadTimeData.getString('extensionErrorInspect'); + + inspectIcon.addEventListener('click', function(e) { + chrome.send('extensionErrorOpenDevTools', + [{'renderProcessId': this.error_.renderProcessId, + 'renderViewId': this.error_.renderViewId, + 'url': url, + 'lineNumber': line ? line : + this.getLastPosition_('lineNumber'), + 'columnNumber': column ? column : + this.getLastPosition_('columnNumber')}]); + }.bind(this)); + linkWrapper.appendChild(inspectIcon); + return linkWrapper; }, /** @@ -170,7 +234,7 @@ cr.define('extensions', function() { * label and a link to the context. * @private */ - getContextNode_: function() { + createContextNode_: function() { var node = cloneTemplate('extension-error-context-wrapper'); var linkNode = node.querySelector('a'); if (isExtensionUrl(this.error_.contextUrl, this.extensionUrl_)) { @@ -179,6 +243,11 @@ cr.define('extensions', function() { } else { linkNode.textContent = this.error_.contextUrl; } + + // Prepend a link to inspect the context page, if possible. + if (this.error_.canInspect) + node.insertBefore(this.createInspectLink_(), linkNode); + linkNode.href = this.error_.contextUrl; linkNode.target = '_blank'; return node; @@ -188,15 +257,17 @@ cr.define('extensions', function() { * Get a node for the stack trace for this error. Each stack frame will * include a resource url, line number, and function name (possibly * anonymous). If possible, these frames will also be linked for viewing the - * source. + * source and inspection. * @return {HTMLDetailsElement} The stack trace node for this error, with * all stack frames nested in a details-summary object. * @private */ - getStackNode_: function() { + createStackNode_: function() { var node = cloneTemplate('extension-error-stack-trace'); var listNode = node.querySelector('.extension-error-stack-trace-list'); this.error_.stackTrace.forEach(function(frame) { + if (!this.shouldDisplayForUrl_(frame.url)) + return; var frameNode = document.createElement('div'); var description = getRelativeUrl(frame.url, this.extensionUrl_) + ':' + frame.lineNumber; @@ -206,12 +277,15 @@ cr.define('extensions', function() { frame.functionName; description += ' (' + functionName + ')'; } - frameNode.appendChild(this.getViewSourceOrPlain_( - description, frame.url, frame.lineNumber)); + frameNode.appendChild(this.createViewSourceAndInspect_( + description, frame.url, frame.lineNumber, frame.columnNumber)); listNode.appendChild( document.createElement('li')).appendChild(frameNode); }, this); + if (listNode.childElementCount == 0) + return undefined; + return node; }, }; diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html index a8927c6..9863915 100644 --- a/chrome/browser/resources/extensions/extensions.html +++ b/chrome/browser/resources/extensions/extensions.html @@ -3,10 +3,10 @@ <head> <meta charset="utf-8"> +<link rel="stylesheet" href="extensions.css"> <link rel="stylesheet" href="extension_commands_overlay.css"> <link rel="stylesheet" href="extension_error.css"> <link rel="stylesheet" href="extension_error_overlay.css"> -<link rel="stylesheet" href="extensions.css"> <link rel="stylesheet" href="pack_extension_overlay.css"> <link rel="stylesheet" href="chrome://resources/css/alert_overlay.css"> <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc index 0093f38..f77bea4 100644 --- a/chrome/browser/task_manager/task_manager_browsertest.cc +++ b/chrome/browser/task_manager/task_manager_browsertest.cc @@ -516,7 +516,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeInTabDevToolsWindow) { DevToolsWindow* dev_tools = DevToolsWindow::ToggleDevToolsWindow( model()->GetResourceWebContents(1)->GetRenderViewHost(), true, - DEVTOOLS_TOGGLE_ACTION_INSPECT); + DevToolsToggleAction::Inspect()); // Dock side bottom should be the default. ASSERT_EQ(DEVTOOLS_DOCK_SIDE_BOTTOM, dev_tools->dock_side()); TaskManagerBrowserTestUtil::WaitForWebResourceChange(2); @@ -530,7 +530,7 @@ IN_PROC_BROWSER_TEST_F(TaskManagerNoShowBrowserTest, DevToolsWindow* dev_tools = DevToolsWindow::ToggleDevToolsWindow( browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(), true, - DEVTOOLS_TOGGLE_ACTION_INSPECT); + DevToolsToggleAction::Inspect()); // Dock side bottom should be the default. ASSERT_EQ(DEVTOOLS_DOCK_SIDE_BOTTOM, dev_tools->dock_side()); // Make sure that the devtools window is loaded before starting the task diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index 2860eb8..50a870d 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc @@ -649,19 +649,19 @@ void BrowserCommandController::ExecuteCommandWithDisposition( CreateApplicationShortcuts(browser_); break; case IDC_DEV_TOOLS: - ToggleDevToolsWindow(browser_, DEVTOOLS_TOGGLE_ACTION_SHOW); + ToggleDevToolsWindow(browser_, DevToolsToggleAction::Show()); break; case IDC_DEV_TOOLS_CONSOLE: - ToggleDevToolsWindow(browser_, DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); + ToggleDevToolsWindow(browser_, DevToolsToggleAction::ShowConsole()); break; case IDC_DEV_TOOLS_DEVICES: InspectUI::InspectDevices(browser_); break; case IDC_DEV_TOOLS_INSPECT: - ToggleDevToolsWindow(browser_, DEVTOOLS_TOGGLE_ACTION_INSPECT); + ToggleDevToolsWindow(browser_, DevToolsToggleAction::Inspect()); break; case IDC_DEV_TOOLS_TOGGLE: - ToggleDevToolsWindow(browser_, DEVTOOLS_TOGGLE_ACTION_TOGGLE); + ToggleDevToolsWindow(browser_, DevToolsToggleAction::Toggle()); break; case IDC_TASK_MANAGER: OpenTaskManager(browser_); diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc index 16ef803..9d6151a 100644 --- a/chrome/browser/ui/browser_commands.cc +++ b/chrome/browser/ui/browser_commands.cc @@ -918,7 +918,7 @@ void FocusPreviousPane(Browser* browser) { } void ToggleDevToolsWindow(Browser* browser, DevToolsToggleAction action) { - if (action == DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE) + if (action.type() == DevToolsToggleAction::kShowConsole) content::RecordAction(UserMetricsAction("DevTools_ToggleConsole")); else content::RecordAction(UserMetricsAction("DevTools_ToggleWindow")); diff --git a/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm b/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm index fdc12b5..8e4aba2 100644 --- a/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm +++ b/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm @@ -23,7 +23,7 @@ class DevToolsControllerTest : public InProcessBrowserTest { virtual void SetUpOnMainThread() OVERRIDE { DevToolsWindow::ToggleDevToolsWindow(browser(), - DEVTOOLS_TOGGLE_ACTION_SHOW); + DevToolsToggleAction::Show()); } private: diff --git a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm index 2b5bc5a..3fdcc0d 100644 --- a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm +++ b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm @@ -49,7 +49,7 @@ class ViewIDTest : public InProcessBrowserTest { // Make sure docked devtools is created to test VIEW_ID_DEV_TOOLS_DOCKED browser()->profile()->GetPrefs()->SetString(prefs::kDevToolsDockSide, "dock_bottom"); - chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT); + chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); // Make sure download shelf is created to test VIEW_ID_DOWNLOAD_SHELF browser()->window()->GetDownloadShelf()->Show(); diff --git a/chrome/browser/ui/panels/panel.cc b/chrome/browser/ui/panels/panel.cc index bef02b1..c83607d 100644 --- a/chrome/browser/ui/panels/panel.cc +++ b/chrome/browser/ui/panels/panel.cc @@ -415,14 +415,14 @@ void Panel::ExecuteCommandWithDisposition(int id, DevToolsWindow::ToggleDevToolsWindow( GetWebContents()->GetRenderViewHost(), true, - DEVTOOLS_TOGGLE_ACTION_SHOW); + DevToolsToggleAction::Show()); break; case IDC_DEV_TOOLS_CONSOLE: content::RecordAction(UserMetricsAction("DevTools_ToggleConsole")); DevToolsWindow::ToggleDevToolsWindow( GetWebContents()->GetRenderViewHost(), true, - DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); + DevToolsToggleAction::ShowConsole()); break; default: diff --git a/chrome/browser/ui/views/extensions/extension_popup.cc b/chrome/browser/ui/views/extensions/extension_popup.cc index 84051ee..7b8d4ef 100644 --- a/chrome/browser/ui/views/extensions/extension_popup.cc +++ b/chrome/browser/ui/views/extensions/extension_popup.cc @@ -201,6 +201,6 @@ void ExtensionPopup::ShowBubble() { if (inspect_with_devtools_) { DevToolsWindow::ToggleDevToolsWindow(host()->render_view_host(), true, - DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); + DevToolsToggleAction::ShowConsole()); } } diff --git a/chrome/browser/ui/views/external_tab_container_win.cc b/chrome/browser/ui/views/external_tab_container_win.cc index 54fcf1d..e906deb 100644 --- a/chrome/browser/ui/views/external_tab_container_win.cc +++ b/chrome/browser/ui/views/external_tab_container_win.cc @@ -1278,22 +1278,22 @@ bool ExternalTabContainerWin::AcceleratorPressed( case IDC_DEV_TOOLS: DevToolsWindow::ToggleDevToolsWindow(web_contents_->GetRenderViewHost(), false, - DEVTOOLS_TOGGLE_ACTION_SHOW); + DevToolsToggleAction::Show()); break; case IDC_DEV_TOOLS_CONSOLE: DevToolsWindow::ToggleDevToolsWindow(web_contents_->GetRenderViewHost(), false, - DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); + DevToolsToggleAction::ShowConsole()); break; case IDC_DEV_TOOLS_INSPECT: DevToolsWindow::ToggleDevToolsWindow(web_contents_->GetRenderViewHost(), false, - DEVTOOLS_TOGGLE_ACTION_INSPECT); + DevToolsToggleAction::Inspect()); break; case IDC_DEV_TOOLS_TOGGLE: DevToolsWindow::ToggleDevToolsWindow(web_contents_->GetRenderViewHost(), false, - DEVTOOLS_TOGGLE_ACTION_TOGGLE); + DevToolsToggleAction::Toggle()); break; default: NOTREACHED() << "Unsupported accelerator: " << command_id; diff --git a/chrome/browser/ui/webui/extensions/extension_error_handler.cc b/chrome/browser/ui/webui/extensions/extension_error_handler.cc index c537365..e99eec1 100644 --- a/chrome/browser/ui/webui/extensions/extension_error_handler.cc +++ b/chrome/browser/ui/webui/extensions/extension_error_handler.cc @@ -11,11 +11,17 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/extensions/extension.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" #include "extensions/browser/extension_error.h" @@ -55,6 +61,9 @@ void ExtensionErrorHandler::GetLocalizedValues( "extensionErrorViewSource", l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_VIEW_SOURCE)); source->AddString( + "extensionErrorInspect", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_INSPECT)); + source->AddString( "extensionErrorContext", l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CONTEXT)); source->AddString( @@ -70,6 +79,10 @@ void ExtensionErrorHandler::RegisterMessages() { "extensionErrorRequestFileSource", base::Bind(&ExtensionErrorHandler::HandleRequestFileSource, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "extensionErrorOpenDevTools", + base::Bind(&ExtensionErrorHandler::HandleOpenDevTools, + base::Unretained(this))); } void ExtensionErrorHandler::HandleRequestFileSource( @@ -156,6 +169,67 @@ void ExtensionErrorHandler::HandleRequestFileSource( closure); } +void ExtensionErrorHandler::HandleOpenDevTools(const base::ListValue* args) { + CHECK(args->GetSize() == 1); + + const base::DictionaryValue* dict = NULL; + int render_process_id = 0; + int render_view_id = 0; + + // The render view and render process ids are required. + if (!args->GetDictionary(0, &dict) || + !dict->GetInteger(RuntimeError::kRenderProcessIdKey, + &render_process_id) || + !dict->GetInteger(RuntimeError::kRenderViewIdKey, &render_view_id)) { + NOTREACHED(); + return; + } + + content::RenderViewHost* rvh = + content::RenderViewHost::FromID(render_process_id, render_view_id); + + // It's possible that the render view was closed since we last updated the + // links. Handle this gracefully. + if (!rvh) + return; + + // Check if we already have an inspector for the given RenderViewHost. If not, + // create one. + DevToolsWindow* window = + DevToolsWindow::GetInstanceForInspectedRenderViewHost(rvh); + if (!window) + window = DevToolsWindow::OpenDevToolsWindow(rvh); + + // If we include a url, we should inspect it specifically (and not just the + // render view). + base::string16 url; + if (dict->GetString(RuntimeError::kUrlKey, &url)) { + // Line and column numbers are optional; default to the first line. + int line_number = 1; + int column_number = 1; + dict->GetInteger(RuntimeError::kLineNumberKey, &line_number); + dict->GetInteger(RuntimeError::kColumnNumberKey, &column_number); + + // Line/column numbers are reported in display-friendly 1-based numbers, + // but are inspected in zero-based numbers. + window->Show( + DevToolsToggleAction::Reveal(url, line_number - 1, column_number - 1)); + } + + // Once we open the inspector, we focus on the appropriate tab... + content::WebContents* web_contents = + content::WebContents::FromRenderViewHost(rvh); + Browser* browser = chrome::FindBrowserWithWebContents(web_contents); + // ... but background pages have no associated browser (and the inspector + // opens in its own window), so our work is done. + if (!browser) + return; + + TabStripModel* tab_strip = browser->tab_strip_model(); + tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(web_contents), + false); // Not through direct user gesture. +} + void ExtensionErrorHandler::GetManifestFileCallback( base::DictionaryValue* results, const std::string& key, diff --git a/chrome/browser/ui/webui/extensions/extension_error_handler.h b/chrome/browser/ui/webui/extensions/extension_error_handler.h index eb61728..b419135 100644 --- a/chrome/browser/ui/webui/extensions/extension_error_handler.h +++ b/chrome/browser/ui/webui/extensions/extension_error_handler.h @@ -43,6 +43,8 @@ class ExtensionErrorHandler : public content::WebUIMessageHandler { // Handle the "requestFileSource" call. void HandleRequestFileSource(const base::ListValue* args); + // Handle the "openDevTools" call. + void HandleOpenDevTools(const base::ListValue* args); // Populate the results for a manifest file's content in response to the // "requestFileSource" call. Highlight the part of the manifest which diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc index c4f1b7e..0c8bc85 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc @@ -148,6 +148,8 @@ ExtensionSettingsHandler::ExtensionSettingsHandler() management_policy_(NULL), ignore_notifications_(false), deleting_rvh_(NULL), + deleting_rwh_id_(-1), + deleting_rph_id_(-1), registered_for_notifications_(false), warning_service_observer_(this), error_console_observer_(this) { @@ -166,6 +168,8 @@ ExtensionSettingsHandler::ExtensionSettingsHandler(ExtensionService* service, management_policy_(policy), ignore_notifications_(false), deleting_rvh_(NULL), + deleting_rwh_id_(-1), + deleting_rph_id_(-1), registered_for_notifications_(false), warning_service_observer_(this), error_console_observer_(this) { @@ -299,10 +303,19 @@ base::DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue( scoped_ptr<ListValue> runtime_errors(new ListValue); for (ErrorConsole::ErrorList::const_iterator iter = errors.begin(); iter != errors.end(); ++iter) { - if ((*iter)->type() == ExtensionError::MANIFEST_ERROR) + if ((*iter)->type() == ExtensionError::MANIFEST_ERROR) { manifest_errors->Append((*iter)->ToValue().release()); - else - runtime_errors->Append((*iter)->ToValue().release()); + } else { // Handle runtime error. + const RuntimeError* error = static_cast<const RuntimeError*>(*iter); + scoped_ptr<DictionaryValue> value = error->ToValue(); + bool can_inspect = + !(deleting_rwh_id_ == error->render_view_id() && + deleting_rph_id_ == error->render_process_id()) && + RenderViewHost::FromID(error->render_process_id(), + error->render_view_id()) != NULL; + value->SetBoolean("canInspect", can_inspect); + runtime_errors->Append(value.release()); + } } if (!manifest_errors->empty()) extension_data->Set("manifestErrors", manifest_errors.release()); @@ -425,7 +438,7 @@ void ExtensionSettingsHandler::GetLocalizedValues( } void ExtensionSettingsHandler::RenderViewDeleted( - content::RenderViewHost* render_view_host) { + RenderViewHost* render_view_host) { deleting_rvh_ = render_view_host; Profile* source_profile = Profile::FromBrowserContext( render_view_host->GetSiteInstance()->GetBrowserContext()); @@ -542,6 +555,14 @@ void ExtensionSettingsHandler::Observe( return; MaybeUpdateAfterNotification(); break; + case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: { + content::RenderWidgetHost* rwh = + content::Source<content::RenderWidgetHost>(source).ptr(); + deleting_rwh_id_ = rwh->GetRoutingID(); + deleting_rph_id_ = rwh->GetProcess()->GetID(); + MaybeUpdateAfterNotification(); + break; + } case chrome::NOTIFICATION_EXTENSION_LOADED: case chrome::NOTIFICATION_EXTENSION_UNLOADED: case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: @@ -1033,6 +1054,10 @@ void ExtensionSettingsHandler::MaybeRegisterForNotifications() { chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Add(this, + content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, + content::NotificationService::AllBrowserContextsAndSources()); + content::WebContentsObserver::Observe(web_ui()->GetWebContents()); warning_service_observer_.Add( diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chrome/browser/ui/webui/extensions/extension_settings_handler.h index c2125f0..ff7ecac 100644 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.h +++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.h @@ -242,6 +242,9 @@ class ExtensionSettingsHandler // it is removed from the process). Keep a pointer to it so we can exclude // it from the active views. content::RenderViewHost* deleting_rvh_; + // Do the same for a deleting RenderWidgetHost ID and RenderProcessHost ID. + int deleting_rwh_id_; + int deleting_rph_id_; // We want to register for notifications only after we've responded at least // once to the page, otherwise we'd be calling JavaScript functions on objects diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index f7f7d59..2262f2b 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -189,6 +189,7 @@ 'browser/devtools/devtools_protocol.h', 'browser/devtools/devtools_target_impl.cc', 'browser/devtools/devtools_target_impl.h', + 'browser/devtools/devtools_toggle_action.cc', 'browser/devtools/devtools_toggle_action.h', 'browser/devtools/devtools_window.cc', 'browser/devtools/devtools_window.h', diff --git a/extensions/browser/extension_error.cc b/extensions/browser/extension_error.cc index 071dbc2..ee6b47e 100644 --- a/extensions/browser/extension_error.cc +++ b/extensions/browser/extension_error.cc @@ -130,6 +130,8 @@ const char RuntimeError::kFunctionNameKey[] = "functionName"; const char RuntimeError::kLineNumberKey[] = "lineNumber"; const char RuntimeError::kStackTraceKey[] = "stackTrace"; const char RuntimeError::kUrlKey[] = "url"; +const char RuntimeError::kRenderProcessIdKey[] = "renderProcessId"; +const char RuntimeError::kRenderViewIdKey[] = "renderViewId"; RuntimeError::RuntimeError(const std::string& extension_id, bool from_incognito, @@ -137,7 +139,9 @@ RuntimeError::RuntimeError(const std::string& extension_id, const string16& message, const StackTrace& stack_trace, const GURL& context_url, - logging::LogSeverity level) + logging::LogSeverity level, + int render_view_id, + int render_process_id) : ExtensionError(ExtensionError::RUNTIME_ERROR, !extension_id.empty() ? extension_id : GURL(source).host(), from_incognito, @@ -145,7 +149,9 @@ RuntimeError::RuntimeError(const std::string& extension_id, source, message), context_url_(context_url), - stack_trace_(stack_trace) { + stack_trace_(stack_trace), + render_view_id_(render_view_id), + render_process_id_(render_process_id) { CleanUpInit(); } @@ -155,8 +161,10 @@ RuntimeError::~RuntimeError() { scoped_ptr<DictionaryValue> RuntimeError::ToValue() const { scoped_ptr<DictionaryValue> value = ExtensionError::ToValue(); value->SetString(kContextUrlKey, context_url_.spec()); + value->SetInteger(kRenderViewIdKey, render_view_id_); + value->SetInteger(kRenderProcessIdKey, render_process_id_); - ListValue* trace_value = new ListValue; + base::ListValue* trace_value = new base::ListValue; for (StackTrace::const_iterator iter = stack_trace_.begin(); iter != stack_trace_.end(); ++iter) { DictionaryValue* frame_value = new DictionaryValue; diff --git a/extensions/browser/extension_error.h b/extensions/browser/extension_error.h index 60e3b90..e905549 100644 --- a/extensions/browser/extension_error.h +++ b/extensions/browser/extension_error.h @@ -126,7 +126,9 @@ class RuntimeError : public ExtensionError { const base::string16& message, const StackTrace& stack_trace, const GURL& context_url, - logging::LogSeverity level); + logging::LogSeverity level, + int render_view_id, + int render_process_id); virtual ~RuntimeError(); virtual scoped_ptr<base::DictionaryValue> ToValue() const OVERRIDE; @@ -135,6 +137,8 @@ class RuntimeError : public ExtensionError { const GURL& context_url() const { return context_url_; } const StackTrace& stack_trace() const { return stack_trace_; } + int render_view_id() const { return render_view_id_; } + int render_process_id() const { return render_process_id_; } // Keys used for retrieving JSON values. static const char kColumnNumberKey[]; @@ -143,6 +147,8 @@ class RuntimeError : public ExtensionError { static const char kLineNumberKey[]; static const char kStackTraceKey[]; static const char kUrlKey[]; + static const char kRenderProcessIdKey[]; + static const char kRenderViewIdKey[]; private: virtual bool IsEqualImpl(const ExtensionError* rhs) const OVERRIDE; @@ -155,6 +161,11 @@ class RuntimeError : public ExtensionError { GURL context_url_; StackTrace stack_trace_; + // Keep track of the render process which caused the error in order to + // inspect the view later, if possible. + int render_view_id_; + int render_process_id_; + DISALLOW_COPY_AND_ASSIGN(RuntimeError); }; |