Home | History | Annotate | Download | only in browser
      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::DictionaryValue;
     14 
     15 namespace extensions {
     16 
     17 ////////////////////////////////////////////////////////////////////////////////
     18 // ExtensionError
     19 
     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";
     27 
     28 ExtensionError::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 
     43 ExtensionError::~ExtensionError() {
     44 }
     45 
     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 
     60 std::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 
     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 
     78 ////////////////////////////////////////////////////////////////////////////////
     79 // ManifestError
     80 
     81 // Static JSON keys.
     82 const char ManifestError::kManifestKeyKey[] = "manifestKey";
     83 const char ManifestError::kManifestSpecificKey[] = "manifestSpecific";
     84 
     85 ManifestError::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 
     99 ManifestError::~ManifestError() {
    100 }
    101 
    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 
    111 std::string ManifestError::PrintForTest() const {
    112   return ExtensionError::PrintForTest() +
    113          "\n  Type:    ManifestError";
    114 }
    115 
    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;
    120 }
    121 
    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";
    132 const char RuntimeError::kRenderProcessIdKey[] = "renderProcessId";
    133 const char RuntimeError::kRenderViewIdKey[] = "renderViewId";
    134 
    135 RuntimeError::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 
    157 RuntimeError::~RuntimeError() {
    158 }
    159 
    160 scoped_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 
    186 std::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 
    203 bool 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 
    216 void 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