This source file includes following definitions.
- GetStackTraceFromError
- CheckStackFrame
- CheckStackFrame
- CheckError
- CheckRuntimeError
- CheckManifestError
- error_console_
- OnErrorAdded
- OnErrorConsoleDestroyed
- WaitForErrors
- SetUpInProcessBrowserTestFixture
- SetUpOnMainThread
- GetTestURL
- LoadExtensionAndCheckErrors
- error_console
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
#include "chrome/browser/extensions/error_console/error_console.h"
#include "base/files/file_path.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/ui_test_utils.h"
#include "extensions/browser/extension_error.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_constants.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using base::string16;
using base::UTF8ToUTF16;
namespace extensions {
namespace {
const char kTestingPage[] = "/extensions/test_file.html";
const char kAnonymousFunction[] = "(anonymous function)";
const char* kBackgroundPageName =
extensions::kGeneratedBackgroundPageFilename;
const int kNoFlags = 0;
const StackTrace& GetStackTraceFromError(const ExtensionError* error) {
CHECK(error->type() == ExtensionError::RUNTIME_ERROR);
return (static_cast<const RuntimeError*>(error))->stack_trace();
}
void CheckStackFrame(const StackFrame& frame,
const std::string& source,
const std::string& function) {
EXPECT_EQ(base::UTF8ToUTF16(source), frame.source);
EXPECT_EQ(base::UTF8ToUTF16(function), frame.function);
}
void CheckStackFrame(const StackFrame& frame,
const std::string& source,
const std::string& function,
size_t line_number,
size_t column_number) {
CheckStackFrame(frame, source, function);
EXPECT_EQ(line_number, frame.line_number);
EXPECT_EQ(column_number, frame.column_number);
}
void CheckError(const ExtensionError* error,
ExtensionError::Type type,
const std::string& id,
const std::string& source,
bool from_incognito,
const std::string& message) {
ASSERT_TRUE(error);
EXPECT_EQ(type, error->type());
EXPECT_EQ(id, error->extension_id());
EXPECT_EQ(base::UTF8ToUTF16(source), error->source());
EXPECT_EQ(from_incognito, error->from_incognito());
EXPECT_EQ(base::UTF8ToUTF16(message), error->message());
}
void CheckRuntimeError(const ExtensionError* error,
const std::string& id,
const std::string& source,
bool from_incognito,
const std::string& message,
logging::LogSeverity level,
const GURL& context,
size_t expected_stack_size) {
CheckError(error,
ExtensionError::RUNTIME_ERROR,
id,
source,
from_incognito,
message);
const RuntimeError* runtime_error = static_cast<const RuntimeError*>(error);
EXPECT_EQ(level, runtime_error->level());
EXPECT_EQ(context, runtime_error->context_url());
EXPECT_EQ(expected_stack_size, runtime_error->stack_trace().size());
}
void CheckManifestError(const ExtensionError* error,
const std::string& id,
const std::string& message,
const std::string& manifest_key,
const std::string& manifest_specific) {
CheckError(error,
ExtensionError::MANIFEST_ERROR,
id,
base::FilePath(kManifestFilename).AsUTF8Unsafe(),
false,
message);
const ManifestError* manifest_error =
static_cast<const ManifestError*>(error);
EXPECT_EQ(base::UTF8ToUTF16(manifest_key), manifest_error->manifest_key());
EXPECT_EQ(base::UTF8ToUTF16(manifest_specific),
manifest_error->manifest_specific());
}
}
class ErrorConsoleBrowserTest : public ExtensionBrowserTest {
public:
ErrorConsoleBrowserTest() : error_console_(NULL) { }
virtual ~ErrorConsoleBrowserTest() { }
protected:
class ErrorObserver : public ErrorConsole::Observer {
public:
ErrorObserver(size_t errors_expected, ErrorConsole* error_console)
: errors_observed_(0),
errors_expected_(errors_expected),
waiting_(false),
error_console_(error_console) {
error_console_->AddObserver(this);
}
virtual ~ErrorObserver() {
if (error_console_)
error_console_->RemoveObserver(this);
}
virtual void OnErrorAdded(const ExtensionError* error) OVERRIDE {
++errors_observed_;
if (errors_observed_ >= errors_expected_) {
if (waiting_)
base::MessageLoopForUI::current()->Quit();
}
}
virtual void OnErrorConsoleDestroyed() OVERRIDE {
error_console_ = NULL;
}
void WaitForErrors() {
if (errors_observed_ < errors_expected_) {
waiting_ = true;
content::RunMessageLoop();
waiting_ = false;
}
}
private:
size_t errors_observed_;
size_t errors_expected_;
bool waiting_;
ErrorConsole* error_console_;
DISALLOW_COPY_AND_ASSIGN(ErrorObserver);
};
enum Action {
ACTION_NAVIGATE,
ACTION_BROWSER_ACTION,
ACTION_NEW_TAB,
ACTION_NONE
};
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
FeatureSwitch::error_console()->SetOverrideValue(
FeatureSwitch::OVERRIDE_ENABLED);
}
virtual void SetUpOnMainThread() OVERRIDE {
ExtensionBrowserTest::SetUpOnMainThread();
profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
error_console_ = ErrorConsole::Get(profile());
CHECK(error_console_);
test_data_dir_ = test_data_dir_.AppendASCII("error_console");
}
const GURL& GetTestURL() {
if (test_url_.is_empty()) {
CHECK(embedded_test_server()->InitializeAndWaitUntilReady());
test_url_ = embedded_test_server()->GetURL(kTestingPage);
}
return test_url_;
}
void LoadExtensionAndCheckErrors(
const std::string& path,
int flags,
size_t errors_expected,
Action action,
const Extension** extension) {
ErrorObserver observer(errors_expected, error_console_);
*extension =
LoadExtensionWithFlags(test_data_dir_.AppendASCII(path), flags);
ASSERT_TRUE(*extension);
switch (action) {
case ACTION_NAVIGATE: {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
break;
}
case ACTION_BROWSER_ACTION: {
ExtensionToolbarModel::Get(profile())->ExecuteBrowserAction(
*extension, browser(), NULL, true);
break;
}
case ACTION_NEW_TAB: {
ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUINewTabURL));
break;
}
case ACTION_NONE:
break;
default:
NOTREACHED();
}
observer.WaitForErrors();
ASSERT_EQ(errors_expected > 0 ? 1u : 0u,
error_console()->get_num_entries_for_test());
ASSERT_EQ(
errors_expected,
error_console()->GetErrorsForExtension((*extension)->id()).size());
}
ErrorConsole* error_console() { return error_console_; }
private:
GURL test_url_;
ErrorConsole* error_console_;
};
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, ReportManifestErrors) {
const Extension* extension = NULL;
LoadExtensionAndCheckErrors("manifest_warnings",
ExtensionBrowserTest::kFlagIgnoreManifestWarnings,
2,
ACTION_NONE,
&extension);
const ErrorList& errors =
error_console()->GetErrorsForExtension(extension->id());
const ExtensionError* permissions_error = NULL;
const ExtensionError* unknown_key_error = NULL;
const char kFakeKey[] = "not_a_real_key";
for (size_t i = 0; i < errors.size(); ++i) {
ASSERT_EQ(ExtensionError::MANIFEST_ERROR, errors[i]->type());
std::string utf8_key = base::UTF16ToUTF8(
(static_cast<const ManifestError*>(errors[i]))->manifest_key());
if (utf8_key == manifest_keys::kPermissions)
permissions_error = errors[i];
else if (utf8_key == kFakeKey)
unknown_key_error = errors[i];
}
ASSERT_TRUE(permissions_error);
ASSERT_TRUE(unknown_key_error);
const char kFakePermission[] = "not_a_real_permission";
CheckManifestError(permissions_error,
extension->id(),
ErrorUtils::FormatErrorMessage(
manifest_errors::kPermissionUnknownOrMalformed,
kFakePermission),
manifest_keys::kPermissions,
kFakePermission);
CheckManifestError(unknown_key_error,
extension->id(),
ErrorUtils::FormatErrorMessage(
manifest_errors::kUnrecognizedManifestKey,
kFakeKey),
kFakeKey,
std::string());
}
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest,
DontStoreErrorsWithoutDeveloperMode) {
profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
const Extension* extension = NULL;
LoadExtensionAndCheckErrors("manifest_warnings",
ExtensionBrowserTest::kFlagIgnoreManifestWarnings,
0,
ACTION_NONE,
&extension);
profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
EXPECT_EQ(2u, error_console()->GetErrorsForExtension(extension->id()).size());
profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
EXPECT_EQ(0u, error_console()->GetErrorsForExtension(extension->id()).size());
}
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest,
DISABLED_ContentScriptLogAndRuntimeError) {
const Extension* extension = NULL;
LoadExtensionAndCheckErrors(
"content_script_log_and_runtime_error",
kNoFlags,
2u,
ACTION_NAVIGATE,
&extension);
std::string script_url = extension->url().Resolve("content_script.js").spec();
const ErrorList& errors =
error_console()->GetErrorsForExtension(extension->id());
CheckRuntimeError(errors[0],
extension->id(),
script_url,
false,
"Hello, World!",
logging::LOG_INFO,
GetTestURL(),
2u);
const StackTrace& stack_trace1 = GetStackTraceFromError(errors[0]);
CheckStackFrame(stack_trace1[0],
script_url,
"logHelloWorld",
6u,
11u );
CheckStackFrame(stack_trace1[1],
script_url,
kAnonymousFunction,
9u,
1u);
CheckRuntimeError(errors[1],
extension->id(),
script_url,
false,
"Uncaught TypeError: "
"Cannot set property 'foo' of undefined",
logging::LOG_ERROR,
GetTestURL(),
1u);
const StackTrace& stack_trace2 = GetStackTraceFromError(errors[1]);
CheckStackFrame(stack_trace2[0],
script_url,
kAnonymousFunction,
12u,
1u);
}
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, BrowserActionRuntimeError) {
const Extension* extension = NULL;
LoadExtensionAndCheckErrors(
"browser_action_runtime_error",
kNoFlags,
1u,
ACTION_BROWSER_ACTION,
&extension);
std::string script_url = extension->url().Resolve("browser_action.js").spec();
const ErrorList& errors =
error_console()->GetErrorsForExtension(extension->id());
std::string event_bindings_str =
base::StringPrintf("extensions::%s", kEventBindings);
std::string event_dispatch_to_listener_str =
base::StringPrintf("Event.publicClass.%s [as dispatchToListener]",
kAnonymousFunction);
CheckRuntimeError(
errors[0],
extension->id(),
script_url,
false,
"Error in event handler for browserAction.onClicked: baz is not defined\n"
"Stack trace: ReferenceError: baz is not defined",
logging::LOG_ERROR,
extension->url().Resolve(kBackgroundPageName),
8u);
const StackTrace& stack_trace = GetStackTraceFromError(errors[0]);
CheckStackFrame(stack_trace[0], script_url, kAnonymousFunction);
CheckStackFrame(stack_trace[1],
"extensions::SafeBuiltins",
std::string("Function.target.") + kAnonymousFunction);
CheckStackFrame(
stack_trace[2], event_bindings_str, "EventImpl.dispatchToListener");
CheckStackFrame(stack_trace[3],
"extensions::SafeBuiltins",
std::string("Function.target.") + kAnonymousFunction);
CheckStackFrame(stack_trace[4], "extensions::utils",
event_dispatch_to_listener_str);
CheckStackFrame(stack_trace[5], event_bindings_str, "EventImpl.dispatch_");
CheckStackFrame(stack_trace[6], event_bindings_str, "dispatchArgs");
CheckStackFrame(stack_trace[7], event_bindings_str, "dispatchEvent");
}
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, BadAPIArgumentsRuntimeError) {
const Extension* extension = NULL;
LoadExtensionAndCheckErrors(
"bad_api_arguments_runtime_error",
kNoFlags,
1,
ACTION_NONE,
&extension);
const ErrorList& errors =
error_console()->GetErrorsForExtension(extension->id());
std::string schema_utils_str =
base::StringPrintf("extensions::%s", kSchemaUtils);
CheckRuntimeError(
errors[0],
extension->id(),
schema_utils_str,
false,
"Uncaught Error: Invocation of form "
"tabs.get(string, function) doesn't match definition "
"tabs.get(integer tabId, function callback)",
logging::LOG_ERROR,
extension->url().Resolve(kBackgroundPageName),
1u);
const StackTrace& stack_trace = GetStackTraceFromError(errors[0]);
ASSERT_EQ(1u, stack_trace.size());
CheckStackFrame(stack_trace[0],
schema_utils_str,
kAnonymousFunction);
}
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, BadAPIPermissionsRuntimeError) {
const Extension* extension = NULL;
LoadExtensionAndCheckErrors(
"bad_api_permissions_runtime_error",
kNoFlags,
1,
ACTION_NONE,
&extension);
std::string script_url = extension->url().Resolve("background.js").spec();
const ErrorList& errors =
error_console()->GetErrorsForExtension(extension->id());
CheckRuntimeError(
errors[0],
extension->id(),
script_url,
false,
"Uncaught TypeError: Cannot read property 'addUrl' of undefined",
logging::LOG_ERROR,
extension->url().Resolve(kBackgroundPageName),
1u);
const StackTrace& stack_trace = GetStackTraceFromError(errors[0]);
ASSERT_EQ(1u, stack_trace.size());
CheckStackFrame(stack_trace[0],
script_url,
kAnonymousFunction,
5u, 1u);
}
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, BadExtensionPage) {
const Extension* extension = NULL;
LoadExtensionAndCheckErrors(
"bad_extension_page",
kNoFlags,
1,
ACTION_NEW_TAB,
&extension);
}
}