Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: extensions/browser/extension_error.cc

Issue 23007021: Report Javascript Runtime Errors to the Error Console (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@dc_ec_feldman
Patch Set: Joi's Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/browser/extension_error.h ('k') | extensions/browser/extension_error_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/browser/extension_error.h" 5 #include "extensions/browser/extension_error.h"
6 6
7 #include "base/json/json_reader.h"
8 #include "base/strings/string_number_conversions.h" 7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h" 11 #include "base/values.h"
11 #include "extensions/common/constants.h" 12 #include "extensions/common/constants.h"
13 #include "third_party/re2/re2/re2.h"
12 #include "url/gurl.h" 14 #include "url/gurl.h"
13 15
14 using base::string16; 16 using base::string16;
15 17
16 namespace extensions { 18 namespace extensions {
17 19
18 namespace { 20 namespace {
19 21
20 const char kLineNumberKey[] = "lineNumber"; 22 const char kLineNumberKey[] = "lineNumber";
21 const char kColumnNumberKey[] = "columnNumber"; 23 const char kColumnNumberKey[] = "columnNumber";
22 const char kURLKey[] = "url"; 24 const char kURLKey[] = "url";
23 const char kFunctionNameKey[] = "functionName"; 25 const char kFunctionNameKey[] = "functionName";
24 const char kExecutionContextURLKey[] = "executionContextURL"; 26 const char kExecutionContextURLKey[] = "executionContextURL";
25 const char kStackTraceKey[] = "stackTrace"; 27 const char kStackTraceKey[] = "stackTrace";
28 const char kStackFrameDelimiter[] = "\n at ";
29 const char kAnonymousFunction[] = "(anonymous function)";
26 30
27 // Try to retrieve an extension ID from a |url|. On success, returns true and 31 // Try to retrieve an extension ID from a |url|. On success, returns true and
28 // populates |extension_id| with the ID. On failure, returns false and leaves 32 // populates |extension_id| with the ID. On failure, returns false and leaves
29 // extension_id untouched. 33 // extension_id untouched.
30 bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) { 34 bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) {
31 if (url.SchemeIs(kExtensionScheme)) { 35 if (url.SchemeIs(kExtensionScheme)) {
32 *extension_id = url.host(); 36 *extension_id = url.host();
33 return true; 37 return true;
34 } 38 }
35 return false; 39 return false;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 } 74 }
71 75
72 ManifestParsingError::~ManifestParsingError() { 76 ManifestParsingError::~ManifestParsingError() {
73 } 77 }
74 78
75 std::string ManifestParsingError::PrintForTest() const { 79 std::string ManifestParsingError::PrintForTest() const {
76 return ExtensionError::PrintForTest() + 80 return ExtensionError::PrintForTest() +
77 "\n Type: ManifestParsingError"; 81 "\n Type: ManifestParsingError";
78 } 82 }
79 83
80 JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1),
81 column_number(-1) {
82 }
83
84 JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line, 84 JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line,
85 size_t frame_column, 85 size_t frame_column,
86 const string16& frame_url, 86 const string16& frame_url,
87 const string16& frame_function) 87 const string16& frame_function)
88 : line_number(frame_line), 88 : line_number(frame_line),
89 column_number(frame_column), 89 column_number(frame_column),
90 url(frame_url), 90 url(frame_url),
91 function(frame_function) { 91 function(frame_function) {
92 } 92 }
93 93
94 JavascriptRuntimeError::StackFrame::~StackFrame() { 94 JavascriptRuntimeError::StackFrame::~StackFrame() {
95 } 95 }
96 96
97 // Create a stack frame from the passed text. The text must follow one of two
98 // formats:
99 // - "function_name (source:line_number:column_number)"
100 // - "source:line_number:column_number"
101 // (We have to recognize two formats because V8 will report stack traces in
102 // both ways. If we reconcile this, we can clean this up.)
103 scoped_ptr<JavascriptRuntimeError::StackFrame>
104 JavascriptRuntimeError::StackFrame::CreateFromText(const string16& utf16_text) {
105 // We need to use utf8 for re2 matching.
106 std::string text = base::UTF16ToUTF8(utf16_text);
107
108 size_t line = -1;
109 size_t column = -1;
110 std::string url;
111 std::string function;
112 if (!re2::RE2::FullMatch(text,
Cris Neckar 2013/08/27 19:10:47 I don't much like that we are exposing the entire
Devlin 2013/08/27 19:52:48 So, to confirm, you would prefer to do this work o
Devlin 2013/08/27 23:51:33 Done.
jam 2013/08/29 16:10:32 I'm not familiar with re2, but I hear that Justin
113 "(.+) \\(([^\\(\\)]+):(\\d+):(\\d+)\\)",
114 &function, &url, &line, &column) &&
115 !re2::RE2::FullMatch(text,
116 "([^\\(\\)]+):(\\d+):(\\d+)",
117 &url, &line, &column)) {
118 return scoped_ptr<StackFrame>();
119 }
120
121 if (function.empty())
122 function = kAnonymousFunction;
123
124 return scoped_ptr<StackFrame>(
125 new StackFrame(
126 line, column, base::UTF8ToUTF16(url), base::UTF8ToUTF16(function)));
127 }
128
97 JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito, 129 JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito,
98 const string16& source, 130 const string16& source,
99 const string16& message, 131 const string16& message,
100 logging::LogSeverity level, 132 const string16& stack_trace,
101 const string16& details) 133 int32 line_number,
134 const GURL& context_url,
135 logging::LogSeverity level)
102 : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR, 136 : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR,
103 std::string(), // We don't know the id yet. 137 GURL(source).host(),
104 from_incognito, 138 from_incognito,
105 source, 139 source,
106 message), 140 message),
141 context_url_(context_url),
107 level_(level) { 142 level_(level) {
108 ParseDetails(details); 143 GetStackTrace(message, stack_trace, line_number);
109 DetermineExtensionID(); 144 CleanUpInit();
110 } 145 }
111 146
112 JavascriptRuntimeError::~JavascriptRuntimeError() { 147 JavascriptRuntimeError::~JavascriptRuntimeError() {
113 } 148 }
114 149
115 std::string JavascriptRuntimeError::PrintForTest() const { 150 std::string JavascriptRuntimeError::PrintForTest() const {
116 std::string result = ExtensionError::PrintForTest() + 151 std::string result = ExtensionError::PrintForTest() +
117 "\n Type: JavascriptRuntimeError" 152 "\n Type: JavascriptRuntimeError"
118 "\n Context: " + base::UTF16ToUTF8(execution_context_url_) + 153 "\n Context: " + context_url_.spec() +
119 "\n Stack Trace: "; 154 "\n Stack Trace: ";
120 for (StackTrace::const_iterator iter = stack_trace_.begin(); 155 for (StackTrace::const_iterator iter = stack_trace_.begin();
121 iter != stack_trace_.end(); ++iter) { 156 iter != stack_trace_.end(); ++iter) {
122 result += "\n {" 157 result += "\n {"
123 "\n Line: " + base::IntToString(iter->line_number) + 158 "\n Line: " + base::IntToString((*iter)->line_number) +
124 "\n Column: " + base::IntToString(iter->column_number) + 159 "\n Column: " + base::IntToString((*iter)->column_number) +
125 "\n URL: " + base::UTF16ToUTF8(iter->url) + 160 "\n URL: " + base::UTF16ToUTF8((*iter)->url) +
126 "\n Function: " + base::UTF16ToUTF8(iter->function) + 161 "\n Function: " + base::UTF16ToUTF8((*iter)->function) +
127 "\n }"; 162 "\n }";
128 } 163 }
129 return result; 164 return result;
130 } 165 }
131 166
132 void JavascriptRuntimeError::ParseDetails(const string16& details) { 167 void JavascriptRuntimeError::GetStackTrace(const string16& message,
133 scoped_ptr<base::Value> value( 168 const string16& stack_trace,
134 base::JSONReader::Read(base::UTF16ToUTF8(details))); 169 int32 line_number) {
135 const base::DictionaryValue* details_value; 170 std::vector<string16> pieces;
136 const base::ListValue* trace_value = NULL; 171 size_t index = 0;
137 172
138 // The |details| value should contain an execution context url and a stack 173 // There are two possible scenarios:
139 // trace. 174 // 1. WebKit gives us a stack trace in |stack_trace|.
140 if (!value.get() || 175 // 2. The stack trace is embedded in the error |message| by the extension
141 !value->GetAsDictionary(&details_value) || 176 // system (such as in the evaluation of a browser action). This will be
142 !details_value->GetString(kExecutionContextURLKey, 177 // more useful than |stack_trace|, since |stack_trace| will include the
143 &execution_context_url_) || 178 // internal bindings trace, instead of a developer's code.
144 !details_value->GetList(kStackTraceKey, &trace_value)) { 179 if (message.find(base::UTF8ToUTF16(kStackFrameDelimiter)) != string16::npos) {
145 NOTREACHED(); 180 base::SplitStringUsingSubstr(message,
146 return; 181 base::UTF8ToUTF16(kStackFrameDelimiter),
182 &pieces);
183 message_ = pieces[0];
184 index = 1;
185 } else if (!stack_trace.empty()) {
186 base::SplitStringUsingSubstr(stack_trace,
187 base::UTF8ToUTF16(kStackFrameDelimiter),
188 &pieces);
147 } 189 }
148 190
149 int line = 0; 191 // If we got a stack trace, parse each frame from the text.
150 int column = 0; 192 if (index < pieces.size()) {
151 string16 url; 193 for (; index < pieces.size(); ++index) {
152 194 scoped_ptr<StackFrame> frame = StackFrame::CreateFromText(pieces[index]);
153 for (size_t i = 0; i < trace_value->GetSize(); ++i) { 195 if (frame.get())
154 const base::DictionaryValue* frame_value = NULL; 196 stack_trace_.push_back(frame.release());
155 CHECK(trace_value->GetDictionary(i, &frame_value)); 197 }
156 198 } else { // Otherwise, mock one up from the given line/url.
157 frame_value->GetInteger(kLineNumberKey, &line); 199 stack_trace_.push_back(new StackFrame(line_number,
Cris Neckar 2013/08/27 19:10:47 ditto for this vector.. Having this complexity on
Devlin 2013/08/27 23:51:33 Done.
158 frame_value->GetInteger(kColumnNumberKey, &column); 200 1, // column number
159 frame_value->GetString(kURLKey, &url); 201 source_,
160 202 UTF8ToUTF16(kAnonymousFunction)));
161 string16 function;
162 frame_value->GetString(kFunctionNameKey, &function); // This can be empty.
163 stack_trace_.push_back(StackFrame(line, column, url, function));
164 } 203 }
165 } 204 }
166 205
167 void JavascriptRuntimeError::DetermineExtensionID() { 206 void JavascriptRuntimeError::CleanUpInit() {
168 if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_)) 207 // If the error came from a generated background page, the "context" is empty
169 GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_); 208 // because there's no visible URL. We should set context to be the generated
209 // background page in this case.
210 GURL source_url = GURL(source_);
211 if (context_url_.is_empty() &&
212 source_url.path() ==
213 std::string("/") + kGeneratedBackgroundPageFilename) {
214 context_url_ = source_url;
215 }
216
217 // In some instances (due to the fact that we're reusing error reporting from
218 // other systems), the source won't match up with the final entry in the stack
219 // trace. (For instance, in a browser action error, the source is the page -
220 // sometimes the background page - but the error is thrown from the script.)
221 // Make the source match the stack trace, since that is more likely the cause
222 // of the error.
223 if (!stack_trace_.empty() && source_ != stack_trace_[0]->url)
224 source_ = stack_trace_[0]->url;
170 } 225 }
171 226
172 } // namespace extensions 227 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/browser/extension_error.h ('k') | extensions/browser/extension_error_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698