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" | 7 #include "base/json/json_reader.h" |
8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
9 #include "base/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
10 #include "base/values.h" | 10 #include "base/values.h" |
11 #include "extensions/common/constants.h" | 11 #include "extensions/common/constants.h" |
12 #include "url/gurl.h" | 12 #include "url/gurl.h" |
13 | 13 |
14 using base::string16; | 14 using base::string16; |
| 15 using base::DictionaryValue; |
15 | 16 |
16 namespace extensions { | 17 namespace extensions { |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
| 21 const char kTypeKey[] = "type"; |
| 22 const char kSourceKey[] = "source"; |
| 23 const char kMessageKey[] = "message"; |
| 24 const char kExtensionIdKey[] = "extensionId"; |
| 25 const char kFromIncognitoKey[] = "fromIncognito"; |
| 26 const char kManifestKeyKey[] = "manifestKey"; |
| 27 const char kManifestSpecificKey[] = "manifestSpecific"; |
| 28 const char kLevelKey[] = "level"; |
20 const char kLineNumberKey[] = "lineNumber"; | 29 const char kLineNumberKey[] = "lineNumber"; |
21 const char kColumnNumberKey[] = "columnNumber"; | 30 const char kColumnNumberKey[] = "columnNumber"; |
22 const char kURLKey[] = "url"; | 31 const char kURLKey[] = "url"; |
23 const char kFunctionNameKey[] = "functionName"; | 32 const char kFunctionNameKey[] = "functionName"; |
24 const char kExecutionContextURLKey[] = "executionContextURL"; | 33 const char kExecutionContextURLKey[] = "executionContextURL"; |
25 const char kStackTraceKey[] = "stackTrace"; | 34 const char kStackTraceKey[] = "stackTrace"; |
26 | 35 |
27 // Try to retrieve an extension ID from a |url|. On success, returns true and | 36 // 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 | 37 // populates |extension_id| with the ID. On failure, returns false and leaves |
29 // extension_id untouched. | 38 // extension_id untouched. |
30 bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) { | 39 bool GetExtensionIdFromGURL(const GURL& url, std::string* extension_id) { |
31 if (url.SchemeIs(kExtensionScheme)) { | 40 if (url.SchemeIs(kExtensionScheme)) { |
32 *extension_id = url.host(); | 41 *extension_id = url.host(); |
33 return true; | 42 return true; |
34 } | 43 } |
35 return false; | 44 return false; |
36 } | 45 } |
37 | 46 |
38 } // namespace | 47 } // namespace |
39 | 48 |
40 ExtensionError::ExtensionError(Type type, | 49 ExtensionError::ExtensionError(Type type, |
41 const std::string& extension_id, | 50 const std::string& extension_id, |
42 bool from_incognito, | 51 bool from_incognito, |
43 const string16& source, | 52 const string16& source, |
44 const string16& message) | 53 const string16& message) |
45 : type_(type), | 54 : type_(type), |
46 extension_id_(extension_id), | 55 extension_id_(extension_id), |
47 from_incognito_(from_incognito), | 56 from_incognito_(from_incognito), |
48 source_(source), | 57 source_(source), |
49 message_(message) { | 58 message_(message) { |
50 } | 59 } |
51 | 60 |
52 ExtensionError::~ExtensionError() { | 61 ExtensionError::~ExtensionError() { |
53 } | 62 } |
54 | 63 |
| 64 scoped_ptr<DictionaryValue> ExtensionError::ToValue() const { |
| 65 scoped_ptr<DictionaryValue> value(new DictionaryValue); |
| 66 value->SetInteger(kTypeKey, static_cast<int>(type_)); |
| 67 value->SetString(kExtensionIdKey, extension_id_); |
| 68 value->SetBoolean(kFromIncognitoKey, from_incognito_); |
| 69 value->SetString(kSourceKey, source_); |
| 70 value->SetString(kMessageKey, message_); |
| 71 |
| 72 return value.Pass(); |
| 73 } |
| 74 |
55 std::string ExtensionError::PrintForTest() const { | 75 std::string ExtensionError::PrintForTest() const { |
56 return std::string("Extension Error:") + | 76 return std::string("Extension Error:") + |
57 "\n OTR: " + std::string(from_incognito_ ? "true" : "false") + | 77 "\n OTR: " + std::string(from_incognito_ ? "true" : "false") + |
58 "\n Source: " + base::UTF16ToUTF8(source_) + | 78 "\n Source: " + base::UTF16ToUTF8(source_) + |
59 "\n Message: " + base::UTF16ToUTF8(message_) + | 79 "\n Message: " + base::UTF16ToUTF8(message_) + |
60 "\n ID: " + extension_id_; | 80 "\n ID: " + extension_id_; |
61 } | 81 } |
62 | 82 |
| 83 bool ExtensionError::IsEqual(const ExtensionError* rhs) const { |
| 84 return type_ == rhs->type_ && |
| 85 extension_id_ == rhs->extension_id_ && |
| 86 source_ == rhs->source_ && |
| 87 message_ == rhs->message_ && |
| 88 IsEqualImpl(rhs); |
| 89 } |
| 90 |
63 ManifestParsingError::ManifestParsingError(const std::string& extension_id, | 91 ManifestParsingError::ManifestParsingError(const std::string& extension_id, |
64 const string16& message) | 92 const string16& message, |
| 93 const string16& manifest_key, |
| 94 const string16& manifest_specific) |
65 : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR, | 95 : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR, |
66 extension_id, | 96 extension_id, |
67 false, // extensions can't be installed while incognito. | 97 false, // extensions can't be installed while incognito. |
68 base::FilePath(kManifestFilename).AsUTF16Unsafe(), | 98 base::FilePath(kManifestFilename).AsUTF16Unsafe(), |
69 message) { | 99 message), |
| 100 manifest_key_(manifest_key), |
| 101 manifest_specific_(manifest_specific) { |
70 } | 102 } |
71 | 103 |
72 ManifestParsingError::~ManifestParsingError() { | 104 ManifestParsingError::~ManifestParsingError() { |
73 } | 105 } |
74 | 106 |
| 107 scoped_ptr<DictionaryValue> ManifestParsingError::ToValue() const { |
| 108 scoped_ptr<DictionaryValue> value = ExtensionError::ToValue(); |
| 109 // All manifest errors are warnings - if it was a fatal error, we wouldn't |
| 110 // get far enough to add an error for the extension. |
| 111 value->SetInteger(kLevelKey, logging::LOG_WARNING); |
| 112 if (!manifest_key_.empty()) |
| 113 value->SetString(kManifestKeyKey, manifest_key_); |
| 114 if (!manifest_specific_.empty()) |
| 115 value->SetString(kManifestSpecificKey, manifest_specific_); |
| 116 return value.Pass(); |
| 117 } |
| 118 |
75 std::string ManifestParsingError::PrintForTest() const { | 119 std::string ManifestParsingError::PrintForTest() const { |
76 return ExtensionError::PrintForTest() + | 120 return ExtensionError::PrintForTest() + |
77 "\n Type: ManifestParsingError"; | 121 "\n Type: ManifestParsingError"; |
78 } | 122 } |
79 | 123 |
| 124 bool ManifestParsingError::IsEqualImpl(const ExtensionError* rhs) const { |
| 125 // If two manifest errors have the same extension id and message (which are |
| 126 // both checked in ExtensionError::IsEqual), then they are equal. |
| 127 return true; |
| 128 } |
| 129 |
80 JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1), | 130 JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1), |
81 column_number(-1) { | 131 column_number(-1) { |
82 } | 132 } |
83 | 133 |
84 JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line, | 134 JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line, |
85 size_t frame_column, | 135 size_t frame_column, |
86 const string16& frame_url, | 136 const string16& frame_url, |
87 const string16& frame_function) | 137 const string16& frame_function) |
88 : line_number(frame_line), | 138 : line_number(frame_line), |
89 column_number(frame_column), | 139 column_number(frame_column), |
90 url(frame_url), | 140 url(frame_url), |
91 function(frame_function) { | 141 function(frame_function) { |
92 } | 142 } |
93 | 143 |
94 JavascriptRuntimeError::StackFrame::~StackFrame() { | 144 JavascriptRuntimeError::StackFrame::~StackFrame() { |
95 } | 145 } |
96 | 146 |
| 147 bool JavascriptRuntimeError::StackFrame::operator==( |
| 148 const JavascriptRuntimeError::StackFrame& rhs) const { |
| 149 return line_number == rhs.line_number && |
| 150 column_number == rhs.column_number && |
| 151 url == rhs.url && |
| 152 function == rhs.function; |
| 153 } |
97 JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito, | 154 JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito, |
98 const string16& source, | 155 const string16& source, |
99 const string16& message, | 156 const string16& message, |
100 logging::LogSeverity level, | 157 logging::LogSeverity level, |
101 const string16& details) | 158 const string16& details) |
102 : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR, | 159 : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR, |
103 std::string(), // We don't know the id yet. | 160 std::string(), // We don't know the id yet. |
104 from_incognito, | 161 from_incognito, |
105 source, | 162 source, |
106 message), | 163 message), |
107 level_(level) { | 164 level_(level) { |
108 ParseDetails(details); | 165 ParseDetails(details); |
109 DetermineExtensionID(); | 166 DetermineExtensionId(); |
110 } | 167 } |
111 | 168 |
112 JavascriptRuntimeError::~JavascriptRuntimeError() { | 169 JavascriptRuntimeError::~JavascriptRuntimeError() { |
113 } | 170 } |
114 | 171 |
115 std::string JavascriptRuntimeError::PrintForTest() const { | 172 std::string JavascriptRuntimeError::PrintForTest() const { |
116 std::string result = ExtensionError::PrintForTest() + | 173 std::string result = ExtensionError::PrintForTest() + |
117 "\n Type: JavascriptRuntimeError" | 174 "\n Type: JavascriptRuntimeError" |
118 "\n Context: " + base::UTF16ToUTF8(execution_context_url_) + | 175 "\n Context: " + base::UTF16ToUTF8(execution_context_url_) + |
119 "\n Stack Trace: "; | 176 "\n Stack Trace: "; |
120 for (StackTrace::const_iterator iter = stack_trace_.begin(); | 177 for (StackTrace::const_iterator iter = stack_trace_.begin(); |
121 iter != stack_trace_.end(); ++iter) { | 178 iter != stack_trace_.end(); ++iter) { |
122 result += "\n {" | 179 result += "\n {" |
123 "\n Line: " + base::IntToString(iter->line_number) + | 180 "\n Line: " + base::IntToString(iter->line_number) + |
124 "\n Column: " + base::IntToString(iter->column_number) + | 181 "\n Column: " + base::IntToString(iter->column_number) + |
125 "\n URL: " + base::UTF16ToUTF8(iter->url) + | 182 "\n URL: " + base::UTF16ToUTF8(iter->url) + |
126 "\n Function: " + base::UTF16ToUTF8(iter->function) + | 183 "\n Function: " + base::UTF16ToUTF8(iter->function) + |
127 "\n }"; | 184 "\n }"; |
128 } | 185 } |
129 return result; | 186 return result; |
130 } | 187 } |
131 | 188 |
| 189 bool JavascriptRuntimeError::IsEqualImpl(const ExtensionError* rhs) const { |
| 190 const JavascriptRuntimeError* error = |
| 191 static_cast<const JavascriptRuntimeError*>(rhs); |
| 192 |
| 193 // We don't look at the full stack trace, because if the first frame is |
| 194 // the same, it's close enough to heuristically count as a duplicate (after |
| 195 // all, the same line caused the error). |
| 196 // If the stack trace is empty, just compare the context. |
| 197 return execution_context_url_ == error->execution_context_url_ && |
| 198 stack_trace_.size() == error->stack_trace_.size() && |
| 199 (stack_trace_.empty() || stack_trace_[0] == error->stack_trace_[0]); |
| 200 } |
| 201 |
132 void JavascriptRuntimeError::ParseDetails(const string16& details) { | 202 void JavascriptRuntimeError::ParseDetails(const string16& details) { |
133 scoped_ptr<base::Value> value( | 203 scoped_ptr<base::Value> value( |
134 base::JSONReader::Read(base::UTF16ToUTF8(details))); | 204 base::JSONReader::Read(base::UTF16ToUTF8(details))); |
135 const base::DictionaryValue* details_value; | 205 const DictionaryValue* details_value; |
136 const base::ListValue* trace_value = NULL; | 206 const base::ListValue* trace_value = NULL; |
137 | 207 |
138 // The |details| value should contain an execution context url and a stack | 208 // The |details| value should contain an execution context url and a stack |
139 // trace. | 209 // trace. |
140 if (!value.get() || | 210 if (!value.get() || |
141 !value->GetAsDictionary(&details_value) || | 211 !value->GetAsDictionary(&details_value) || |
142 !details_value->GetString(kExecutionContextURLKey, | 212 !details_value->GetString(kExecutionContextURLKey, |
143 &execution_context_url_) || | 213 &execution_context_url_) || |
144 !details_value->GetList(kStackTraceKey, &trace_value)) { | 214 !details_value->GetList(kStackTraceKey, &trace_value)) { |
145 NOTREACHED(); | 215 NOTREACHED(); |
146 return; | 216 return; |
147 } | 217 } |
148 | 218 |
149 int line = 0; | 219 int line = 0; |
150 int column = 0; | 220 int column = 0; |
151 string16 url; | 221 string16 url; |
152 | 222 |
153 for (size_t i = 0; i < trace_value->GetSize(); ++i) { | 223 for (size_t i = 0; i < trace_value->GetSize(); ++i) { |
154 const base::DictionaryValue* frame_value = NULL; | 224 const DictionaryValue* frame_value = NULL; |
155 CHECK(trace_value->GetDictionary(i, &frame_value)); | 225 CHECK(trace_value->GetDictionary(i, &frame_value)); |
156 | 226 |
157 frame_value->GetInteger(kLineNumberKey, &line); | 227 frame_value->GetInteger(kLineNumberKey, &line); |
158 frame_value->GetInteger(kColumnNumberKey, &column); | 228 frame_value->GetInteger(kColumnNumberKey, &column); |
159 frame_value->GetString(kURLKey, &url); | 229 frame_value->GetString(kURLKey, &url); |
160 | 230 |
161 string16 function; | 231 string16 function; |
162 frame_value->GetString(kFunctionNameKey, &function); // This can be empty. | 232 frame_value->GetString(kFunctionNameKey, &function); // This can be empty. |
163 stack_trace_.push_back(StackFrame(line, column, url, function)); | 233 stack_trace_.push_back(StackFrame(line, column, url, function)); |
164 } | 234 } |
165 } | 235 } |
166 | 236 |
167 void JavascriptRuntimeError::DetermineExtensionID() { | 237 void JavascriptRuntimeError::DetermineExtensionId() { |
168 if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_)) | 238 if (!GetExtensionIdFromGURL(GURL(source_), &extension_id_)) |
169 GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_); | 239 GetExtensionIdFromGURL(GURL(execution_context_url_), &extension_id_); |
170 } | 240 } |
171 | 241 |
172 } // namespace extensions | 242 } // namespace extensions |
OLD | NEW |