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