OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |