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