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

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, 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
« no previous file with comments | « no previous file | chrome/browser/extensions/error_console/error_console_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/prefs/pref_service.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/extensions/extension_browsertest.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_system.h"
13 #include "chrome/browser/extensions/extension_toolbar_model.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/extensions/extension.h"
16 #include "chrome/common/extensions/feature_switch.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/ui_test_utils.h"
19 #include "extensions/browser/extension_error.h"
20 #include "extensions/common/constants.h"
21 #include "net/test/embedded_test_server/embedded_test_server.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "url/gurl.h"
24
25 using base::string16;
26 using base::UTF8ToUTF16;
27
28 namespace extensions {
29
30 namespace {
31
32 const char kTestingPage[] = "/extensions/test_file.html";
33 const char kAnonymousFunction[] = "(anonymous function)";
34 const char* kBackgroundPageName =
35 extensions::kGeneratedBackgroundPageFilename;
36 const int kNoFlags = 0;
37
38 const StackTrace& GetStackTraceFromError(const ExtensionError* error) {
39 CHECK(error->type() == ExtensionError::RUNTIME_ERROR);
40 return (static_cast<const RuntimeError*>(error))->stack_trace();
41 }
42
43 // Verify that a given |frame| has the proper source and function name.
44 void CheckStackFrame(const StackFrame& frame,
45 const std::string& source,
46 const std::string& function) {
47 EXPECT_EQ(UTF8ToUTF16(source), frame.source);
48 EXPECT_EQ(UTF8ToUTF16(function), frame.function);
49 }
50
51 // Verify that all properties of a given |frame| are correct. Overloaded because
52 // we commonly do not check line/column numbers, as they are too likely
53 // to change.
54 void CheckStackFrame(const StackFrame& frame,
55 const std::string& source,
56 const std::string& function,
57 size_t line_number,
58 size_t column_number) {
59 CheckStackFrame(frame, source, function);
60 EXPECT_EQ(line_number, frame.line_number);
61 EXPECT_EQ(column_number, frame.column_number);
62 }
63
64 // Verify that all properties of a given |error| are correct.
65 void CheckError(const ExtensionError* error,
66 ExtensionError::Type type,
67 const std::string& id,
68 const std::string& source,
69 bool from_incognito,
70 const std::string& message) {
71 ASSERT_TRUE(error);
72 EXPECT_EQ(type, error->type());
73 EXPECT_EQ(id, error->extension_id());
74 EXPECT_EQ(UTF8ToUTF16(source), error->source());
75 EXPECT_EQ(from_incognito, error->from_incognito());
76 EXPECT_EQ(UTF8ToUTF16(message), error->message());
77 }
78
79 // Verify that all properties of a JS runtime error are correct.
80 void CheckRuntimeError(const ExtensionError* error,
81 const std::string& id,
82 const std::string& source,
83 bool from_incognito,
84 const std::string& message,
85 logging::LogSeverity level,
86 const GURL& context,
87 size_t expected_stack_size) {
88 CheckError(error,
89 ExtensionError::RUNTIME_ERROR,
90 id,
91 source,
92 from_incognito,
93 message);
94
95 const RuntimeError* runtime_error = static_cast<const RuntimeError*>(error);
96 EXPECT_EQ(level, runtime_error->level());
97 EXPECT_EQ(context, runtime_error->context_url());
98 EXPECT_EQ(expected_stack_size, runtime_error->stack_trace().size());
99 }
100
101 } // namespace
102
103 class ErrorConsoleBrowserTest : public ExtensionBrowserTest {
104 public:
105 ErrorConsoleBrowserTest() : error_console_(NULL) { }
106 virtual ~ErrorConsoleBrowserTest() { }
107
108 protected:
109 // A helper class in order to wait for the proper number of errors to be
110 // caught by the ErrorConsole. This will run the MessageLoop until a given
111 // number of errors are observed.
112 // Usage:
113 // ...
114 // ErrorObserver observer(3, error_console);
115 // <Cause three errors...>
116 // observer.WaitForErrors();
117 // <Perform any additional checks...>
118 class ErrorObserver : public ErrorConsole::Observer {
119 public:
120 ErrorObserver(size_t errors_expected, ErrorConsole* error_console)
121 : errors_observed_(0),
122 errors_expected_(errors_expected),
123 waiting_(false),
124 error_console_(error_console) {
125 error_console_->AddObserver(this);
126 }
127 virtual ~ErrorObserver() {
128 if (error_console_)
129 error_console_->RemoveObserver(this);
130 }
131
132 // ErrorConsole::Observer implementation.
133 virtual void OnErrorAdded(const ExtensionError* error) OVERRIDE {
134 ++errors_observed_;
135 if (errors_observed_ >= errors_expected_) {
136 if (waiting_)
137 base::MessageLoopForUI::current()->Quit();
138 }
139 }
140
141 virtual void OnErrorConsoleDestroyed() OVERRIDE {
142 error_console_ = NULL;
143 }
144
145 // Spin until the appropriate number of errors have been observed.
146 void WaitForErrors() {
147 if (errors_observed_ < errors_expected_) {
148 waiting_ = true;
149 content::RunMessageLoop();
150 waiting_ = false;
151 }
152 }
153
154 private:
155 size_t errors_observed_;
156 size_t errors_expected_;
157 bool waiting_;
158
159 ErrorConsole* error_console_;
160
161 DISALLOW_COPY_AND_ASSIGN(ErrorObserver);
162 };
163
164 // The type of action which we take after we load an extension in order to
165 // cause any errors.
166 enum Action {
167 ACTION_NAVIGATE, // navigate to a page to allow a content script to run.
168 ACTION_BROWSER_ACTION, // simulate a browser action click.
169 ACTION_NONE // Do nothing (errors will be caused by a background script,
170 // or by a manifest/loading warning).
171 };
172
173 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
174 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
175
176 // We need to enable the ErrorConsole FeatureSwitch in order to collect
177 // errors.
178 FeatureSwitch::error_console()->SetOverrideValue(
179 FeatureSwitch::OVERRIDE_ENABLED);
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 std::string script_url = extension->url().Resolve("content_script.js").spec();
266
267 const ErrorConsole::ErrorList& errors =
268 error_console()->GetErrorsForExtension(extension->id());
269
270 // The first error should be a console log.
271 CheckRuntimeError(errors[0],
272 extension->id(),
273 script_url, // The source should be the content script url.
274 false, // Not from incognito.
275 "Hello, World!", // The error message is the log.
276 logging::LOG_INFO,
277 GetTestURL(), // Content scripts run in the web page.
278 2u);
279
280 const StackTrace& stack_trace1 = 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 StackTrace& stack_trace2 = GetStackTraceFromError(errors[1]);
305 CheckStackFrame(stack_trace2[0],
306 script_url,
307 kAnonymousFunction,
308 12u,
309 1u);
310 }
311
312 // Catch an error from a BrowserAction; this is more complex than a content
313 // script error, since browser actions are routed through our own code.
314 IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, BrowserActionRuntimeError) {
315 const Extension* extension = NULL;
316 LoadExtensionAndCheckErrors(
317 "browser_action_runtime_error",
318 kNoFlags,
319 1u, // One error: A reference error from within the browser action.
320 ACTION_BROWSER_ACTION,
321 &extension);
322
323 std::string event_bindings_str = "event_bindings";
324 std::string script_url = extension->url().Resolve("browser_action.js").spec();
325
326 const ErrorConsole::ErrorList& errors =
327 error_console()->GetErrorsForExtension(extension->id());
328
329 CheckRuntimeError(
330 errors[0],
331 extension->id(),
332 script_url,
333 false, // not incognito
334 "Error in event handler for browserAction.onClicked: "
335 "ReferenceError: baz is not defined",
336 logging::LOG_ERROR,
337 extension->url().Resolve(kBackgroundPageName),
338 6u);
339
340 const StackTrace& stack_trace = GetStackTraceFromError(errors[0]);
341
342 CheckStackFrame(stack_trace[0], script_url, kAnonymousFunction);
343 CheckStackFrame(stack_trace[1],
344 "extensions::SafeBuiltins",
345 std::string("Function.target.") + kAnonymousFunction);
346 CheckStackFrame(
347 stack_trace[2], event_bindings_str, "Event.dispatchToListener");
348 CheckStackFrame(stack_trace[3], event_bindings_str, "Event.dispatch_");
349 CheckStackFrame(stack_trace[4], event_bindings_str, "dispatchArgs");
350 CheckStackFrame(stack_trace[5], event_bindings_str, "dispatchEvent");
351 }
352
353 } // namespace extensions
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/extensions/error_console/error_console_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698