// 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/json/json_reader.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::string16;
namespace extensions {
namespace {
const char kLineNumberKey[] = "lineNumber";
const char kColumnNumberKey[] = "columnNumber";
const char kURLKey[] = "url";
const char kFunctionNameKey[] = "functionName";
const char kExecutionContextURLKey[] = "executionContextURL";
const char kStackTraceKey[] = "stackTrace";
// Try to retrieve an extension ID from a |url|. On success, returns true and
// populates |extension_id| with the ID. On failure, returns false and leaves
// extension_id untouched.
bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) {
if (url.SchemeIs(kExtensionScheme)) {
*extension_id = url.host();
return true;
}
return false;
}
} // namespace
ExtensionError::ExtensionError(Type type,
const std::string& extension_id,
bool from_incognito,
logging::LogSeverity level,
const string16& source,
const string16& message)
: type_(type),
extension_id_(extension_id),
from_incognito_(from_incognito),
level_(level),
source_(source),
message_(message),
occurrences_(1u) {
}
ExtensionError::~ExtensionError() {
}
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::ManifestError(const std::string& extension_id,
const string16& message)
: 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) {
}
ManifestError::~ManifestError() {
}
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::StackFrame::StackFrame() : line_number(-1), column_number(-1) {
}
RuntimeError::StackFrame::StackFrame(size_t frame_line,
size_t frame_column,
const string16& frame_url,
const string16& frame_function)
: line_number(frame_line),
column_number(frame_column),
url(frame_url),
function(frame_function) {
}
RuntimeError::StackFrame::~StackFrame() {
}
bool RuntimeError::StackFrame::operator==(
const RuntimeError::StackFrame& rhs) const {
return line_number == rhs.line_number &&
column_number == rhs.column_number &&
url == rhs.url &&
function == rhs.function;
}
RuntimeError::RuntimeError(bool from_incognito,
const string16& source,
const string16& message,
logging::LogSeverity level,
const string16& details)
: ExtensionError(ExtensionError::RUNTIME_ERROR,
std::string(), // We don't know the id yet.
from_incognito,
level,
source,
message) {
ParseDetails(details);
DetermineExtensionID();
}
RuntimeError::~RuntimeError() {
}
std::string RuntimeError::PrintForTest() const {
std::string result = ExtensionError::PrintForTest() +
"\n Type: RuntimeError"
"\n Context: " + base::UTF16ToUTF8(execution_context_url_) +
"\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->url) +
"\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_ == level_ &&
source_ == source_ &&
execution_context_url_ == error->execution_context_url_ &&
stack_trace_.size() == error->stack_trace_.size() &&
(stack_trace_.empty() || stack_trace_[0] == error->stack_trace_[0]);
}
void RuntimeError::ParseDetails(const string16& details) {
scoped_ptr<base::Value> value(
base::JSONReader::Read(base::UTF16ToUTF8(details)));
const base::DictionaryValue* details_value;
const base::ListValue* trace_value = NULL;
// The |details| value should contain an execution context url and a stack
// trace.
if (!value.get() ||
!value->GetAsDictionary(&details_value) ||
!details_value->GetString(kExecutionContextURLKey,
&execution_context_url_) ||
!details_value->GetList(kStackTraceKey, &trace_value)) {
NOTREACHED();
return;
}
int line = 0;
int column = 0;
string16 url;
for (size_t i = 0; i < trace_value->GetSize(); ++i) {
const base::DictionaryValue* frame_value = NULL;
CHECK(trace_value->GetDictionary(i, &frame_value));
frame_value->GetInteger(kLineNumberKey, &line);
frame_value->GetInteger(kColumnNumberKey, &column);
frame_value->GetString(kURLKey, &url);
string16 function;
frame_value->GetString(kFunctionNameKey, &function); // This can be empty.
stack_trace_.push_back(StackFrame(line, column, url, function));
}
}
void RuntimeError::DetermineExtensionID() {
if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_))
GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_);
}
} // namespace extensions