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

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

Powered by Google App Engine
This is Rietveld 408576698