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/json/json_reader.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/values.h"
11#include "extensions/common/constants.h"
12#include "url/gurl.h"
13
14using base::string16;
15
16namespace extensions {
17
18namespace {
19
20const char kLineNumberKey[] = "lineNumber";
21const char kColumnNumberKey[] = "columnNumber";
22const char kURLKey[] = "url";
23const char kFunctionNameKey[] = "functionName";
24const char kExecutionContextURLKey[] = "executionContextURL";
25const char kStackTraceKey[] = "stackTrace";
26
27// Try to retrieve an extension ID from a |url|. On success, returns true and
28// populates |extension_id| with the ID. On failure, returns false and leaves
29// extension_id untouched.
30bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) {
31  if (url.SchemeIs(kExtensionScheme)) {
32    *extension_id = url.host();
33    return true;
34  }
35  return false;
36}
37
38}  // namespace
39
40ExtensionError::ExtensionError(Type type,
41                               const std::string& extension_id,
42                               bool from_incognito,
43                               const string16& source,
44                               const string16& message)
45    : type_(type),
46      extension_id_(extension_id),
47      from_incognito_(from_incognito),
48      source_(source),
49      message_(message) {
50}
51
52ExtensionError::~ExtensionError() {
53}
54
55std::string ExtensionError::PrintForTest() const {
56  return std::string("Extension Error:") +
57         "\n  OTR:     " + std::string(from_incognito_ ? "true" : "false") +
58         "\n  Source:  " + base::UTF16ToUTF8(source_) +
59         "\n  Message: " + base::UTF16ToUTF8(message_) +
60         "\n  ID:      " + extension_id_;
61}
62
63ManifestParsingError::ManifestParsingError(const std::string& extension_id,
64                                           const string16& message)
65    : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR,
66                     extension_id,
67                     false,  // extensions can't be installed while incognito.
68                     base::FilePath(kManifestFilename).AsUTF16Unsafe(),
69                     message) {
70}
71
72ManifestParsingError::~ManifestParsingError() {
73}
74
75std::string ManifestParsingError::PrintForTest() const {
76  return ExtensionError::PrintForTest() +
77         "\n  Type:    ManifestParsingError";
78}
79
80JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1),
81                                                   column_number(-1) {
82}
83
84JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line,
85                                               size_t frame_column,
86                                               const string16& frame_url,
87                                               const string16& frame_function)
88    : line_number(frame_line),
89      column_number(frame_column),
90      url(frame_url),
91      function(frame_function) {
92}
93
94JavascriptRuntimeError::StackFrame::~StackFrame() {
95}
96
97JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito,
98                                               const string16& source,
99                                               const string16& message,
100                                               logging::LogSeverity level,
101                                               const string16& details)
102    : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR,
103                     std::string(),  // We don't know the id yet.
104                     from_incognito,
105                     source,
106                     message),
107      level_(level) {
108  ParseDetails(details);
109  DetermineExtensionID();
110}
111
112JavascriptRuntimeError::~JavascriptRuntimeError() {
113}
114
115std::string JavascriptRuntimeError::PrintForTest() const {
116  std::string result = ExtensionError::PrintForTest() +
117         "\n  Type:    JavascriptRuntimeError"
118         "\n  Context: " + base::UTF16ToUTF8(execution_context_url_) +
119         "\n  Stack Trace: ";
120  for (StackTrace::const_iterator iter = stack_trace_.begin();
121       iter != stack_trace_.end(); ++iter) {
122    result += "\n    {"
123              "\n      Line:     " + base::IntToString(iter->line_number) +
124              "\n      Column:   " + base::IntToString(iter->column_number) +
125              "\n      URL:      " + base::UTF16ToUTF8(iter->url) +
126              "\n      Function: " + base::UTF16ToUTF8(iter->function) +
127              "\n    }";
128  }
129  return result;
130}
131
132void JavascriptRuntimeError::ParseDetails(const string16& details) {
133  scoped_ptr<base::Value> value(
134      base::JSONReader::Read(base::UTF16ToUTF8(details)));
135  const base::DictionaryValue* details_value;
136  const base::ListValue* trace_value = NULL;
137
138  // The |details| value should contain an execution context url and a stack
139  // trace.
140  if (!value.get() ||
141      !value->GetAsDictionary(&details_value) ||
142      !details_value->GetString(kExecutionContextURLKey,
143                                &execution_context_url_) ||
144      !details_value->GetList(kStackTraceKey, &trace_value)) {
145    NOTREACHED();
146    return;
147  }
148
149  int line = 0;
150  int column = 0;
151  string16 url;
152
153  for (size_t i = 0; i < trace_value->GetSize(); ++i) {
154    const base::DictionaryValue* frame_value = NULL;
155    CHECK(trace_value->GetDictionary(i, &frame_value));
156
157    frame_value->GetInteger(kLineNumberKey, &line);
158    frame_value->GetInteger(kColumnNumberKey, &column);
159    frame_value->GetString(kURLKey, &url);
160
161    string16 function;
162    frame_value->GetString(kFunctionNameKey, &function);  // This can be empty.
163    stack_trace_.push_back(StackFrame(line, column, url, function));
164  }
165}
166
167void JavascriptRuntimeError::DetermineExtensionID() {
168  if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_))
169    GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_);
170}
171
172}  // namespace extensions
173