Commit dac897b6 authored by Darren Shen's avatar Darren Shen Committed by Commit Bot

crash: Add private API for reporting JavaScript errors.

We add a new private API for Chrome OS component extensions to report
JavaScript errors to Crash. The API mirrors the window.onerror API.

Design doc:
https://docs.google.com/document/d/1XqN_wO1_UfVRTfhDf6yzHCyBwq_TcSw8ILDOOY_h3w4/edit?usp=sharing

Bug: 986178
Change-Id: I8e11553fd7d1c07c712c7a87a68f1c0ccace54f1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1732329
Commit-Queue: Darren Shen <shend@chromium.org>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarBruce Dawson <brucedawson@chromium.org>
Reviewed-by: default avatarChris Palmer <palmer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#719493}
parent b6edee88
...@@ -840,6 +840,7 @@ TEST(PermissionsTest, PermissionMessages) { ...@@ -840,6 +840,7 @@ TEST(PermissionsTest, PermissionMessages) {
skip.insert(APIPermission::kChromeosInfoPrivate); skip.insert(APIPermission::kChromeosInfoPrivate);
skip.insert(APIPermission::kCloudPrintPrivate); skip.insert(APIPermission::kCloudPrintPrivate);
skip.insert(APIPermission::kCommandLinePrivate); skip.insert(APIPermission::kCommandLinePrivate);
skip.insert(APIPermission::kCrashReportPrivate);
skip.insert(APIPermission::kDeveloperPrivate); skip.insert(APIPermission::kDeveloperPrivate);
skip.insert(APIPermission::kDownloadsInternal); skip.insert(APIPermission::kDownloadsInternal);
skip.insert(APIPermission::kEchoPrivate); skip.insert(APIPermission::kEchoPrivate);
......
...@@ -41,6 +41,8 @@ source_set("crashpad_handler_main") { ...@@ -41,6 +41,8 @@ source_set("crashpad_handler_main") {
static_library("app") { static_library("app") {
sources = [ sources = [
"client_upload_info.cc",
"client_upload_info.h",
"crash_export_thunks.h", "crash_export_thunks.h",
"crash_switches.cc", "crash_switches.cc",
"crash_switches.h", "crash_switches.h",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/crash/content/app/client_upload_info.h"
#include "components/crash/content/app/crash_reporter_client.h"
namespace crash_reporter {
bool GetClientCollectStatsConsent() {
return GetCrashReporterClient()->GetCollectStatsConsent();
}
#if defined(OS_POSIX) && !defined(OS_MACOSX)
void GetClientProductNameAndVersion(std::string* product,
std::string* version,
std::string* channel) {
GetCrashReporterClient()->GetProductNameAndVersion(product, version, channel);
}
#endif
} // namespace crash_reporter
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_CRASH_CONTENT_APP_CLIENT_UPLOAD_INFO_H_
#define COMPONENTS_CRASH_CONTENT_APP_CLIENT_UPLOAD_INFO_H_
#include <string>
#include "build/build_config.h"
namespace crash_reporter {
// Returns whether the user has consented to collecting stats.
bool GetClientCollectStatsConsent();
#if defined(OS_POSIX) && !defined(OS_MACOSX)
// Returns a textual description of the product type, version and channel
// to include in crash reports.
// TODO(https://crbug.com/986178): Implement this for other platforms.
void GetClientProductNameAndVersion(std::string* product,
std::string* version,
std::string* channel);
#endif
} // namespace crash_reporter
#endif // COMPONENTS_CRASH_CONTENT_APP_CLIENT_UPLOAD_INFO_H_
...@@ -504,6 +504,7 @@ source_set("browser_tests") { ...@@ -504,6 +504,7 @@ source_set("browser_tests") {
sources += [ sources += [
"api/audio/audio_apitest_chromeos.cc", "api/audio/audio_apitest_chromeos.cc",
"api/cec_private/cec_private_apitest.cc", "api/cec_private/cec_private_apitest.cc",
"api/crash_report_private/crash_report_private_apitest.cc",
"api/media_perception_private/media_perception_private_apitest.cc", "api/media_perception_private/media_perception_private_apitest.cc",
"api/system_power_source/system_power_source_apitest.cc", "api/system_power_source/system_power_source_apitest.cc",
"api/virtual_keyboard/virtual_keyboard_apitest.cc", "api/virtual_keyboard/virtual_keyboard_apitest.cc",
...@@ -521,6 +522,7 @@ source_set("browser_tests") { ...@@ -521,6 +522,7 @@ source_set("browser_tests") {
"//chromeos/dbus/upstart", "//chromeos/dbus/upstart",
"//chromeos/login/login_state", "//chromeos/login/login_state",
"//chromeos/network", "//chromeos/network",
"//components/crash/content/app:app",
] ]
} }
} }
......
...@@ -131,6 +131,7 @@ source_set("api") { ...@@ -131,6 +131,7 @@ source_set("api") {
public_deps += [ public_deps += [
"//extensions/browser/api/cec_private", "//extensions/browser/api/cec_private",
"//extensions/browser/api/clipboard", "//extensions/browser/api/clipboard",
"//extensions/browser/api/crash_report_private",
"//extensions/browser/api/diagnostics", "//extensions/browser/api/diagnostics",
"//extensions/browser/api/networking_config", "//extensions/browser/api/networking_config",
"//extensions/browser/api/system_power_source", "//extensions/browser/api/system_power_source",
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//extensions/buildflags/buildflags.gni")
assert(enable_extensions,
"Cannot depend on extensions because enable_extensions=false.")
source_set("crash_report_private") {
sources = [
"crash_report_private_api.cc",
"crash_report_private_api.h",
]
deps = [
"//components/crash/content/app",
"//content/public/browser",
"//extensions/common/api",
"//net",
"//services/network:network_service",
"//services/network/public/cpp",
]
}
include_rules = [
"+components/crash/content/app/client_upload_info.h",
]
specific_include_rules = {
"crash_report_private_apitest.cc": [
"+components/crash/content/app/crash_reporter_client.h",
],
}
shend@chromium.org
ortuno@chromium.org
# COMPONENT: Platform>Apps>SystemWebApps
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/api/crash_report_private/crash_report_private_api.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/task/post_task.h"
#include "base/time/default_clock.h"
#include "components/crash/content/app/client_upload_info.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/common/api/crash_report_private.h"
#include "net/base/escape.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace extensions {
namespace api {
namespace {
// Used for throttling the API calls.
base::Time g_last_called_time;
base::Clock* g_clock = base::DefaultClock::GetInstance();
#if defined(GOOGLE_CHROME_BUILD)
constexpr char kCrashEndpointUrl[] = "https://clients2.google.com/cr/report";
#else
constexpr char kCrashEndpointUrl[] = "";
#endif
std::string& GetCrashEndpoint() {
static base::NoDestructor<std::string> crash_endpoint(kCrashEndpointUrl);
return *crash_endpoint;
}
constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024;
void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader,
base::OnceCallback<void()> callback,
std::unique_ptr<std::string> response_body) {
if (response_body) {
DVLOG(1) << "Uploaded crash report. ID: " << *response_body;
} else {
LOG(ERROR) << "Failed to upload crash report";
}
std::move(callback).Run();
}
// Sometimes, the stack trace will contain an error message as the first line,
// which confuses the Crash server. This function deletes it if it is present.
std::string RemoveErrorMessageFromStackTrace(const std::string& stack_trace,
const std::string& error_message) {
// Return the original stack trace if the error message is not present.
const auto error_message_index = stack_trace.find(error_message);
if (error_message_index == std::string::npos)
return stack_trace;
// If the stack trace only contains one line, then delete the whole trace.
const auto first_line_end_index = stack_trace.find('\n');
if (first_line_end_index == std::string::npos)
return std::string();
// Otherwise, delete the first line.
return stack_trace.substr(first_line_end_index + 1);
}
using ParameterMap = std::map<std::string, std::string>;
std::string BuildPostRequestQueryString(const ParameterMap& params) {
std::vector<std::string> query_parts;
for (const auto& kv : params) {
query_parts.push_back(base::StrCat(
{kv.first, "=",
net::EscapeQueryParamValue(kv.second, /* use_plus */ false)}));
}
return base::JoinString(query_parts, "&");
}
struct PlatformInfo {
std::string product_name;
std::string version;
std::string channel;
std::string platform;
std::string os_version;
};
PlatformInfo GetPlatformInfo() {
PlatformInfo info;
crash_reporter::GetClientProductNameAndVersion(&info.product_name,
&info.version, &info.channel);
int32_t os_major_version = 0;
int32_t os_minor_version = 0;
int32_t os_bugfix_version = 0;
base::SysInfo::OperatingSystemVersionNumbers(
&os_major_version, &os_minor_version, &os_bugfix_version);
info.os_version = base::StringPrintf("%d.%d.%d", os_major_version,
os_minor_version, os_bugfix_version);
return info;
}
void SendReport(network::mojom::URLLoaderFactory* loader_factory,
const GURL& url,
const std::string& body,
base::OnceCallback<void()> callback) {
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->method = "POST";
resource_request->url = url;
const auto traffic_annotation =
net::DefineNetworkTrafficAnnotation("javascript_report_error", R"(
semantics {
sender: "JavaScript error reporter"
description:
"Chrome can send JavaScript errors that occur within built-in "
"component extensions. If enabled, the error message, along "
"with information about Chrome and the operating system."
trigger:
"A JavaScript error occurs in a Chrome component extension"
data:
"The JavaScript error message, the version and channel of Chrome, "
"the URL of the extension, the line and column number where the "
"error occurred, and a stack trace of the error."
destination: GOOGLE_OWNED_SERVICE
}
)");
DVLOG(1) << "Sending crash report: " << resource_request->url;
auto url_loader = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
if (!body.empty()) {
url_loader->AttachStringForUpload(body, "text/plain");
}
network::SimpleURLLoader* loader = url_loader.get();
loader->DownloadToString(
loader_factory,
base::BindOnce(&OnRequestComplete, std::move(url_loader),
std::move(callback)),
kCrashEndpointResponseMaxSizeInBytes);
}
void ReportJavaScriptError(network::mojom::URLLoaderFactory* loader_factory,
const crash_report_private::ErrorInfo& error,
base::OnceCallback<void()> callback) {
const auto platform = GetPlatformInfo();
const GURL source(error.url);
const auto product = error.product ? *error.product : platform.product_name;
const auto version = error.version ? *error.version : platform.version;
ParameterMap params;
params["prod"] = net::EscapeQueryParamValue(product, /* use_plus */ false);
params["ver"] = net::EscapeQueryParamValue(version, /* use_plus */ false);
params["type"] = "JavascriptError";
// TODO(https://crbug.com/986178): Include |error.message| once we scrub PII.
params["browser"] = "Chrome";
params["browser_version"] = platform.version;
params["channel"] = platform.channel;
params["os"] = "ChromeOS";
params["os_version"] = platform.os_version;
params["full_url"] = source.spec();
params["url"] = source.path();
params["src"] = source.spec();
if (error.line_number)
params["line"] = *error.line_number;
if (error.column_number)
params["column"] = *error.column_number;
// The network request must be made on the UI thread.
const GURL url(base::StrCat(
{GetCrashEndpoint(), "?", BuildPostRequestQueryString(params)}));
const std::string body =
error.stack_trace
? RemoveErrorMessageFromStackTrace(*error.stack_trace, error.message)
: "";
SendReport(loader_factory, url, body, std::move(callback));
}
} // namespace
CrashReportPrivateReportErrorFunction::CrashReportPrivateReportErrorFunction() =
default;
CrashReportPrivateReportErrorFunction::
~CrashReportPrivateReportErrorFunction() = default;
ExtensionFunction::ResponseAction CrashReportPrivateReportErrorFunction::Run() {
// Do not report errors if the user did not give consent for crash reporting.
if (!crash_reporter::GetClientCollectStatsConsent())
return RespondNow(NoArguments());
// Ensure we don't send too many crash reports. Limit to one report per hour.
if (!g_last_called_time.is_null() &&
g_clock->Now() - g_last_called_time < base::TimeDelta::FromHours(1)) {
return RespondNow(Error("Too many calls to this API"));
}
// TODO(https://crbug.com/986166): Use crash_reporter for Chrome OS.
const auto params = crash_report_private::ReportError::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params.get());
ReportJavaScriptError(
content::BrowserContext::GetDefaultStoragePartition(browser_context())
->GetURLLoaderFactoryForBrowserProcess()
.get(),
params->info,
base::BindOnce(&CrashReportPrivateReportErrorFunction::OnReportComplete,
this));
g_last_called_time = base::Time::Now();
return RespondLater();
}
void CrashReportPrivateReportErrorFunction::OnReportComplete() {
Respond(NoArguments());
}
void SetClockForTesting(base::Clock* clock) {
g_clock = clock;
}
void SetCrashEndpointForTesting(const std::string& endpoint) {
GetCrashEndpoint() = endpoint;
}
} // namespace api
} // namespace extensions
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_API_CRASH_REPORT_PRIVATE_CRASH_REPORT_PRIVATE_API_H_
#define EXTENSIONS_BROWSER_API_CRASH_REPORT_PRIVATE_CRASH_REPORT_PRIVATE_API_H_
#include <string>
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_function_histogram_value.h"
namespace base {
class Clock;
}
namespace extensions {
namespace api {
class CrashReportPrivateReportErrorFunction : public ExtensionFunction {
public:
CrashReportPrivateReportErrorFunction();
DECLARE_EXTENSION_FUNCTION("crashReportPrivate.reportError",
CRASHREPORTPRIVATE_REPORTERROR)
protected:
~CrashReportPrivateReportErrorFunction() override;
ResponseAction Run() override;
private:
void OnReportComplete();
DISALLOW_COPY_AND_ASSIGN(CrashReportPrivateReportErrorFunction);
};
void SetClockForTesting(base::Clock* clock);
void SetCrashEndpointForTesting(const std::string& endpoint);
} // namespace api
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_CRASH_REPORT_PRIVATE_CRASH_REPORT_PRIVATE_API_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/system/sys_info.h"
#include "base/test/simple_test_clock.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/api/crash_report_private/crash_report_private_api.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/common/switches.h"
#include "extensions/shell/test/shell_apitest.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
namespace extensions {
using browsertest_util::ExecuteScriptInBackgroundPage;
namespace {
constexpr const char* kTestExtensionId = "jjeoclcdfjddkdjokiejckgcildcflpp";
constexpr const char* kTestCrashEndpoint = "/crash";
class MockCrashReporterClient : public crash_reporter::CrashReporterClient {
bool GetCollectStatsConsent() override { return true; }
void GetProductNameAndVersion(std::string* product_name,
std::string* version,
std::string* channel) override {
*product_name = "Chrome (Chrome OS)";
*version = "1.2.3.4";
*channel = "Stable";
}
};
std::string GetOsVersion() {
int32_t os_major_version = 0;
int32_t os_minor_version = 0;
int32_t os_bugfix_version = 0;
base::SysInfo::OperatingSystemVersionNumbers(
&os_major_version, &os_minor_version, &os_bugfix_version);
return base::StringPrintf("%d.%d.%d", os_major_version, os_minor_version,
os_bugfix_version);
}
} // namespace
class CrashReportPrivateApiTest : public ShellApiTest {
public:
CrashReportPrivateApiTest() = default;
~CrashReportPrivateApiTest() override = default;
void SetUpOnMainThread() override {
ShellApiTest::SetUpOnMainThread();
constexpr char kKey[] =
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+uU63MD6T82Ldq5wjrDFn5mGmPnnnj"
"WZBWxYXfpG4kVf0s+p24VkXwTXsxeI12bRm8/ft9sOq0XiLfgQEh5JrVUZqvFlaZYoS+g"
"iZfUqzKFGMLa4uiSMDnvv+byxrqAepKz5G8XX/q5Wm5cvpdjwgiu9z9iM768xJy+Ca/G5"
"qQwIDAQAB";
constexpr char kManifestTemplate[] =
R"({
"key": "%s",
"name": "chrome.crashReportPrivate basic extension tests",
"version": "1.0",
"manifest_version": 2,
"background": { "scripts": ["test.js"] },
"permissions": ["crashReportPrivate"]
})";
TestExtensionDir test_dir;
test_dir.WriteManifest(base::StringPrintf(kManifestTemplate, kKey));
test_dir.WriteFile(FILE_PATH_LITERAL("test.js"),
R"(chrome.test.sendMessage('ready');)");
ExtensionTestMessageListener listener("ready", false);
extension_ = LoadExtension(test_dir.UnpackedPath());
EXPECT_TRUE(listener.WaitUntilSatisfied());
embedded_test_server()->RegisterRequestHandler(base::Bind(
&CrashReportPrivateApiTest::HandleRequest, base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
api::SetCrashEndpointForTesting(
embedded_test_server()->GetURL(kTestCrashEndpoint).spec());
crash_reporter::SetCrashReporterClient(&client_);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(
extensions::switches::kWhitelistedExtensionID, kTestExtensionId);
ShellApiTest::SetUpCommandLine(command_line);
}
protected:
struct Report {
std::string query;
std::string content;
};
const Extension* extension_;
Report last_report_;
private:
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
GURL absolute_url = embedded_test_server()->GetURL(request.relative_url);
if (absolute_url.path() != kTestCrashEndpoint) {
return nullptr;
}
last_report_ = {absolute_url.query(), request.content};
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content("123");
http_response->set_content_type("text/plain");
return http_response;
}
MockCrashReporterClient client_;
DISALLOW_COPY_AND_ASSIGN(CrashReportPrivateApiTest);
};
IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, Basic) {
constexpr char kTestScript[] = R"(
chrome.crashReportPrivate.reportError({
message: "hi",
url: "http://www.test.com",
},
() => window.domAutomationController.send(""));
)";
ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
kTestScript);
EXPECT_EQ(last_report_.query,
"browser=Chrome&browser_version=1.2.3.4&channel=Stable&"
"full_url=http%3A%2F%2Fwww.test.com%2F&os=ChromeOS&os_version=" +
GetOsVersion() +
"&prod=Chrome%2520(Chrome%2520OS)&src=http%3A%2F%2Fwww.test."
"com%2F&type=JavascriptError&url=%2F&ver=1.2.3.4");
EXPECT_EQ(last_report_.content, "");
}
IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, ExtraParamsAndStackTrace) {
constexpr char kTestScript[] = R"(
chrome.crashReportPrivate.reportError({
message: "hi",
url: "http://www.test.com/foo",
product: "TestApp",
version: "1.0.0.0",
lineNumber: 123,
columnNumber: 456,
stackTrace: " at <anonymous>:1:1",
},
() => window.domAutomationController.send(""));
)";
ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
kTestScript);
EXPECT_EQ(last_report_.query,
"browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=%C8&"
"full_url=http%3A%2F%2Fwww.test.com%2Ffoo&line=%7B&os=ChromeOS&"
"os_version=" +
GetOsVersion() +
"&prod=TestApp&src=http%3A%2F%2Fwww.test.com%2Ffoo&type="
"JavascriptError&url=%2Ffoo&ver=1.0.0.0");
EXPECT_EQ(last_report_.content, " at <anonymous>:1:1");
}
IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, StackTraceWithErrorMessage) {
constexpr char kTestScript[] = R"(
chrome.crashReportPrivate.reportError({
message: "hi",
url: "http://www.test.com/foo",
product: 'TestApp',
version: '1.0.0.0',
lineNumber: 123,
columnNumber: 456,
stackTrace: 'hi'
},
() => window.domAutomationController.send(""));
)";
ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
kTestScript);
EXPECT_EQ(last_report_.query,
"browser=Chrome&browser_version=1.2.3.4&channel=Stable&column=%C8&"
"full_url=http%3A%2F%2Fwww.test.com%2Ffoo&line=%7B&os=ChromeOS&"
"os_version=" +
GetOsVersion() +
"&prod=TestApp&src=http%3A%2F%2Fwww.test.com%2Ffoo&type="
"JavascriptError&url=%2Ffoo&ver=1.0.0.0");
EXPECT_EQ(last_report_.content, "");
}
IN_PROC_BROWSER_TEST_F(CrashReportPrivateApiTest, Throttling) {
constexpr char kTestScript[] = R"(
chrome.crashReportPrivate.reportError({
message: "hi",
url: "http://www.test.com",
},
() => {
window.domAutomationController.send(chrome.runtime.lastError ?
chrome.runtime.lastError.message : "")
});
)";
base::SimpleTestClock test_clock;
test_clock.SetNow(base::Time::Now());
api::SetClockForTesting(&test_clock);
// Use an exact time for the first API call.
EXPECT_EQ("", ExecuteScriptInBackgroundPage(browser_context(),
extension_->id(), kTestScript));
// API is limited to one call per hr. So pretend the second call is just
// before 1 hr.
test_clock.Advance(base::TimeDelta::FromMinutes(59));
EXPECT_EQ("Too many calls to this API",
ExecuteScriptInBackgroundPage(browser_context(), extension_->id(),
kTestScript));
// Call again after 1 hr.
test_clock.Advance(base::TimeDelta::FromMinutes(2));
EXPECT_EQ("", ExecuteScriptInBackgroundPage(browser_context(),
extension_->id(), kTestScript));
}
} // namespace extensions
...@@ -1479,6 +1479,7 @@ enum HistogramValue { ...@@ -1479,6 +1479,7 @@ enum HistogramValue {
TERMINALPRIVATE_SETSETTINGS = 1416, TERMINALPRIVATE_SETSETTINGS = 1416,
WEBSTOREPRIVATE_REQUESTEXTENSION = 1417, WEBSTOREPRIVATE_REQUESTEXTENSION = 1417,
AUTOTESTPRIVATE_INSTALLPLUGINVM = 1418, AUTOTESTPRIVATE_INSTALLPLUGINVM = 1418,
CRASHREPORTPRIVATE_REPORTERROR = 1419,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -142,6 +142,10 @@ ...@@ -142,6 +142,10 @@
"clipboard.setImageData": { "clipboard.setImageData": {
"dependencies": ["permission:clipboardWrite"] "dependencies": ["permission:clipboardWrite"]
}, },
"crashReportPrivate": {
"dependencies": ["permission:crashReportPrivate"],
"contexts": ["blessed_extension"]
},
"declarativeNetRequest": { "declarativeNetRequest": {
"dependencies": ["permission:declarativeNetRequest"], "dependencies": ["permission:declarativeNetRequest"],
"contexts": ["blessed_extension"] "contexts": ["blessed_extension"]
......
...@@ -199,6 +199,13 @@ ...@@ -199,6 +199,13 @@
"extension_types": ["platform_app"], "extension_types": ["platform_app"],
"platforms": ["chromeos"] "platforms": ["chromeos"]
}, },
"crashReportPrivate": {
"channel": "dev",
"extension_types": ["extension"],
"whitelist": [
"06BE211D5F014BAB34BC22D9DDA09C63A81D828E" // http://crbug.com/946241
]
},
"declarativeNetRequest": { "declarativeNetRequest": {
"channel": "beta", "channel": "beta",
"extension_types": ["extension"], "extension_types": ["extension"],
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Private API for Chrome component extensions to report errors.
[platforms=("chromeos")]
namespace crashReportPrivate {
// A dictionary containing additional context about the error.
dictionary ErrorInfo {
// The error message.
DOMString message;
// URL where the error occurred.
// Must be the full URL, containing the protocol (e.g.
// http://www.example.com).
DOMString url;
// Name of the product where the error occurred.
// Defaults to the product variant of Chrome that is hosting the extension.
// (e.g. "Chrome" or "Chrome_ChromeOS").
DOMString? product;
// Version of the product where the error occurred.
// Defaults to the version of Chrome that is hosting the extension (e.g.
// "73.0.3683.75").
DOMString? version;
// Line number where the error occurred.
long? lineNumber;
// Column number where the error occurred.
long? columnNumber;
// String containing the stack trace for the error.
// Defaults to the empty string.
DOMString? stackTrace;
};
// Callback for |reportError|.
callback ReportCallback = void ();
interface Functions {
// Report and upload an error to Crash.
// |info|: Information about the error.
// |callback|: Called when the error has been uploaded.
static void reportError(ErrorInfo info, ReportCallback callback);
};
};
...@@ -63,6 +63,7 @@ extensions_api_schema_files_ = [ ...@@ -63,6 +63,7 @@ extensions_api_schema_files_ = [
if (is_chromeos) { if (is_chromeos) {
extensions_api_schema_files_ += [ extensions_api_schema_files_ += [
"crash_report_private.idl",
"diagnostics.idl", "diagnostics.idl",
"lock_screen_data.idl", "lock_screen_data.idl",
"media_perception_private.idl", "media_perception_private.idl",
......
...@@ -266,6 +266,7 @@ class APIPermission { ...@@ -266,6 +266,7 @@ class APIPermission {
kLoginState = 222, kLoginState = 222,
kPrintingMetrics = 223, kPrintingMetrics = 223,
kPrinting = 224, kPrinting = 224,
kCrashReportPrivate = 225,
// Last entry: Add new entries above and ensure to update the // Last entry: Add new entries above and ensure to update the
// "ExtensionPermission3" enum in tools/metrics/histograms/enums.xml // "ExtensionPermission3" enum in tools/metrics/histograms/enums.xml
// (by running update_extension_permission.py). // (by running update_extension_permission.py).
......
...@@ -44,6 +44,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = { ...@@ -44,6 +44,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
APIPermissionInfo::kFlagSupportsContentCapabilities}, APIPermissionInfo::kFlagSupportsContentCapabilities},
{APIPermission::kClipboardWrite, "clipboardWrite", {APIPermission::kClipboardWrite, "clipboardWrite",
APIPermissionInfo::kFlagSupportsContentCapabilities}, APIPermissionInfo::kFlagSupportsContentCapabilities},
{APIPermission::kCrashReportPrivate, "crashReportPrivate"},
{APIPermission::kDeclarativeWebRequest, "declarativeWebRequest"}, {APIPermission::kDeclarativeWebRequest, "declarativeWebRequest"},
{APIPermission::kDiagnostics, "diagnostics", {APIPermission::kDiagnostics, "diagnostics",
APIPermissionInfo::kFlagCannotBeOptional}, APIPermissionInfo::kFlagCannotBeOptional},
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include "extensions/shell/test/shell_apitest.h" #include "extensions/shell/test/shell_apitest.h"
#include "base/files/file_path.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
...@@ -31,6 +30,11 @@ const Extension* ShellApiTest::LoadExtension(const std::string& extension_dir) { ...@@ -31,6 +30,11 @@ const Extension* ShellApiTest::LoadExtension(const std::string& extension_dir) {
return extension_system_->LoadExtension(extension_path); return extension_system_->LoadExtension(extension_path);
} }
const Extension* ShellApiTest::LoadExtension(
const base::FilePath& extension_path) {
return extension_system_->LoadExtension(extension_path);
}
const Extension* ShellApiTest::LoadApp(const std::string& app_dir) { const Extension* ShellApiTest::LoadApp(const std::string& app_dir) {
base::ScopedAllowBlockingForTesting allow_blocking; base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath test_data_dir; base::FilePath test_data_dir;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string> #include <string>
#include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "extensions/shell/test/shell_test.h" #include "extensions/shell/test/shell_test.h"
...@@ -27,6 +28,11 @@ class ShellApiTest : public AppShellTest { ...@@ -27,6 +28,11 @@ class ShellApiTest : public AppShellTest {
// |extension_dir| should be a subpath under extensions/test/data. // |extension_dir| should be a subpath under extensions/test/data.
const Extension* LoadExtension(const std::string& extension_dir); const Extension* LoadExtension(const std::string& extension_dir);
// Loads an unpacked extension. Returns an instance of the extension that was
// just loaded.
// |extension_path| should be an absolute path to the extension.
const Extension* LoadExtension(const base::FilePath& extension_path);
// Loads and launches an unpacked platform app. Returns an instance of the // Loads and launches an unpacked platform app. Returns an instance of the
// extension that was just loaded. // extension that was just loaded.
// |app_dir| should be a subpath under extensions/test/data. // |app_dir| should be a subpath under extensions/test/data.
......
...@@ -21435,6 +21435,7 @@ Called by update_net_error_codes.py.--> ...@@ -21435,6 +21435,7 @@ Called by update_net_error_codes.py.-->
<int value="1416" label="TERMINALPRIVATE_SETSETTINGS"/> <int value="1416" label="TERMINALPRIVATE_SETSETTINGS"/>
<int value="1417" label="WEBSTOREPRIVATE_REQUESTEXTENSION"/> <int value="1417" label="WEBSTOREPRIVATE_REQUESTEXTENSION"/>
<int value="1418" label="AUTOTESTPRIVATE_INSTALLPLUGINVM"/> <int value="1418" label="AUTOTESTPRIVATE_INSTALLPLUGINVM"/>
<int value="1419" label="CRASHREPORTPRIVATE_REPORTERROR"/>
</enum> </enum>
<enum name="ExtensionIconState"> <enum name="ExtensionIconState">
...@@ -21988,6 +21989,7 @@ Called by update_net_error_codes.py.--> ...@@ -21988,6 +21989,7 @@ Called by update_net_error_codes.py.-->
<int value="222" label="kLoginState"/> <int value="222" label="kLoginState"/>
<int value="223" label="kPrintingMetrics"/> <int value="223" label="kPrintingMetrics"/>
<int value="224" label="kPrinting"/> <int value="224" label="kPrinting"/>
<int value="225" label="kCrashReportPrivate"/>
</enum> </enum>
<enum name="ExtensionServiceVerifyAllSuccess"> <enum name="ExtensionServiceVerifyAllSuccess">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment