Chromium Code Reviews| 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 |