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