// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/extension_error.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/constants.h"
#include "url/gurl.h"
using base::DictionaryValue;
namespace extensions {
////////////////////////////////////////////////////////////////////////////////
// ExtensionError
// Static JSON keys.
const char ExtensionError::kExtensionIdKey[] = "extensionId";
const char ExtensionError::kFromIncognitoKey[] = "fromIncognito";
const char ExtensionError::kLevelKey[] = "level";
const char ExtensionError::kMessageKey[] = "message";
const char ExtensionError::kSourceKey[] = "source";
const char ExtensionError::kTypeKey[] = "type";
ExtensionError::ExtensionError(Type type,
const std::string& extension_id,
bool from_incognito,
logging::LogSeverity level,
const base::string16& source,
const base::string16& message)
: type_(type),
extension_id_(extension_id),
from_incognito_(from_incognito),
level_(level),
source_(source),
message_(message),
occurrences_(1u) {
}
ExtensionError::~ExtensionError() {
}
scoped_ptr<DictionaryValue> ExtensionError::ToValue() const {
// TODO(rdevlin.cronin): Use ValueBuilder when it's moved from
// chrome/common/extensions.
scoped_ptr<DictionaryValue> value(new DictionaryValue);
value->SetInteger(kTypeKey, static_cast<int>(type_));
value->SetString(kExtensionIdKey, extension_id_);
value->SetBoolean(kFromIncognitoKey, from_incognito_);
value->SetInteger(kLevelKey, static_cast<int>(level_));
value->SetString(kSourceKey, source_);
value->SetString(kMessageKey, message_);
return value.Pass();
}
std::string ExtensionError::PrintForTest() const {
return std::string("Extension Error:") +
"\n OTR: " + std::string(from_incognito_ ? "true" : "false") +
"\n Level: " + base::IntToString(static_cast<int>(level_)) +
"\n Source: " + base::UTF16ToUTF8(source_) +
"\n Message: " + base::UTF16ToUTF8(message_) +
"\n ID: " + extension_id_;
}
bool ExtensionError::IsEqual(const ExtensionError* rhs) const {
// We don't check |source_| or |level_| here, since they are constant for
// manifest errors. Check them in RuntimeError::IsEqualImpl() instead.
return type_ == rhs->type_ &&
extension_id_ == rhs->extension_id_ &&
message_ == rhs->message_ &&
IsEqualImpl(rhs);
}
////////////////////////////////////////////////////////////////////////////////
// ManifestError
// Static JSON keys.
const char ManifestError::kManifestKeyKey[] = "manifestKey";
const char ManifestError::kManifestSpecificKey[] = "manifestSpecific";
ManifestError::ManifestError(const std::string& extension_id,
const base::string16& message,
const base::string16& manifest_key,
const base::string16& manifest_specific)
: ExtensionError(ExtensionError::MANIFEST_ERROR,
extension_id,
false, // extensions can't be installed while incognito.
logging::LOG_WARNING, // All manifest errors are warnings.
base::FilePath(kManifestFilename).AsUTF16Unsafe(),
message),
manifest_key_(manifest_key),
manifest_specific_(manifest_specific) {
}
ManifestError::~ManifestError() {
}
scoped_ptr<DictionaryValue> ManifestError::ToValue() const {
scoped_ptr<DictionaryValue> value = ExtensionError::ToValue();
if (!manifest_key_.empty())
value->SetString(kManifestKeyKey, manifest_key_);
if (!manifest_specific_.empty())
value->SetString(kManifestSpecificKey, manifest_specific_);
return value.Pass();
}
std::string ManifestError::PrintForTest() const {
return ExtensionError::PrintForTest() +
"\n Type: ManifestError";
}
bool ManifestError::IsEqualImpl(const ExtensionError* rhs) const {
// If two manifest errors have the same extension id and message (which are
// both checked in ExtensionError::IsEqual), then they are equal.
return true;
}
////////////////////////////////////////////////////////////////////////////////
// RuntimeError
// Static JSON keys.
const char RuntimeError::kColumnNumberKey[] = "columnNumber";
const char RuntimeError::kContextUrlKey[] = "contextUrl";
const char RuntimeError::kFunctionNameKey[] = "functionName";
const char RuntimeError::kLineNumberKey[] = "lineNumber";
const char RuntimeError::kStackTraceKey[] = "stackTrace";
const char RuntimeError::kUrlKey[] = "url";
const char RuntimeError::kRenderProcessIdKey[] = "renderProcessId";
const char RuntimeError::kRenderViewIdKey[] = "renderViewId";
RuntimeError::RuntimeError(const std::string& extension_id,
bool from_incognito,
const base::string16& source,
const base::string16& message,
const StackTrace& stack_trace,
const GURL& context_url,
logging::LogSeverity level,
int render_view_id,
int render_process_id)
: ExtensionError(ExtensionError::RUNTIME_ERROR,
!extension_id.empty() ? extension_id : GURL(source).host(),
from_incognito,
level,
source,
message),
context_url_(context_url),
stack_trace_(stack_trace),
render_view_id_(render_view_id),
render_process_id_(render_process_id) {
CleanUpInit();
}
RuntimeError::~RuntimeError() {
}
scoped_ptr<DictionaryValue> RuntimeError::ToValue() const {
scoped_ptr<DictionaryValue> value = ExtensionError::ToValue();
value->SetString(kContextUrlKey, context_url_.spec());
value->SetInteger(kRenderViewIdKey, render_view_id_);
value->SetInteger(kRenderProcessIdKey, render_process_id_);
base::ListValue* trace_value = new base::ListValue;
for (StackTrace::const_iterator iter = stack_trace_.begin();
iter != stack_trace_.end(); ++iter) {
DictionaryValue* frame_value = new DictionaryValue;
frame_value->SetInteger(kLineNumberKey, iter->line_number);
frame_value->SetInteger(kColumnNumberKey, iter->column_number);
frame_value->SetString(kUrlKey, iter->source);
frame_value->SetString(kFunctionNameKey, iter->function);
trace_value->Append(frame_value);
}
value->Set(kStackTraceKey, trace_value);
return value.Pass();
}
std::string RuntimeError::PrintForTest() const {
std::string result = ExtensionError::PrintForTest() +
"\n Type: RuntimeError"
"\n Context: " + context_url_.spec() +
"\n Stack Trace: ";
for (StackTrace::const_iterator iter = stack_trace_.begin();
iter != stack_trace_.end(); ++iter) {
result += "\n {"
"\n Line: " + base::IntToString(iter->line_number) +
"\n Column: " + base::IntToString(iter->column_number) +
"\n URL: " + base::UTF16ToUTF8(iter->source) +
"\n Function: " + base::UTF16ToUTF8(iter->function) +
"\n }";
}
return result;
}
bool RuntimeError::IsEqualImpl(const ExtensionError* rhs) const {
const RuntimeError* error = static_cast<const RuntimeError*>(rhs);
// Only look at the first frame of a stack trace to save time and group
// nearly-identical errors. The most recent error is kept, so there's no risk
// of displaying an old and inaccurate stack trace.
return level_ == error->level_ &&
source_ == error->source_ &&
context_url_ == error->context_url_ &&
stack_trace_.size() == error->stack_trace_.size() &&
(stack_trace_.empty() || stack_trace_[0] == error->stack_trace_[0]);
}
void RuntimeError::CleanUpInit() {
// If the error came from a generated background page, the "context" is empty
// because there's no visible URL. We should set context to be the generated
// background page in this case.
GURL source_url = GURL(source_);
if (context_url_.is_empty() &&
source_url.path() ==
std::string("/") + kGeneratedBackgroundPageFilename) {
context_url_ = source_url;
}
// In some instances (due to the fact that we're reusing error reporting from
// other systems), the source won't match up with the final entry in the stack
// trace. (For instance, in a browser action error, the source is the page -
// sometimes the background page - but the error is thrown from the script.)
// Make the source match the stack trace, since that is more likely the cause
// of the error.
if (!stack_trace_.empty() && source_ != stack_trace_[0].source)
source_ = stack_trace_[0].source;
}
} // namespace extensions