| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "extensions/browser/extension_error.h" | 5 #include "extensions/browser/extension_error.h" |
| 6 | 6 |
| 7 #include "base/json/json_reader.h" | 7 #include "base/json/json_reader.h" |
| 8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "base/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
| 10 #include "base/values.h" | 10 #include "base/values.h" |
| 11 #include "extensions/common/constants.h" | 11 #include "extensions/common/constants.h" |
| 12 #include "url/gurl.h" | 12 #include "url/gurl.h" |
| 13 | 13 |
| 14 using base::string16; | 14 using base::string16; |
| 15 using base::DictionaryValue; |
| 15 | 16 |
| 16 namespace extensions { | 17 namespace extensions { |
| 17 | 18 |
| 18 namespace { | 19 namespace { |
| 19 | 20 |
| 21 const char kTypeKey[] = "type"; |
| 22 const char kSourceKey[] = "source"; |
| 23 const char kMessageKey[] = "message"; |
| 24 const char kExtensionIdKey[] = "extensionId"; |
| 25 const char kFromIncognitoKey[] = "fromIncognito"; |
| 26 const char kManifestKeyKey[] = "manifestKey"; |
| 27 const char kManifestSpecificKey[] = "manifestSpecific"; |
| 28 const char kLevelKey[] = "level"; |
| 20 const char kLineNumberKey[] = "lineNumber"; | 29 const char kLineNumberKey[] = "lineNumber"; |
| 21 const char kColumnNumberKey[] = "columnNumber"; | 30 const char kColumnNumberKey[] = "columnNumber"; |
| 22 const char kURLKey[] = "url"; | 31 const char kURLKey[] = "url"; |
| 23 const char kFunctionNameKey[] = "functionName"; | 32 const char kFunctionNameKey[] = "functionName"; |
| 24 const char kExecutionContextURLKey[] = "executionContextURL"; | 33 const char kExecutionContextURLKey[] = "executionContextURL"; |
| 25 const char kStackTraceKey[] = "stackTrace"; | 34 const char kStackTraceKey[] = "stackTrace"; |
| 26 | 35 |
| 27 // Try to retrieve an extension ID from a |url|. On success, returns true and | 36 // 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 | 37 // populates |extension_id| with the ID. On failure, returns false and leaves |
| 29 // extension_id untouched. | 38 // extension_id untouched. |
| 30 bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) { | 39 bool GetExtensionIdFromGURL(const GURL& url, std::string* extension_id) { |
| 31 if (url.SchemeIs(kExtensionScheme)) { | 40 if (url.SchemeIs(kExtensionScheme)) { |
| 32 *extension_id = url.host(); | 41 *extension_id = url.host(); |
| 33 return true; | 42 return true; |
| 34 } | 43 } |
| 35 return false; | 44 return false; |
| 36 } | 45 } |
| 37 | 46 |
| 38 } // namespace | 47 } // namespace |
| 39 | 48 |
| 40 ExtensionError::ExtensionError(Type type, | 49 ExtensionError::ExtensionError(Type type, |
| 41 const std::string& extension_id, | 50 const std::string& extension_id, |
| 42 bool from_incognito, | 51 bool from_incognito, |
| 43 const string16& source, | 52 const string16& source, |
| 44 const string16& message) | 53 const string16& message) |
| 45 : type_(type), | 54 : type_(type), |
| 46 extension_id_(extension_id), | 55 extension_id_(extension_id), |
| 47 from_incognito_(from_incognito), | 56 from_incognito_(from_incognito), |
| 48 source_(source), | 57 source_(source), |
| 49 message_(message) { | 58 message_(message) { |
| 50 } | 59 } |
| 51 | 60 |
| 52 ExtensionError::~ExtensionError() { | 61 ExtensionError::~ExtensionError() { |
| 53 } | 62 } |
| 54 | 63 |
| 64 scoped_ptr<DictionaryValue> ExtensionError::ToValue() const { |
| 65 scoped_ptr<DictionaryValue> value(new DictionaryValue); |
| 66 value->SetInteger(kTypeKey, static_cast<int>(type_)); |
| 67 value->SetString(kExtensionIdKey, extension_id_); |
| 68 value->SetBoolean(kFromIncognitoKey, from_incognito_); |
| 69 value->SetString(kSourceKey, source_); |
| 70 value->SetString(kMessageKey, message_); |
| 71 |
| 72 return value.Pass(); |
| 73 } |
| 74 |
| 55 std::string ExtensionError::PrintForTest() const { | 75 std::string ExtensionError::PrintForTest() const { |
| 56 return std::string("Extension Error:") + | 76 return std::string("Extension Error:") + |
| 57 "\n OTR: " + std::string(from_incognito_ ? "true" : "false") + | 77 "\n OTR: " + std::string(from_incognito_ ? "true" : "false") + |
| 58 "\n Source: " + base::UTF16ToUTF8(source_) + | 78 "\n Source: " + base::UTF16ToUTF8(source_) + |
| 59 "\n Message: " + base::UTF16ToUTF8(message_) + | 79 "\n Message: " + base::UTF16ToUTF8(message_) + |
| 60 "\n ID: " + extension_id_; | 80 "\n ID: " + extension_id_; |
| 61 } | 81 } |
| 62 | 82 |
| 83 bool ExtensionError::IsEqual(const ExtensionError* rhs) const { |
| 84 return type_ == rhs->type_ && |
| 85 extension_id_ == rhs->extension_id_ && |
| 86 source_ == rhs->source_ && |
| 87 message_ == rhs->message_ && |
| 88 IsEqualImpl(rhs); |
| 89 } |
| 90 |
| 63 ManifestParsingError::ManifestParsingError(const std::string& extension_id, | 91 ManifestParsingError::ManifestParsingError(const std::string& extension_id, |
| 64 const string16& message) | 92 const string16& message, |
| 93 const string16& manifest_key, |
| 94 const string16& manifest_specific) |
| 65 : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR, | 95 : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR, |
| 66 extension_id, | 96 extension_id, |
| 67 false, // extensions can't be installed while incognito. | 97 false, // extensions can't be installed while incognito. |
| 68 base::FilePath(kManifestFilename).AsUTF16Unsafe(), | 98 base::FilePath(kManifestFilename).AsUTF16Unsafe(), |
| 69 message) { | 99 message), |
| 100 manifest_key_(manifest_key), |
| 101 manifest_specific_(manifest_specific) { |
| 70 } | 102 } |
| 71 | 103 |
| 72 ManifestParsingError::~ManifestParsingError() { | 104 ManifestParsingError::~ManifestParsingError() { |
| 73 } | 105 } |
| 74 | 106 |
| 107 scoped_ptr<DictionaryValue> ManifestParsingError::ToValue() const { |
| 108 scoped_ptr<DictionaryValue> value = ExtensionError::ToValue(); |
| 109 // All manifest errors are warnings - if it was a fatal error, we wouldn't |
| 110 // get far enough to add an error for the extension. |
| 111 value->SetInteger(kLevelKey, logging::LOG_WARNING); |
| 112 if (!manifest_key_.empty()) |
| 113 value->SetString(kManifestKeyKey, manifest_key_); |
| 114 if (!manifest_specific_.empty()) |
| 115 value->SetString(kManifestSpecificKey, manifest_specific_); |
| 116 return value.Pass(); |
| 117 } |
| 118 |
| 75 std::string ManifestParsingError::PrintForTest() const { | 119 std::string ManifestParsingError::PrintForTest() const { |
| 76 return ExtensionError::PrintForTest() + | 120 return ExtensionError::PrintForTest() + |
| 77 "\n Type: ManifestParsingError"; | 121 "\n Type: ManifestParsingError"; |
| 78 } | 122 } |
| 79 | 123 |
| 124 bool ManifestParsingError::IsEqualImpl(const ExtensionError* rhs) const { |
| 125 // If two manifest errors have the same extension id and message (which are |
| 126 // both checked in ExtensionError::IsEqual), then they are equal. |
| 127 return true; |
| 128 } |
| 129 |
| 80 JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1), | 130 JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1), |
| 81 column_number(-1) { | 131 column_number(-1) { |
| 82 } | 132 } |
| 83 | 133 |
| 84 JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line, | 134 JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line, |
| 85 size_t frame_column, | 135 size_t frame_column, |
| 86 const string16& frame_url, | 136 const string16& frame_url, |
| 87 const string16& frame_function) | 137 const string16& frame_function) |
| 88 : line_number(frame_line), | 138 : line_number(frame_line), |
| 89 column_number(frame_column), | 139 column_number(frame_column), |
| 90 url(frame_url), | 140 url(frame_url), |
| 91 function(frame_function) { | 141 function(frame_function) { |
| 92 } | 142 } |
| 93 | 143 |
| 94 JavascriptRuntimeError::StackFrame::~StackFrame() { | 144 JavascriptRuntimeError::StackFrame::~StackFrame() { |
| 95 } | 145 } |
| 96 | 146 |
| 147 bool JavascriptRuntimeError::StackFrame::operator==( |
| 148 const JavascriptRuntimeError::StackFrame& rhs) const { |
| 149 return line_number == rhs.line_number && |
| 150 column_number == rhs.column_number && |
| 151 url == rhs.url && |
| 152 function == rhs.function; |
| 153 } |
| 97 JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito, | 154 JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito, |
| 98 const string16& source, | 155 const string16& source, |
| 99 const string16& message, | 156 const string16& message, |
| 100 logging::LogSeverity level, | 157 logging::LogSeverity level, |
| 101 const string16& details) | 158 const string16& details) |
| 102 : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR, | 159 : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR, |
| 103 std::string(), // We don't know the id yet. | 160 std::string(), // We don't know the id yet. |
| 104 from_incognito, | 161 from_incognito, |
| 105 source, | 162 source, |
| 106 message), | 163 message), |
| 107 level_(level) { | 164 level_(level) { |
| 108 ParseDetails(details); | 165 ParseDetails(details); |
| 109 DetermineExtensionID(); | 166 DetermineExtensionId(); |
| 110 } | 167 } |
| 111 | 168 |
| 112 JavascriptRuntimeError::~JavascriptRuntimeError() { | 169 JavascriptRuntimeError::~JavascriptRuntimeError() { |
| 113 } | 170 } |
| 114 | 171 |
| 115 std::string JavascriptRuntimeError::PrintForTest() const { | 172 std::string JavascriptRuntimeError::PrintForTest() const { |
| 116 std::string result = ExtensionError::PrintForTest() + | 173 std::string result = ExtensionError::PrintForTest() + |
| 117 "\n Type: JavascriptRuntimeError" | 174 "\n Type: JavascriptRuntimeError" |
| 118 "\n Context: " + base::UTF16ToUTF8(execution_context_url_) + | 175 "\n Context: " + base::UTF16ToUTF8(execution_context_url_) + |
| 119 "\n Stack Trace: "; | 176 "\n Stack Trace: "; |
| 120 for (StackTrace::const_iterator iter = stack_trace_.begin(); | 177 for (StackTrace::const_iterator iter = stack_trace_.begin(); |
| 121 iter != stack_trace_.end(); ++iter) { | 178 iter != stack_trace_.end(); ++iter) { |
| 122 result += "\n {" | 179 result += "\n {" |
| 123 "\n Line: " + base::IntToString(iter->line_number) + | 180 "\n Line: " + base::IntToString(iter->line_number) + |
| 124 "\n Column: " + base::IntToString(iter->column_number) + | 181 "\n Column: " + base::IntToString(iter->column_number) + |
| 125 "\n URL: " + base::UTF16ToUTF8(iter->url) + | 182 "\n URL: " + base::UTF16ToUTF8(iter->url) + |
| 126 "\n Function: " + base::UTF16ToUTF8(iter->function) + | 183 "\n Function: " + base::UTF16ToUTF8(iter->function) + |
| 127 "\n }"; | 184 "\n }"; |
| 128 } | 185 } |
| 129 return result; | 186 return result; |
| 130 } | 187 } |
| 131 | 188 |
| 189 bool JavascriptRuntimeError::IsEqualImpl(const ExtensionError* rhs) const { |
| 190 const JavascriptRuntimeError* error = |
| 191 static_cast<const JavascriptRuntimeError*>(rhs); |
| 192 |
| 193 // We don't look at the full stack trace, because if the first frame is |
| 194 // the same, it's close enough to heuristically count as a duplicate (after |
| 195 // all, the same line caused the error). |
| 196 // If the stack trace is empty, just compare the context. |
| 197 return execution_context_url_ == error->execution_context_url_ && |
| 198 stack_trace_.size() == error->stack_trace_.size() && |
| 199 (stack_trace_.empty() || stack_trace_[0] == error->stack_trace_[0]); |
| 200 } |
| 201 |
| 132 void JavascriptRuntimeError::ParseDetails(const string16& details) { | 202 void JavascriptRuntimeError::ParseDetails(const string16& details) { |
| 133 scoped_ptr<base::Value> value( | 203 scoped_ptr<base::Value> value( |
| 134 base::JSONReader::Read(base::UTF16ToUTF8(details))); | 204 base::JSONReader::Read(base::UTF16ToUTF8(details))); |
| 135 const base::DictionaryValue* details_value; | 205 const DictionaryValue* details_value; |
| 136 const base::ListValue* trace_value = NULL; | 206 const base::ListValue* trace_value = NULL; |
| 137 | 207 |
| 138 // The |details| value should contain an execution context url and a stack | 208 // The |details| value should contain an execution context url and a stack |
| 139 // trace. | 209 // trace. |
| 140 if (!value.get() || | 210 if (!value.get() || |
| 141 !value->GetAsDictionary(&details_value) || | 211 !value->GetAsDictionary(&details_value) || |
| 142 !details_value->GetString(kExecutionContextURLKey, | 212 !details_value->GetString(kExecutionContextURLKey, |
| 143 &execution_context_url_) || | 213 &execution_context_url_) || |
| 144 !details_value->GetList(kStackTraceKey, &trace_value)) { | 214 !details_value->GetList(kStackTraceKey, &trace_value)) { |
| 145 NOTREACHED(); | 215 NOTREACHED(); |
| 146 return; | 216 return; |
| 147 } | 217 } |
| 148 | 218 |
| 149 int line = 0; | 219 int line = 0; |
| 150 int column = 0; | 220 int column = 0; |
| 151 string16 url; | 221 string16 url; |
| 152 | 222 |
| 153 for (size_t i = 0; i < trace_value->GetSize(); ++i) { | 223 for (size_t i = 0; i < trace_value->GetSize(); ++i) { |
| 154 const base::DictionaryValue* frame_value = NULL; | 224 const DictionaryValue* frame_value = NULL; |
| 155 CHECK(trace_value->GetDictionary(i, &frame_value)); | 225 CHECK(trace_value->GetDictionary(i, &frame_value)); |
| 156 | 226 |
| 157 frame_value->GetInteger(kLineNumberKey, &line); | 227 frame_value->GetInteger(kLineNumberKey, &line); |
| 158 frame_value->GetInteger(kColumnNumberKey, &column); | 228 frame_value->GetInteger(kColumnNumberKey, &column); |
| 159 frame_value->GetString(kURLKey, &url); | 229 frame_value->GetString(kURLKey, &url); |
| 160 | 230 |
| 161 string16 function; | 231 string16 function; |
| 162 frame_value->GetString(kFunctionNameKey, &function); // This can be empty. | 232 frame_value->GetString(kFunctionNameKey, &function); // This can be empty. |
| 163 stack_trace_.push_back(StackFrame(line, column, url, function)); | 233 stack_trace_.push_back(StackFrame(line, column, url, function)); |
| 164 } | 234 } |
| 165 } | 235 } |
| 166 | 236 |
| 167 void JavascriptRuntimeError::DetermineExtensionID() { | 237 void JavascriptRuntimeError::DetermineExtensionId() { |
| 168 if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_)) | 238 if (!GetExtensionIdFromGURL(GURL(source_), &extension_id_)) |
| 169 GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_); | 239 GetExtensionIdFromGURL(GURL(execution_context_url_), &extension_id_); |
| 170 } | 240 } |
| 171 | 241 |
| 172 } // namespace extensions | 242 } // namespace extensions |
| OLD | NEW |