[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 921237d06 | 2013-08-10 15:30:49 | [diff] [blame] | 5 | #include "extensions/browser/extension_error.h" |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 6 | |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 7 | #include "base/strings/string_number_conversions.h" |
| 8 | #include "base/strings/utf_string_conversions.h" |
| 9 | #include "base/values.h" |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 10 | #include "extensions/common/constants.h" |
[email protected] | 921237d06 | 2013-08-10 15:30:49 | [diff] [blame] | 11 | #include "url/gurl.h" |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 12 | |
[email protected] | fa5fed3 | 2013-09-05 21:56:22 | [diff] [blame] | 13 | using base::DictionaryValue; |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 14 | |
| 15 | namespace extensions { |
| 16 | |
[email protected] | fa5fed3 | 2013-09-05 21:56:22 | [diff] [blame] | 17 | //////////////////////////////////////////////////////////////////////////////// |
| 18 | // ExtensionError |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 19 | |
[email protected] | fa5fed3 | 2013-09-05 21:56:22 | [diff] [blame] | 20 | // Static JSON keys. |
| 21 | const char ExtensionError::kExtensionIdKey[] = "extensionId"; |
| 22 | const char ExtensionError::kFromIncognitoKey[] = "fromIncognito"; |
| 23 | const char ExtensionError::kLevelKey[] = "level"; |
| 24 | const char ExtensionError::kMessageKey[] = "message"; |
| 25 | const char ExtensionError::kSourceKey[] = "source"; |
| 26 | const char ExtensionError::kTypeKey[] = "type"; |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 27 | |
| 28 | ExtensionError::ExtensionError(Type type, |
[email protected] | 921237d06 | 2013-08-10 15:30:49 | [diff] [blame] | 29 | const std::string& extension_id, |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 30 | bool from_incognito, |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 31 | logging::LogSeverity level, |
[email protected] | 0d163dc | 2013-12-20 23:48:36 | [diff] [blame] | 32 | const base::string16& source, |
| 33 | const base::string16& message) |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 34 | : type_(type), |
[email protected] | 921237d06 | 2013-08-10 15:30:49 | [diff] [blame] | 35 | extension_id_(extension_id), |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 36 | from_incognito_(from_incognito), |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 37 | level_(level), |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 38 | source_(source), |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 39 | message_(message), |
| 40 | occurrences_(1u) { |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 41 | } |
| 42 | |
| 43 | ExtensionError::~ExtensionError() { |
| 44 | } |
| 45 | |
[email protected] | fa5fed3 | 2013-09-05 21:56:22 | [diff] [blame] | 46 | scoped_ptr<DictionaryValue> ExtensionError::ToValue() const { |
| 47 | // TODO(rdevlin.cronin): Use ValueBuilder when it's moved from |
| 48 | // chrome/common/extensions. |
| 49 | scoped_ptr<DictionaryValue> value(new DictionaryValue); |
| 50 | value->SetInteger(kTypeKey, static_cast<int>(type_)); |
| 51 | value->SetString(kExtensionIdKey, extension_id_); |
| 52 | value->SetBoolean(kFromIncognitoKey, from_incognito_); |
| 53 | value->SetInteger(kLevelKey, static_cast<int>(level_)); |
| 54 | value->SetString(kSourceKey, source_); |
| 55 | value->SetString(kMessageKey, message_); |
| 56 | |
| 57 | return value.Pass(); |
| 58 | } |
| 59 | |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 60 | std::string ExtensionError::PrintForTest() const { |
| 61 | return std::string("Extension Error:") + |
| 62 | "\n OTR: " + std::string(from_incognito_ ? "true" : "false") + |
[email protected] | 50af608 | 2013-09-10 02:05:19 | [diff] [blame] | 63 | "\n Level: " + base::IntToString(static_cast<int>(level_)) + |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 64 | "\n Source: " + base::UTF16ToUTF8(source_) + |
| 65 | "\n Message: " + base::UTF16ToUTF8(message_) + |
| 66 | "\n ID: " + extension_id_; |
| 67 | } |
| 68 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 69 | bool ExtensionError::IsEqual(const ExtensionError* rhs) const { |
| 70 | // We don't check |source_| or |level_| here, since they are constant for |
| 71 | // manifest errors. Check them in RuntimeError::IsEqualImpl() instead. |
| 72 | return type_ == rhs->type_ && |
| 73 | extension_id_ == rhs->extension_id_ && |
| 74 | message_ == rhs->message_ && |
| 75 | IsEqualImpl(rhs); |
| 76 | } |
| 77 | |
[email protected] | fa5fed3 | 2013-09-05 21:56:22 | [diff] [blame] | 78 | //////////////////////////////////////////////////////////////////////////////// |
| 79 | // ManifestError |
| 80 | |
| 81 | // Static JSON keys. |
| 82 | const char ManifestError::kManifestKeyKey[] = "manifestKey"; |
| 83 | const char ManifestError::kManifestSpecificKey[] = "manifestSpecific"; |
| 84 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 85 | ManifestError::ManifestError(const std::string& extension_id, |
[email protected] | 0d163dc | 2013-12-20 23:48:36 | [diff] [blame] | 86 | const base::string16& message, |
| 87 | const base::string16& manifest_key, |
| 88 | const base::string16& manifest_specific) |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 89 | : ExtensionError(ExtensionError::MANIFEST_ERROR, |
[email protected] | 921237d06 | 2013-08-10 15:30:49 | [diff] [blame] | 90 | extension_id, |
| 91 | false, // extensions can't be installed while incognito. |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 92 | logging::LOG_WARNING, // All manifest errors are warnings. |
[email protected] | 921237d06 | 2013-08-10 15:30:49 | [diff] [blame] | 93 | base::FilePath(kManifestFilename).AsUTF16Unsafe(), |
[email protected] | b191e2d3 | 2013-09-03 21:08:30 | [diff] [blame] | 94 | message), |
| 95 | manifest_key_(manifest_key), |
| 96 | manifest_specific_(manifest_specific) { |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 97 | } |
| 98 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 99 | ManifestError::~ManifestError() { |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 100 | } |
| 101 | |
[email protected] | fa5fed3 | 2013-09-05 21:56:22 | [diff] [blame] | 102 | scoped_ptr<DictionaryValue> ManifestError::ToValue() const { |
| 103 | scoped_ptr<DictionaryValue> value = ExtensionError::ToValue(); |
| 104 | if (!manifest_key_.empty()) |
| 105 | value->SetString(kManifestKeyKey, manifest_key_); |
| 106 | if (!manifest_specific_.empty()) |
| 107 | value->SetString(kManifestSpecificKey, manifest_specific_); |
| 108 | return value.Pass(); |
| 109 | } |
| 110 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 111 | std::string ManifestError::PrintForTest() const { |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 112 | return ExtensionError::PrintForTest() + |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 113 | "\n Type: ManifestError"; |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 114 | } |
| 115 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 116 | bool ManifestError::IsEqualImpl(const ExtensionError* rhs) const { |
| 117 | // If two manifest errors have the same extension id and message (which are |
| 118 | // both checked in ExtensionError::IsEqual), then they are equal. |
| 119 | return true; |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 120 | } |
| 121 | |
[email protected] | 2fb9bd2 | 2013-09-07 00:08:08 | [diff] [blame] | 122 | //////////////////////////////////////////////////////////////////////////////// |
| 123 | // RuntimeError |
| 124 | |
| 125 | // Static JSON keys. |
| 126 | const char RuntimeError::kColumnNumberKey[] = "columnNumber"; |
| 127 | const char RuntimeError::kContextUrlKey[] = "contextUrl"; |
| 128 | const char RuntimeError::kFunctionNameKey[] = "functionName"; |
| 129 | const char RuntimeError::kLineNumberKey[] = "lineNumber"; |
| 130 | const char RuntimeError::kStackTraceKey[] = "stackTrace"; |
| 131 | const char RuntimeError::kUrlKey[] = "url"; |
[email protected] | c934c38 | 2013-11-01 00:36:01 | [diff] [blame] | 132 | const char RuntimeError::kRenderProcessIdKey[] = "renderProcessId"; |
| 133 | const char RuntimeError::kRenderViewIdKey[] = "renderViewId"; |
[email protected] | 2fb9bd2 | 2013-09-07 00:08:08 | [diff] [blame] | 134 | |
[email protected] | a0ed268 | 2013-09-06 08:41:07 | [diff] [blame] | 135 | RuntimeError::RuntimeError(const std::string& extension_id, |
| 136 | bool from_incognito, |
[email protected] | 0d163dc | 2013-12-20 23:48:36 | [diff] [blame] | 137 | const base::string16& source, |
| 138 | const base::string16& message, |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 139 | const StackTrace& stack_trace, |
| 140 | const GURL& context_url, |
[email protected] | c934c38 | 2013-11-01 00:36:01 | [diff] [blame] | 141 | logging::LogSeverity level, |
| 142 | int render_view_id, |
| 143 | int render_process_id) |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 144 | : ExtensionError(ExtensionError::RUNTIME_ERROR, |
[email protected] | a0ed268 | 2013-09-06 08:41:07 | [diff] [blame] | 145 | !extension_id.empty() ? extension_id : GURL(source).host(), |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 146 | from_incognito, |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 147 | level, |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 148 | source, |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 149 | message), |
| 150 | context_url_(context_url), |
[email protected] | c934c38 | 2013-11-01 00:36:01 | [diff] [blame] | 151 | stack_trace_(stack_trace), |
| 152 | render_view_id_(render_view_id), |
| 153 | render_process_id_(render_process_id) { |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 154 | CleanUpInit(); |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 155 | } |
| 156 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 157 | RuntimeError::~RuntimeError() { |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 158 | } |
| 159 | |
[email protected] | 2fb9bd2 | 2013-09-07 00:08:08 | [diff] [blame] | 160 | scoped_ptr<DictionaryValue> RuntimeError::ToValue() const { |
| 161 | scoped_ptr<DictionaryValue> value = ExtensionError::ToValue(); |
| 162 | value->SetString(kContextUrlKey, context_url_.spec()); |
[email protected] | c934c38 | 2013-11-01 00:36:01 | [diff] [blame] | 163 | value->SetInteger(kRenderViewIdKey, render_view_id_); |
| 164 | value->SetInteger(kRenderProcessIdKey, render_process_id_); |
[email protected] | 2fb9bd2 | 2013-09-07 00:08:08 | [diff] [blame] | 165 | |
[email protected] | c934c38 | 2013-11-01 00:36:01 | [diff] [blame] | 166 | base::ListValue* trace_value = new base::ListValue; |
[email protected] | 2fb9bd2 | 2013-09-07 00:08:08 | [diff] [blame] | 167 | for (StackTrace::const_iterator iter = stack_trace_.begin(); |
| 168 | iter != stack_trace_.end(); ++iter) { |
| 169 | DictionaryValue* frame_value = new DictionaryValue; |
| 170 | frame_value->SetInteger(kLineNumberKey, iter->line_number); |
| 171 | frame_value->SetInteger(kColumnNumberKey, iter->column_number); |
| 172 | frame_value->SetString(kUrlKey, iter->source); |
| 173 | frame_value->SetString(kFunctionNameKey, iter->function); |
| 174 | trace_value->Append(frame_value); |
| 175 | } |
| 176 | |
| 177 | value->Set(kStackTraceKey, trace_value); |
| 178 | |
| 179 | return value.Pass(); |
| 180 | } |
| 181 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 182 | std::string RuntimeError::PrintForTest() const { |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 183 | std::string result = ExtensionError::PrintForTest() + |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 184 | "\n Type: RuntimeError" |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 185 | "\n Context: " + context_url_.spec() + |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 186 | "\n Stack Trace: "; |
| 187 | for (StackTrace::const_iterator iter = stack_trace_.begin(); |
| 188 | iter != stack_trace_.end(); ++iter) { |
| 189 | result += "\n {" |
| 190 | "\n Line: " + base::IntToString(iter->line_number) + |
| 191 | "\n Column: " + base::IntToString(iter->column_number) + |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 192 | "\n URL: " + base::UTF16ToUTF8(iter->source) + |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 193 | "\n Function: " + base::UTF16ToUTF8(iter->function) + |
| 194 | "\n }"; |
| 195 | } |
| 196 | return result; |
| 197 | } |
| 198 | |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 199 | bool RuntimeError::IsEqualImpl(const ExtensionError* rhs) const { |
| 200 | const RuntimeError* error = static_cast<const RuntimeError*>(rhs); |
| 201 | |
| 202 | // Only look at the first frame of a stack trace to save time and group |
| 203 | // nearly-identical errors. The most recent error is kept, so there's no risk |
| 204 | // of displaying an old and inaccurate stack trace. |
[email protected] | 010ff507 | 2013-09-10 08:36:50 | [diff] [blame] | 205 | return level_ == error->level_ && |
| 206 | source_ == error->source_ && |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 207 | context_url_ == error->context_url_ && |
[email protected] | d466f78 | 2013-08-28 21:59:23 | [diff] [blame] | 208 | stack_trace_.size() == error->stack_trace_.size() && |
| 209 | (stack_trace_.empty() || stack_trace_[0] == error->stack_trace_[0]); |
| 210 | } |
| 211 | |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 212 | void RuntimeError::CleanUpInit() { |
| 213 | // If the error came from a generated background page, the "context" is empty |
| 214 | // because there's no visible URL. We should set context to be the generated |
| 215 | // background page in this case. |
| 216 | GURL source_url = GURL(source_); |
| 217 | if (context_url_.is_empty() && |
| 218 | source_url.path() == |
| 219 | std::string("/") + kGeneratedBackgroundPageFilename) { |
| 220 | context_url_ = source_url; |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 221 | } |
| 222 | |
[email protected] | 88b50b6 | 2013-09-01 23:05:06 | [diff] [blame] | 223 | // In some instances (due to the fact that we're reusing error reporting from |
| 224 | // other systems), the source won't match up with the final entry in the stack |
| 225 | // trace. (For instance, in a browser action error, the source is the page - |
| 226 | // sometimes the background page - but the error is thrown from the script.) |
| 227 | // Make the source match the stack trace, since that is more likely the cause |
| 228 | // of the error. |
| 229 | if (!stack_trace_.empty() && source_ != stack_trace_[0].source) |
| 230 | source_ = stack_trace_[0].source; |
[email protected] | 1b66fdb | 2013-07-26 09:57:28 | [diff] [blame] | 231 | } |
| 232 | |
| 233 | } // namespace extensions |