Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(204)

Side by Side Diff: chrome/browser/extensions/error_console/error_console_browsertest.cc

Issue 23007021: Report Javascript Runtime Errors to the Error Console (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@dc_ec_feldman
Patch Set: Joi's Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "chrome/browser/extensions/error_console/error_console.h"
6
7 #include "base/files/file_path.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/extensions/extension_browsertest.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_system.h"
14 #include "chrome/browser/extensions/extension_toolbar_model.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/extensions/extension.h"
17 #include "chrome/common/extensions/extension_manifest_constants.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "extensions/browser/extension_error.h"
22 #include "extensions/common/constants.h"
23 #include "extensions/common/error_utils.h"
24 #include "extensions/common/manifest_constants.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "url/gurl.h"
28
29 using base::string16;
30 using base::UTF8ToUTF16;
31
32 namespace extensions {
33
34 namespace {
35
36 namespace errors = extension_manifest_errors;
37
38 const char kTestingPage[] = "/extensions/test_file.html";
39 const char kAnonymousFunction[] = "(anonymous function)";
40 const char* kBackgroundPageName =
41 extensions::kGeneratedBackgroundPageFilename;
42 const int kNoFlags = 0;
43
44 const JavascriptRuntimeError::StackTrace& GetStackTraceFromError(
45 const ExtensionError* error) {
46 CHECK(error->type() == ExtensionError::JAVASCRIPT_RUNTIME_ERROR);
47 return (static_cast<const JavascriptRuntimeError*>(error))->stack_trace();
48 }
49
50 // Verify that a given |frame| has the proper url/source and function name.
51 void CheckStackFrame(const JavascriptRuntimeError::StackFrame* frame,
52 const std::string& url,
53 const std::string& function) {
54 EXPECT_EQ(UTF8ToUTF16(url), frame->url);
55 EXPECT_EQ(UTF8ToUTF16(function), frame->function);
56 }
57
58 // Verify that all properties of a given |frame| are correct. Overloaded because
59 // we commonly do not check line/column numbers, as they are too likely
60 // to change.
61 void CheckStackFrame(const JavascriptRuntimeError::StackFrame* frame,
62 const std::string& url,
63 const std::string& function,
64 size_t line_number,
65 size_t column_number) {
66 CheckStackFrame(frame, url, function);
67 EXPECT_EQ(line_number, frame->line_number);
68 EXPECT_EQ(column_number, frame->column_number);
69 }
70
71 // Verify that all properties of a given |error| are correct.
72 void CheckError(const ExtensionError* error,
73 ExtensionError::Type type,
74 const std::string& id,
75 const std::string& source,
76 bool from_incognito,
77 const std::string& message) {
78 ASSERT_TRUE(error);
79 EXPECT_EQ(type, error->type());
80 EXPECT_EQ(id, error->extension_id());
81 EXPECT_EQ(UTF8ToUTF16(source), error->source());
82 EXPECT_EQ(from_incognito, error->from_incognito());
83 EXPECT_EQ(UTF8ToUTF16(message), error->message());
84 }
85
86 // Verify that all properties of a JS runtime error are correct.
87 void CheckRuntimeError(const ExtensionError* error,
88 const std::string& id,
89 const std::string& source,
90 bool from_incognito,
91 const std::string& message,
92 logging::LogSeverity level,
93 const GURL& context,
94 size_t expected_stack_size) {
95 CheckError(error,
96 ExtensionError::JAVASCRIPT_RUNTIME_ERROR,
97 id,
98 source,
99 from_incognito,
100 message);
101
102 const JavascriptRuntimeError* runtime_error =
103 static_cast<const JavascriptRuntimeError*>(error);
104 EXPECT_EQ(level, runtime_error->level());
105 EXPECT_EQ(context, runtime_error->context_url());
106 EXPECT_EQ(expected_stack_size, runtime_error->stack_trace().size());
107 }
108
109 } // namespace
110
111 class ErrorConsoleBrowserTest : public ExtensionBrowserTest {
112 public:
113 ErrorConsoleBrowserTest() : error_console_(NULL) { }
114 virtual ~ErrorConsoleBrowserTest() { }
115
116 protected:
117 // A helper class in order to wait for the proper number of errors to be
118 // caught by the ErrorConsole. This will run the MessageLoop until a given
119 // number of errors are observed.
120 // Usage:
121 // ...
122 // ErrorObserver observer(3, error_console);
123 // <Cause three errors...>
124 // observer.WaitForErrors();
125 // <Perform any additional checks...>
126 class ErrorObserver : public ErrorConsole::Observer {
127 public:
128 ErrorObserver(size_t errors_expected, ErrorConsole* error_console)
129 : errors_observed_(0),
130 errors_expected_(errors_expected),
131 waiting_(false),
132 error_console_(error_console) {
133 error_console_->AddObserver(this);
134 }
135 virtual ~ErrorObserver() {
136 if (error_console_)
137 error_console_->RemoveObserver(this);
138 }
139
140 // ErrorConsole::Observer implementation.
141 virtual void OnErrorAdded(const ExtensionError* error) OVERRIDE {
142 ++errors_observed_;
143 if (errors_observed_ >= errors_expected_) {
144 if (waiting_)
145 base::MessageLoopForUI::current()->Quit();
146 }
147 }
148
149 virtual void OnErrorConsoleDestroyed() OVERRIDE {
150 error_console_ = NULL;
151 }
152
153 // Spin until the appropriate number of errors have been observed.
154 void WaitForErrors() {
155 if (errors_observed_ < errors_expected_) {
156 waiting_ = true;
157 content::RunMessageLoop();
158 waiting_ = false;
159 }
160 }
161
162 private:
163 size_t errors_observed_;
164 size_t errors_expected_;
165 bool waiting_;
166
167 ErrorConsole* error_console_;
168
169 DISALLOW_COPY_AND_ASSIGN(ErrorObserver);
170 };
171
172 // The type of action which we take after we load an extension in order to
173 // cause any errors.
174 enum Action {
175 ACTION_NAVIGATE, // navigate to a page to allow a content script to run.
176 ACTION_BROWSER_ACTION, // simulate a browser action click.
177 ACTION_NONE // Do nothing (errors will be caused by a background script,
178 // or by a manifest/loading warning).
179 };
180
181 virtual void SetUpOnMainThread() OVERRIDE {
182 ExtensionBrowserTest::SetUpOnMainThread();
183
184 // Errors are only kept if we have Developer Mode enabled.
185 profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
186
187 error_console_ = ErrorConsole::Get(profile());
188 CHECK(error_console_);
189
190 test_data_dir_ = test_data_dir_.AppendASCII("error_console");
191 }
192
193 const GURL& GetTestURL() {
194 if (test_url_.is_empty()) {
195 CHECK(embedded_test_server()->InitializeAndWaitUntilReady());
196 test_url_ = embedded_test_server()->GetURL(kTestingPage);
197 }
198 return test_url_;
199 }
200
201 // Load the extension at |path|, take the specified |action|, and wait for
202 // |expected_errors| errors. Populate |extension| with a pointer to the loaded
203 // extension.
204 void LoadExtensionAndCheckErrors(
205 const std::string& path,
206 int flags,
207 size_t errors_expected,
208 Action action,
209 const Extension** extension) {
210 ErrorObserver observer(errors_expected, error_console_);
211 *extension =
212 LoadExtensionWithFlags(test_data_dir_.AppendASCII(path), flags);
213 ASSERT_TRUE(*extension);
214
215 switch (action) {
216 case ACTION_NAVIGATE: {
217 ui_test_utils::NavigateToURL(browser(), GetTestURL());
218 break;
219 }
220 case ACTION_BROWSER_ACTION: {
221 ExtensionService* service =
222 extensions::ExtensionSystem::Get(profile())->extension_service();
223 service->toolbar_model()->ExecuteBrowserAction(
224 *extension, browser(), NULL);
225 break;
226 }
227 case ACTION_NONE:
228 break;
229 default:
230 NOTREACHED();
231 }
232
233 observer.WaitForErrors();
234
235 // We should only have errors for a single extension, or should have no
236 // entries, if no errors were expected.
237 ASSERT_EQ(errors_expected > 0 ? 1u : 0u, error_console()->errors().size());
238 ASSERT_EQ(
239 errors_expected,
240 error_console()->GetErrorsForExtension((*extension)->id()).size());
241 }
242
243 ErrorConsole* error_console() { return error_console_; }
244 private:
245 // The URL used in testing for simple page navigations.
246 GURL test_url_;
247
248 // Weak reference to the ErrorConsole.
249 ErrorConsole* error_console_;
250 };
251
252 // Load an extension which, upon visiting any page, first sends out a console
253 // log, and then crashes with a JS TypeError.
254 IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest,
255 ContentScriptLogAndRuntimeError) {
256 const Extension* extension = NULL;
257 LoadExtensionAndCheckErrors(
258 "content_script_log_and_runtime_error",
259 kNoFlags,
260 2u, // Two errors: A log message and a JS type error.
261 ACTION_NAVIGATE,
262 &extension);
263
264 std::string script_url = extension->url().Resolve("content_script.js").spec();
265
266 const ErrorConsole::ErrorList& errors =
267 error_console()->GetErrorsForExtension(extension->id());
268
269 // The first error should be a console log.
270 CheckRuntimeError(errors[0],
271 extension->id(),
272 script_url, // The source should be the content script url.
273 false, // Not from incognito.
274 "Hello, World!", // The error message is the log.
275 logging::LOG_INFO,
276 GetTestURL(), // Content scripts run in the web page.
277 2u);
278
279 const JavascriptRuntimeError::StackTrace& stack_trace1 =
280 GetStackTraceFromError(errors[0]);
281 CheckStackFrame(stack_trace1[0],
282 script_url,
283 "logHelloWorld", // function name
284 6u, // line number
285 11u /* column number */ );
286
287 CheckStackFrame(stack_trace1[1],
288 script_url,
289 kAnonymousFunction,
290 9u,
291 1u);
292
293 // The second error should be a runtime error.
294 CheckRuntimeError(errors[1],
295 extension->id(),
296 script_url,
297 false, // not from incognito
298 "Uncaught TypeError: "
299 "Cannot set property 'foo' of undefined",
300 logging::LOG_ERROR, // JS errors are always ERROR level.
301 GetTestURL(),
302 1u);
303
304 const JavascriptRuntimeError::StackTrace& stack_trace2 =
305 GetStackTraceFromError(errors[1]);
306 CheckStackFrame(stack_trace2[0],
307 script_url,
308 kAnonymousFunction,
309 12u,
310 1u);
311 }
312
313 // Catch an error from a BrowserAction; this is more complex than a content
314 // script error, since browser actions are routed through our own code.
315 IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, BrowserActionRuntimeError) {
316 const Extension* extension = NULL;
317 LoadExtensionAndCheckErrors(
318 "browser_action_runtime_error",
319 kNoFlags,
320 1u, // One error: A reference error from within the browser action.
321 ACTION_BROWSER_ACTION,
322 &extension);
323
324 std::string event_bindings_str = "event_bindings";
325 std::string script_url = extension->url().Resolve("browser_action.js").spec();
326
327 const ErrorConsole::ErrorList& errors =
328 error_console()->GetErrorsForExtension(extension->id());
329
330 CheckRuntimeError(
331 errors[0],
332 extension->id(),
333 script_url,
334 false, // not incognito
335 "Error in event handler for browserAction.onClicked: "
336 "ReferenceError: baz is not defined",
337 logging::LOG_ERROR,
338 extension->url().Resolve(kBackgroundPageName),
339 6u);
340
341 const JavascriptRuntimeError::StackTrace& stack_trace =
342 GetStackTraceFromError(errors[0]);
343
344 CheckStackFrame(stack_trace[0], script_url, kAnonymousFunction);
345 CheckStackFrame(stack_trace[1],
346 "extensions::SafeBuiltins",
347 std::string("Function.target.") + kAnonymousFunction);
348 CheckStackFrame(
349 stack_trace[2], event_bindings_str, "Event.dispatchToListener");
350 CheckStackFrame(stack_trace[3], event_bindings_str, "Event.dispatch_");
351 CheckStackFrame(stack_trace[4], event_bindings_str, "dispatchArgs");
352 CheckStackFrame(stack_trace[5], event_bindings_str, "dispatchEvent");
353 }
354
355 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698