mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
23 Commits
v16.0.0-be
...
v16.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c16e94ebbf | ||
|
|
48be5c3387 | ||
|
|
9fd781073f | ||
|
|
c25edb5fdd | ||
|
|
cf6df4ae36 | ||
|
|
6729edf590 | ||
|
|
2415813313 | ||
|
|
136c539836 | ||
|
|
a76f2a07a9 | ||
|
|
8b6aecc6f0 | ||
|
|
3186a246a5 | ||
|
|
15ff515437 | ||
|
|
d3a24c24af | ||
|
|
f4a51e2adc | ||
|
|
cdc897c745 | ||
|
|
8c842552b0 | ||
|
|
e918fe6030 | ||
|
|
d9e0025429 | ||
|
|
5fc4a4768e | ||
|
|
cf54123ec6 | ||
|
|
948aa12af0 | ||
|
|
5fdf37eac9 | ||
|
|
1dcf237c8d |
4
BUILD.gn
4
BUILD.gn
@@ -420,7 +420,6 @@ source_set("electron_lib") {
|
||||
]
|
||||
|
||||
include_dirs = [
|
||||
"chromium_src",
|
||||
".",
|
||||
"$target_gen_dir",
|
||||
|
||||
@@ -554,8 +553,9 @@ source_set("electron_lib") {
|
||||
"GLIB_DISABLE_DEPRECATION_WARNINGS",
|
||||
]
|
||||
|
||||
sources += filenames.lib_sources_nss
|
||||
sources += [
|
||||
"shell/browser/certificate_manager_model.cc",
|
||||
"shell/browser/certificate_manager_model.h",
|
||||
"shell/browser/ui/gtk/app_indicator_icon.cc",
|
||||
"shell/browser/ui/gtk/app_indicator_icon.h",
|
||||
"shell/browser/ui/gtk/app_indicator_icon_menu.cc",
|
||||
|
||||
2
DEPS
2
DEPS
@@ -15,7 +15,7 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'96.0.4664.27',
|
||||
'96.0.4664.35',
|
||||
'node_version':
|
||||
'v16.9.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -1 +1 @@
|
||||
16.0.0-beta.6
|
||||
16.0.0-beta.9
|
||||
@@ -197,7 +197,7 @@ Returns `Boolean` - Whether the clipboard supports the specified `format`.
|
||||
```js
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const hasFormat = clipboard.has('<p>selection</p>')
|
||||
const hasFormat = clipboard.has('public/utf8-plain-text')
|
||||
console.log(hasFormat)
|
||||
// 'true' or 'false'
|
||||
```
|
||||
|
||||
@@ -675,11 +675,6 @@ filenames = {
|
||||
"shell/utility/electron_content_utility_client.h",
|
||||
]
|
||||
|
||||
lib_sources_nss = [
|
||||
"chromium_src/chrome/browser/certificate_manager_model.cc",
|
||||
"chromium_src/chrome/browser/certificate_manager_model.h",
|
||||
]
|
||||
|
||||
lib_sources_extensions = [
|
||||
"shell/browser/extensions/api/i18n/i18n_api.cc",
|
||||
"shell/browser/extensions/api/i18n/i18n_api.h",
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
export const enum IPC_MESSAGES {
|
||||
BROWSER_REQUIRE = 'REMOTE_BROWSER_REQUIRE',
|
||||
BROWSER_GET_BUILTIN = 'REMOTE_BROWSER_GET_BUILTIN',
|
||||
BROWSER_GET_GLOBAL = 'REMOTE_BROWSER_GET_GLOBAL',
|
||||
BROWSER_GET_CURRENT_WINDOW = 'REMOTE_BROWSER_GET_CURRENT_WINDOW',
|
||||
BROWSER_GET_CURRENT_WEB_CONTENTS = 'REMOTE_BROWSER_GET_CURRENT_WEB_CONTENTS',
|
||||
BROWSER_CONSTRUCTOR = 'REMOTE_BROWSER_CONSTRUCTOR',
|
||||
BROWSER_FUNCTION_CALL = 'REMOTE_BROWSER_FUNCTION_CALL',
|
||||
BROWSER_MEMBER_CONSTRUCTOR = 'REMOTE_BROWSER_MEMBER_CONSTRUCTOR',
|
||||
BROWSER_MEMBER_CALL = 'REMOTE_BROWSER_MEMBER_CALL',
|
||||
BROWSER_MEMBER_GET = 'REMOTE_BROWSER_MEMBER_GET',
|
||||
BROWSER_MEMBER_SET = 'REMOTE_BROWSER_MEMBER_SET',
|
||||
BROWSER_DEREFERENCE = 'REMOTE_BROWSER_DEREFERENCE',
|
||||
BROWSER_CONTEXT_RELEASE = 'REMOTE_BROWSER_CONTEXT_RELEASE',
|
||||
BROWSER_WRONG_CONTEXT_ERROR = 'REMOTE_BROWSER_WRONG_CONTEXT_ERROR',
|
||||
|
||||
RENDERER_CALLBACK = 'REMOTE_RENDERER_CALLBACK',
|
||||
RENDERER_RELEASE_CALLBACK = 'REMOTE_RENDERER_RELEASE_CALLBACK',
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "16.0.0-beta.6",
|
||||
"version": "16.0.0-beta.9",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
@@ -78,7 +78,7 @@
|
||||
"generate-version-json": "node script/generate-version-json.js",
|
||||
"lint": "node ./script/lint.js && npm run lint:clang-format && npm run lint:docs",
|
||||
"lint:js": "node ./script/lint.js --js",
|
||||
"lint:clang-format": "python script/run-clang-format.py -r -c chromium_src/ shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
|
||||
"lint:clang-format": "python script/run-clang-format.py -r -c shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
|
||||
"lint:clang-tidy": "ts-node ./script/run-clang-tidy.ts",
|
||||
"lint:cpp": "node ./script/lint.js --cc",
|
||||
"lint:objc": "node ./script/lint.js --objc",
|
||||
|
||||
@@ -106,6 +106,7 @@ feat_expose_raw_response_headers_from_urlloader.patch
|
||||
chore_do_not_use_chrome_windows_in_cryptotoken_webrequestsender.patch
|
||||
process_singleton.patch
|
||||
fix_expose_decrementcapturercount_in_web_contents_impl.patch
|
||||
add_ui_scopedcliboardwriter_writeunsaferawdata.patch
|
||||
feat_add_data_parameter_to_processsingleton.patch
|
||||
mas_gate_private_enterprise_APIs
|
||||
load_v8_snapshot_in_browser_process.patch
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Henri Torgemane <henrit@gmail.com>
|
||||
Date: Thu, 23 Sep 2021 21:30:33 -0500
|
||||
Subject: add ui::ScopedCliboardWriter::WriteUnsafeRawData
|
||||
|
||||
This restores some ability to write to the clipboard using raw formats, which
|
||||
was removed as part of the Raw Clipboard API scrubbing.
|
||||
https://bugs.chromium.org/p/chromium/issues/detail?id=1217643
|
||||
|
||||
diff --git a/ui/base/clipboard/scoped_clipboard_writer.cc b/ui/base/clipboard/scoped_clipboard_writer.cc
|
||||
index 153f169d2cdef6f8a726c188283a5bc1b7395fa3..3a5d9ab8dafacafb1025e1cb8c157e8a82078424 100644
|
||||
--- a/ui/base/clipboard/scoped_clipboard_writer.cc
|
||||
+++ b/ui/base/clipboard/scoped_clipboard_writer.cc
|
||||
@@ -212,6 +212,16 @@ void ScopedClipboardWriter::WriteData(const std::u16string& format,
|
||||
}
|
||||
}
|
||||
|
||||
+void ScopedClipboardWriter::WriteUnsafeRawData(const std::u16string& format,
|
||||
+ mojo_base::BigBuffer data) {
|
||||
+ static constexpr int kMaxRegisteredFormats = 100;
|
||||
+ if (counter_ >= kMaxRegisteredFormats)
|
||||
+ return;
|
||||
+ counter_++;
|
||||
+ platform_representations_.push_back(
|
||||
+ {base::UTF16ToUTF8(format), std::move(data)});
|
||||
+}
|
||||
+
|
||||
void ScopedClipboardWriter::Reset() {
|
||||
objects_.clear();
|
||||
platform_representations_.clear();
|
||||
diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h
|
||||
index 879acd4f6f0101a6da3af58d78eeda877ea41a4a..4d4149b6aa34c7073804994cb1c03368830c736d 100644
|
||||
--- a/ui/base/clipboard/scoped_clipboard_writer.h
|
||||
+++ b/ui/base/clipboard/scoped_clipboard_writer.h
|
||||
@@ -80,6 +80,10 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter {
|
||||
// This is only used to write custom format data.
|
||||
void WriteData(const std::u16string& format, mojo_base::BigBuffer data);
|
||||
|
||||
+ // write raw (non-pickled) data to the clipboard
|
||||
+ void WriteUnsafeRawData(const std::u16string& format,
|
||||
+ mojo_base::BigBuffer data);
|
||||
+
|
||||
void WriteImage(const SkBitmap& bitmap);
|
||||
|
||||
// Mark the data to be written as confidential.
|
||||
@@ -46,7 +46,7 @@ index 5016aa8bcbcc0c8db3e8e42b04ccef3552adda8b..21e92b783a9c9af54fb81174dddbc4a5
|
||||
}
|
||||
|
||||
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
|
||||
index e3c3351864055c25754567eebcc3509cff8c1a1e..0e9dd3d92a1937df7b929a49f4e56bbe36fac244 100644
|
||||
index 5f351bbdd5d71af84c9acabe745be4f0d7b77f94..ca85a577d29c301cb0cf7ab7118557663edba119 100644
|
||||
--- a/chrome/test/BUILD.gn
|
||||
+++ b/chrome/test/BUILD.gn
|
||||
@@ -5499,7 +5499,6 @@ test("unit_tests") {
|
||||
|
||||
@@ -9,7 +9,7 @@ potentially prevent a window from being created.
|
||||
TODO(loc): this patch is currently broken.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 1d0639d7b6efcc60b26e8a13f35dc0402cef3e64..2f6bf718382ab63d7b736d18e1b69222ab1a8333 100644
|
||||
index 7a7cc0ebf81bcecc7141ddbe217b770846a9b6ce..af9f5db7e38bd48d478b346325bb4944fb3bb0d8 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -6497,6 +6497,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
|
||||
@@ -61,7 +61,7 @@ index eec994c4252f17d9c9c41e66d5dae6509ed98a18..e538c9b76da4d4435e10cd3848438446
|
||||
#if defined(OS_WIN)
|
||||
bool EscapeVirtualization(const base::FilePath& user_data_dir);
|
||||
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
|
||||
index a04d139f958a7aaef9b96e8c29317ccf7c97f009..29188668a69047b3ad3bebd1f0057565a330b509 100644
|
||||
index a04d139f958a7aaef9b96e8c29317ccf7c97f009..e77cebd31967d28f3cb0db78e736011510634c0e 100644
|
||||
--- a/chrome/browser/process_singleton_posix.cc
|
||||
+++ b/chrome/browser/process_singleton_posix.cc
|
||||
@@ -567,6 +567,7 @@ class ProcessSingleton::LinuxWatcher
|
||||
@@ -101,7 +101,7 @@ index a04d139f958a7aaef9b96e8c29317ccf7c97f009..29188668a69047b3ad3bebd1f0057565
|
||||
const size_t kMinMessageLength = base::size(kStartToken) + 4;
|
||||
if (bytes_read_ < kMinMessageLength) {
|
||||
buf_[bytes_read_] = 0;
|
||||
@@ -705,10 +710,25 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
@@ -705,10 +710,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
tokens.erase(tokens.begin());
|
||||
tokens.erase(tokens.begin());
|
||||
|
||||
@@ -110,13 +110,16 @@ index a04d139f958a7aaef9b96e8c29317ccf7c97f009..29188668a69047b3ad3bebd1f0057565
|
||||
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
|
||||
+
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (tokens.size() == 3 + num_args) {
|
||||
+ if (tokens.size() >= 3 + num_args) {
|
||||
+ size_t additional_data_size;
|
||||
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
|
||||
+ std::string remaining_args = base::JoinString(
|
||||
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
|
||||
+ std::string(1, kTokenDelimiter));
|
||||
+ const uint8_t* additional_data_bits =
|
||||
+ reinterpret_cast<const uint8_t*>(tokens[2 + num_args].c_str());
|
||||
+ additional_data = std::vector<const uint8_t>(additional_data_bits,
|
||||
+ additional_data_bits + additional_data_size);
|
||||
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
|
||||
+ additional_data = std::vector<const uint8_t>(
|
||||
+ additional_data_bits, additional_data_bits + additional_data_size);
|
||||
+ }
|
||||
+
|
||||
// Return to the UI thread to handle opening a new browser tab.
|
||||
@@ -128,7 +131,7 @@ index a04d139f958a7aaef9b96e8c29317ccf7c97f009..29188668a69047b3ad3bebd1f0057565
|
||||
fd_watch_controller_.reset();
|
||||
|
||||
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
|
||||
@@ -737,8 +757,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
@@ -737,8 +760,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
//
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
@@ -139,7 +142,7 @@ index a04d139f958a7aaef9b96e8c29317ccf7c97f009..29188668a69047b3ad3bebd1f0057565
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
||||
@@ -855,7 +877,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
@@ -855,7 +880,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
sizeof(socket_timeout));
|
||||
|
||||
// Found another process, prepare our command line
|
||||
@@ -149,7 +152,7 @@ index a04d139f958a7aaef9b96e8c29317ccf7c97f009..29188668a69047b3ad3bebd1f0057565
|
||||
std::string to_send(kStartToken);
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
||||
@@ -865,11 +888,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
@@ -865,11 +891,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
to_send.append(current_dir.value());
|
||||
|
||||
const std::vector<std::string>& argv = cmd_line.argv();
|
||||
|
||||
@@ -103,10 +103,10 @@ index cea1fb864ab46b4b0eabf1db11a0392d6cd575c1..df033f65d50b088778268827e506963a
|
||||
string mime_type;
|
||||
|
||||
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
|
||||
index 1dbd7aca823fc44a4c4574b51c71ad6b995a7664..9e53423cead44e8bc1cf36e54bfc4b233da15569 100644
|
||||
index 136427ff144f7ce23674f2b18e7deac5f5a5c8dd..94212b48f0a31de898f351c824b289c8c8e4dfad 100644
|
||||
--- a/services/network/url_loader.cc
|
||||
+++ b/services/network/url_loader.cc
|
||||
@@ -497,6 +497,7 @@ URLLoader::URLLoader(
|
||||
@@ -526,6 +526,7 @@ URLLoader::URLLoader(
|
||||
peer_closed_handle_watcher_(FROM_HERE,
|
||||
mojo::SimpleWatcher::ArmingPolicy::MANUAL,
|
||||
base::SequencedTaskRunnerHandle::Get()),
|
||||
@@ -114,7 +114,7 @@ index 1dbd7aca823fc44a4c4574b51c71ad6b995a7664..9e53423cead44e8bc1cf36e54bfc4b23
|
||||
devtools_request_id_(request.devtools_request_id),
|
||||
request_mode_(request.mode),
|
||||
request_credentials_mode_(request.credentials_mode),
|
||||
@@ -640,7 +641,7 @@ URLLoader::URLLoader(
|
||||
@@ -669,7 +670,7 @@ URLLoader::URLLoader(
|
||||
url_request_->SetRequestHeadersCallback(base::BindRepeating(
|
||||
&URLLoader::SetRawRequestHeadersAndNotify, base::Unretained(this)));
|
||||
|
||||
@@ -123,7 +123,7 @@ index 1dbd7aca823fc44a4c4574b51c71ad6b995a7664..9e53423cead44e8bc1cf36e54bfc4b23
|
||||
url_request_->SetResponseHeadersCallback(base::BindRepeating(
|
||||
&URLLoader::SetRawResponseHeaders, base::Unretained(this)));
|
||||
}
|
||||
@@ -1269,6 +1270,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
|
||||
@@ -1299,6 +1300,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
|
||||
response_ = network::mojom::URLResponseHead::New();
|
||||
PopulateResourceResponse(url_request_.get(), is_load_timing_enabled_,
|
||||
options_, response_.get());
|
||||
@@ -144,10 +144,10 @@ index 1dbd7aca823fc44a4c4574b51c71ad6b995a7664..9e53423cead44e8bc1cf36e54bfc4b23
|
||||
// Parse and remove the Trust Tokens response headers, if any are expected,
|
||||
// potentially failing the request if an error occurs.
|
||||
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
|
||||
index d7f278d52db72d18fb779cf409ce8d70c9e32a17..d7e168a184df19d429db2cdc1db40651cb136d68 100644
|
||||
index f40ab2d8823e0b7f71b9ea7932d258022518e632..a36ab764380d542b060979763d20dbf65e1925b5 100644
|
||||
--- a/services/network/url_loader.h
|
||||
+++ b/services/network/url_loader.h
|
||||
@@ -472,6 +472,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
|
||||
@@ -497,6 +497,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
|
||||
std::unique_ptr<ResourceScheduler::ScheduledResourceRequest>
|
||||
resource_scheduler_request_handle_;
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: render_widget_host_view_base.patch
|
||||
... something to do with OSR? and maybe <webview> as well? terrifying.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
|
||||
index 9cddf7c7ba6c91b9800e04dbb2ff3f1553c64c5f..33ec9c904798ea316029bc86c8889b191e4eb6ed 100644
|
||||
index 97e76d6be1ccfcd26470bb6383d81d1b49f6c026..d7bcd8dabe2fe606568f238ba1aa8aa0b5b81270 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
|
||||
@@ -666,6 +666,13 @@ bool RenderWidgetHostViewBase::ScreenRectIsUnstableFor(
|
||||
@@ -673,6 +673,13 @@ bool RenderWidgetHostViewBase::ScreenRectIsUnstableFor(
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Note that we also need to manually update embedder's
|
||||
`api::WebContents::IsFullscreenForTabOrPending` value.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 2f6bf718382ab63d7b736d18e1b69222ab1a8333..63aafafeabfbc004b866662e3a66dea7262888f1 100644
|
||||
index af9f5db7e38bd48d478b346325bb4944fb3bb0d8..d50e2acea1dc704e99100a4dd035f55eaa06e68b 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -5900,6 +5900,15 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
|
||||
@@ -26,3 +26,5 @@ fix_readbarrier_undefined_symbol_error_on_woa_arm64.patch
|
||||
fix_-wunreachable-code-return.patch
|
||||
fix_crash_creating_private_key_with_unsupported_algorithm.patch
|
||||
fix_event_with_invalid_timestamp_in_trace_log.patch
|
||||
test_fix_test-datetime-change-notify_after_daylight_change.patch
|
||||
test_add_fixture_trim_option.patch
|
||||
|
||||
@@ -8,7 +8,7 @@ modules from being used in the renderer process. This should be upstreamed as
|
||||
a customizable error message.
|
||||
|
||||
diff --git a/src/node_binding.cc b/src/node_binding.cc
|
||||
index 8b8389ae53608afedc9cc0ad2a6c8461b7aa893e..e79ddab7d73c6297ef1dc74b2a0ce469bf49e37c 100644
|
||||
index 8b8389ae53608afedc9cc0ad2a6c8461b7aa893e..58aa43010c9d5282955a11c786b5029b0618d2f0 100644
|
||||
--- a/src/node_binding.cc
|
||||
+++ b/src/node_binding.cc
|
||||
@@ -4,6 +4,7 @@
|
||||
@@ -27,7 +27,7 @@ index 8b8389ae53608afedc9cc0ad2a6c8461b7aa893e..e79ddab7d73c6297ef1dc74b2a0ce469
|
||||
+ char errmsg[1024];
|
||||
+ snprintf(errmsg,
|
||||
+ sizeof(errmsg),
|
||||
+ "Loading non-context-aware native module in renderer: '%s', but app.allowRendererProcessReuse is true. See https://github.com/electron/electron/issues/18397.",
|
||||
+ "Loading non-context-aware native module in renderer: '%s'. See https://github.com/electron/electron/issues/18397.",
|
||||
+ *filename);
|
||||
+ env->ThrowError(errmsg);
|
||||
return false;
|
||||
|
||||
49
patches/node/test_add_fixture_trim_option.patch
Normal file
49
patches/node/test_add_fixture_trim_option.patch
Normal file
@@ -0,0 +1,49 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 8 Nov 2021 15:52:17 +0100
|
||||
Subject: test: add fixture trim option
|
||||
|
||||
Fixes a spec failure originating with a strict requirement in BoringSSL
|
||||
that base64 strings be evenly divisible by 4 in their implementation of
|
||||
`NETSCAPE_SPKI_b64_decode`.
|
||||
|
||||
Fixes that issue by trimming the newlines out of the file.
|
||||
|
||||
Upstreamed at https://github.com/nodejs/node/pull/40757.
|
||||
|
||||
diff --git a/test/common/fixtures.js b/test/common/fixtures.js
|
||||
index e5e1d887df525e493989a4aa8df6952a0e5b6c47..2da8aeb6a694e4b45d76bc3908284783d83f6755 100644
|
||||
--- a/test/common/fixtures.js
|
||||
+++ b/test/common/fixtures.js
|
||||
@@ -15,8 +15,13 @@ function readFixtureSync(args, enc) {
|
||||
return fs.readFileSync(fixturesPath(args), enc);
|
||||
}
|
||||
|
||||
-function readFixtureKey(name, enc) {
|
||||
- return fs.readFileSync(fixturesPath('keys', name), enc);
|
||||
+function readFixtureKey(name, enc, trim) {
|
||||
+ let result = fs.readFileSync(fixturesPath('keys', name), enc);
|
||||
+ if (trim) {
|
||||
+ result = Buffer.from(result.toString().trim(), 'utf8');
|
||||
+ }
|
||||
+
|
||||
+ return result;
|
||||
}
|
||||
|
||||
function readFixtureKeys(enc, ...names) {
|
||||
diff --git a/test/parallel/test-crypto-certificate.js b/test/parallel/test-crypto-certificate.js
|
||||
index 4a5f1f149fe6c739f7f1d2ee17df6e61a942d621..a21fbff81c840da29034cb07ae2bd711cfe78b0a 100644
|
||||
--- a/test/parallel/test-crypto-certificate.js
|
||||
+++ b/test/parallel/test-crypto-certificate.js
|
||||
@@ -30,9 +30,9 @@ const { Certificate } = crypto;
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
// Test Certificates
|
||||
-const spkacValid = fixtures.readKey('rsa_spkac.spkac');
|
||||
+const spkacValid = fixtures.readKey('rsa_spkac.spkac', null, true);
|
||||
const spkacChallenge = 'this-is-a-challenge';
|
||||
-const spkacFail = fixtures.readKey('rsa_spkac_invalid.spkac');
|
||||
+const spkacFail = fixtures.readKey('rsa_spkac_invalid.spkac', null, true);
|
||||
const spkacPublicPem = fixtures.readKey('rsa_public.pem');
|
||||
|
||||
function copyArrayBuffer(buf) {
|
||||
@@ -0,0 +1,39 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Piotr Rybak <rybak.piotr@yahoo.com>
|
||||
Date: Sun, 31 Oct 2021 17:58:09 +0900
|
||||
Subject: test: fix test-datetime-change-notify after daylight change
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add standard timezone name for Dublin without daylight saving
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/40684
|
||||
Reviewed-By: Michaël Zasso <targos@protonmail.com>
|
||||
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
|
||||
Reviewed-By: Voltrex <mohammadkeyvanzade94@gmail.com>
|
||||
(cherry picked from commit 747ef34fb0c9c1f2924ab1b79ea000c87e67a8eb)
|
||||
|
||||
diff --git a/test/parallel/test-datetime-change-notify.js b/test/parallel/test-datetime-change-notify.js
|
||||
index 9cd6d7abfd898ac6781b04422362a6b459b7dc2c..01843511907077857be22c9bc7e7f8568fc677d1 100644
|
||||
--- a/test/parallel/test-datetime-change-notify.js
|
||||
+++ b/test/parallel/test-datetime-change-notify.js
|
||||
@@ -18,15 +18,15 @@ const cases = [
|
||||
},
|
||||
{
|
||||
timeZone: 'America/New_York',
|
||||
- expected: /Eastern (Standard|Daylight) Time/,
|
||||
+ expected: /Eastern (?:Standard|Daylight) Time/,
|
||||
},
|
||||
{
|
||||
timeZone: 'America/Los_Angeles',
|
||||
- expected: /Pacific (Standard|Daylight) Time/,
|
||||
+ expected: /Pacific (?:Standard|Daylight) Time/,
|
||||
},
|
||||
{
|
||||
timeZone: 'Europe/Dublin',
|
||||
- expected: /Irish/,
|
||||
+ expected: /Irish Standard Time|Greenwich Mean Time/,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -89,6 +89,7 @@ const LINTERS = [{
|
||||
spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]);
|
||||
}
|
||||
const filter = [
|
||||
'-readability/braces',
|
||||
'-readability/casting',
|
||||
'-whitespace/braces',
|
||||
'-whitespace/indent',
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"parallel/test-crypto-aes-wrap",
|
||||
"parallel/test-crypto-async-sign-verify",
|
||||
"parallel/test-crypto-authenticated-stream",
|
||||
"parallel/test-crypto-certificate",
|
||||
"parallel/test-crypto-des3-wrap",
|
||||
"parallel/test-crypto-dh-stateless",
|
||||
"parallel/test-crypto-ecb",
|
||||
|
||||
@@ -127,11 +127,7 @@ bool ElectronPathProvider(int key, base::FilePath* result) {
|
||||
case DIR_CRASH_DUMPS:
|
||||
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
|
||||
return false;
|
||||
#if defined(OS_MAC) || defined(OS_WIN)
|
||||
cur = cur.Append(FILE_PATH_LITERAL("Crashpad"));
|
||||
#else
|
||||
cur = cur.Append(FILE_PATH_LITERAL("Crash Reports"));
|
||||
#endif
|
||||
create_dir = true;
|
||||
break;
|
||||
case chrome::DIR_APP_DICTIONARIES:
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
|
||||
#if defined(USE_NSS_CERTS)
|
||||
#include "chrome/browser/certificate_manager_model.h"
|
||||
#include "shell/browser/certificate_manager_model.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
@@ -147,7 +147,11 @@ gfx::Rect BrowserView::GetBounds() {
|
||||
}
|
||||
|
||||
void BrowserView::SetBackgroundColor(const std::string& color_name) {
|
||||
view_->SetBackgroundColor(ParseHexColor(color_name));
|
||||
if (!web_contents())
|
||||
return;
|
||||
|
||||
auto* wc = web_contents()->web_contents();
|
||||
wc->SetPageBaseBackgroundColor(ParseHexColor(color_name));
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> BrowserView::GetWebContents(v8::Isolate* isolate) {
|
||||
|
||||
@@ -254,7 +254,7 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
|
||||
ScheduleUnresponsiveEvent(5000);
|
||||
|
||||
// Already closed by renderer.
|
||||
if (!web_contents())
|
||||
if (!web_contents() || !api_web_contents_)
|
||||
return;
|
||||
|
||||
// Required to make beforeunload handler work.
|
||||
|
||||
@@ -1454,7 +1454,9 @@ void WebContents::HandleNewRenderFrame(
|
||||
// Set the background color of RenderWidgetHostView.
|
||||
auto* web_preferences = WebContentsPreferences::From(web_contents());
|
||||
if (web_preferences) {
|
||||
absl::optional<SkColor> color = web_preferences->GetBackgroundColor();
|
||||
bool guest = IsGuest() || type_ == Type::kBrowserView;
|
||||
absl::optional<SkColor> color =
|
||||
guest ? SK_ColorTRANSPARENT : web_preferences->GetBackgroundColor();
|
||||
web_contents()->SetPageBaseBackgroundColor(color);
|
||||
rwhv->SetBackgroundColor(color.value_or(SK_ColorWHITE));
|
||||
}
|
||||
@@ -4028,9 +4030,11 @@ gin::Handle<WebContents> WebContents::CreateFromWebPreferences(
|
||||
absl::optional<SkColor> color =
|
||||
existing_preferences->GetBackgroundColor();
|
||||
web_contents->web_contents()->SetPageBaseBackgroundColor(color);
|
||||
// Because web preferences don't recognize transparency,
|
||||
// only set rwhv background color if a color exists
|
||||
auto* rwhv = web_contents->web_contents()->GetRenderWidgetHostView();
|
||||
if (rwhv)
|
||||
rwhv->SetBackgroundColor(color.value_or(SK_ColorWHITE));
|
||||
if (rwhv && color.has_value())
|
||||
rwhv->SetBackgroundColor(color.value());
|
||||
}
|
||||
} else {
|
||||
// Create one if not.
|
||||
|
||||
@@ -133,8 +133,10 @@ v8::Local<v8::Value> HttpResponseHeadersToV8(
|
||||
net::HttpContentDisposition header(value, std::string());
|
||||
std::string decodedFilename =
|
||||
header.is_attachment() ? " attachment" : " inline";
|
||||
decodedFilename += "; filename=" + header.filename();
|
||||
value = decodedFilename;
|
||||
// The filename must be encased in double quotes for serialization
|
||||
// to happen correctly.
|
||||
std::string filename = "\"" + header.filename() + "\"";
|
||||
value = decodedFilename + "; filename=" + filename;
|
||||
}
|
||||
if (!values)
|
||||
values = response_headers.SetKey(key, base::ListValue());
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/certificate_manager_model.h"
|
||||
#include "shell/browser/certificate_manager_model.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
|
||||
#define CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
|
||||
#ifndef SHELL_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
|
||||
#define SHELL_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -112,4 +112,4 @@ class CertificateManagerModel {
|
||||
DISALLOW_COPY_AND_ASSIGN(CertificateManagerModel);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
|
||||
#endif // SHELL_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
|
||||
@@ -559,7 +559,11 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
enable_crash_reporter = breakpad::IsCrashReporterEnabled();
|
||||
}
|
||||
|
||||
if (enable_crash_reporter) {
|
||||
// Zygote Process gets booted before any JS runs, accessing GetClientId
|
||||
// will end up touching DIR_USER_DATA path provider and this will
|
||||
// configure default value because app.name from browser_init has
|
||||
// not run yet.
|
||||
if (enable_crash_reporter && process_type != ::switches::kZygoteProcess) {
|
||||
std::string switch_value =
|
||||
api::crash_reporter::GetClientId() + ",no_channel";
|
||||
command_line->AppendSwitchASCII(::switches::kEnableCrashReporter,
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "services/device/public/cpp/hid/hid_switches.h"
|
||||
#include "shell/browser/hid/hid_chooser_context.h"
|
||||
#include "shell/browser/hid/hid_chooser_context_factory.h"
|
||||
#include "shell/browser/hid/hid_chooser_controller.h"
|
||||
@@ -103,7 +105,8 @@ const device::mojom::HidDeviceInfo* ElectronHidDelegate::GetDeviceInfo(
|
||||
}
|
||||
|
||||
bool ElectronHidDelegate::IsFidoAllowedForOrigin(const url::Origin& origin) {
|
||||
return false;
|
||||
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDisableHidBlocklist);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::OnDeviceAdded(
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 16,0,0,6
|
||||
PRODUCTVERSION 16,0,0,6
|
||||
FILEVERSION 16,0,0,9
|
||||
PRODUCTVERSION 16,0,0,9
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
|
||||
@@ -290,6 +293,27 @@ void ReadDialogPaths(NSOpenPanel* dialog, std::vector<base::FilePath>* paths) {
|
||||
ReadDialogPathsWithBookmarks(dialog, paths, &ignored_bookmarks);
|
||||
}
|
||||
|
||||
void ResolvePromiseInNextTick(gin_helper::Promise<v8::Local<v8::Value>> promise,
|
||||
v8::Local<v8::Value> value) {
|
||||
// The completionHandler runs inside a transaction commit, and we should
|
||||
// not do any runModal inside it. However since we can not control what
|
||||
// users will run in the microtask, we have to delay the resolution until
|
||||
// next tick, otherwise crash like this may happen:
|
||||
// https://github.com/electron/electron/issues/26884
|
||||
base::PostTask(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<v8::Local<v8::Value>> promise,
|
||||
v8::Global<v8::Value> global) {
|
||||
v8::Isolate* isolate = promise.isolate();
|
||||
v8::Locker locker(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Value> value = global.Get(isolate);
|
||||
promise.Resolve(value);
|
||||
},
|
||||
std::move(promise), v8::Global<v8::Value>(promise.isolate(), value)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
@@ -320,7 +344,6 @@ void OpenDialogCompletion(int chosen,
|
||||
#if defined(MAS_BUILD)
|
||||
dict.Set("bookmarks", std::vector<std::string>());
|
||||
#endif
|
||||
promise.Resolve(dict);
|
||||
} else {
|
||||
std::vector<base::FilePath> paths;
|
||||
dict.Set("canceled", false);
|
||||
@@ -336,8 +359,9 @@ void OpenDialogCompletion(int chosen,
|
||||
ReadDialogPaths(dialog, &paths);
|
||||
dict.Set("filePaths", paths);
|
||||
#endif
|
||||
promise.Resolve(dict);
|
||||
}
|
||||
ResolvePromiseInNextTick(promise.As<v8::Local<v8::Value>>(),
|
||||
dict.GetHandle());
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
@@ -410,7 +434,8 @@ void SaveDialogCompletion(int chosen,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
promise.Resolve(dict);
|
||||
ResolvePromiseInNextTick(promise.As<v8::Local<v8::Value>>(),
|
||||
dict.GetHandle());
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "skia/ext/skia_utils_mac.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
@@ -160,20 +163,26 @@ void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
__block absl::optional<int> id = std::move(settings.id);
|
||||
__block int cancel_id = settings.cancel_id;
|
||||
|
||||
[alert beginSheetModalForWindow:window
|
||||
completionHandler:^(NSModalResponse response) {
|
||||
if (id)
|
||||
GetDialogsMap().erase(*id);
|
||||
// When the alert is cancelled programmatically, the
|
||||
// response would be something like -1000. This currently
|
||||
// only happens when users call CloseMessageBox API, and we
|
||||
// should return cancelId as result.
|
||||
if (response < 0)
|
||||
response = cancel_id;
|
||||
std::move(callback_).Run(
|
||||
response, alert.suppressionButton.state == NSOnState);
|
||||
[alert release];
|
||||
}];
|
||||
auto handler = ^(NSModalResponse response) {
|
||||
if (id)
|
||||
GetDialogsMap().erase(*id);
|
||||
// When the alert is cancelled programmatically, the response would be
|
||||
// something like -1000. This currently only happens when users call
|
||||
// CloseMessageBox API, and we should return cancelId as result.
|
||||
if (response < 0)
|
||||
response = cancel_id;
|
||||
bool suppressed = alert.suppressionButton.state == NSOnState;
|
||||
[alert release];
|
||||
// The completionHandler runs inside a transaction commit, and we should
|
||||
// not do any runModal inside it. However since we can not control what
|
||||
// users will run in the callback, we have to delay running the callback
|
||||
// until next tick, otherwise crash like this may happen:
|
||||
// https://github.com/electron/electron/issues/26884
|
||||
base::PostTask(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(std::move(callback_), response, suppressed));
|
||||
};
|
||||
[alert beginSheetModalForWindow:window completionHandler:handler];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,12 @@ void WebContentsPreferences::SetFromDictionary(
|
||||
web_preferences.Get("disablePopups", &disable_popups_);
|
||||
web_preferences.Get("disableDialogs", &disable_dialogs_);
|
||||
web_preferences.Get("safeDialogs", &safe_dialogs_);
|
||||
// preferences don't save a transparency option,
|
||||
// apply any existing transparency setting to background_color_
|
||||
bool transparent;
|
||||
if (web_preferences.Get(options::kTransparent, &transparent)) {
|
||||
background_color_ = SK_ColorTRANSPARENT;
|
||||
}
|
||||
std::string background_color;
|
||||
if (web_preferences.GetHidden(options::kBackgroundColor, &background_color))
|
||||
background_color_ = ParseHexColor(background_color);
|
||||
|
||||
@@ -51,17 +51,23 @@ bool Clipboard::Has(const std::string& format_string,
|
||||
|
||||
std::string Clipboard::Read(const std::string& format_string) {
|
||||
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
|
||||
ui::ClipboardFormatType format(
|
||||
// Prefer raw platform format names
|
||||
ui::ClipboardFormatType rawFormat(
|
||||
ui::ClipboardFormatType::CustomPlatformType(format_string));
|
||||
|
||||
std::string data;
|
||||
clipboard->ReadData(format, /* data_dst = */ nullptr, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Clipboard::ReadBuffer(const std::string& format_string,
|
||||
gin_helper::Arguments* args) {
|
||||
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
|
||||
bool rawFormatAvailable = clipboard->IsFormatAvailable(
|
||||
rawFormat, ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
|
||||
#if defined(OS_LINUX)
|
||||
if (!rawFormatAvailable) {
|
||||
rawFormatAvailable = clipboard->IsFormatAvailable(
|
||||
rawFormat, ui::ClipboardBuffer::kSelection, /* data_dst = */ nullptr);
|
||||
}
|
||||
#endif
|
||||
if (rawFormatAvailable) {
|
||||
std::string data;
|
||||
clipboard->ReadData(rawFormat, /* data_dst = */ nullptr, &data);
|
||||
return data;
|
||||
}
|
||||
// Otherwise, resolve custom format names
|
||||
std::map<std::string, std::string> custom_format_names;
|
||||
custom_format_names =
|
||||
clipboard->ExtractCustomPlatformNames(ui::ClipboardBuffer::kCopyPaste,
|
||||
@@ -73,12 +79,24 @@ v8::Local<v8::Value> Clipboard::ReadBuffer(const std::string& format_string,
|
||||
/* data_dst = */ nullptr);
|
||||
}
|
||||
#endif
|
||||
CHECK(custom_format_names.find(format_string) != custom_format_names.end());
|
||||
ui::ClipboardFormatType format(ui::ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[format_string]));
|
||||
|
||||
ui::ClipboardFormatType format;
|
||||
if (custom_format_names.find(format_string) != custom_format_names.end()) {
|
||||
format =
|
||||
ui::ClipboardFormatType(ui::ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[format_string]));
|
||||
} else {
|
||||
format = ui::ClipboardFormatType(
|
||||
ui::ClipboardFormatType::CustomPlatformType(format_string));
|
||||
}
|
||||
std::string data;
|
||||
clipboard->ReadData(format, /* data_dst = */ nullptr, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Clipboard::ReadBuffer(const std::string& format_string,
|
||||
gin_helper::Arguments* args) {
|
||||
std::string data = Read(format_string);
|
||||
return node::Buffer::Copy(args->isolate(), data.data(), data.length())
|
||||
.ToLocalChecked();
|
||||
}
|
||||
@@ -95,8 +113,8 @@ void Clipboard::WriteBuffer(const std::string& format,
|
||||
base::span<const uint8_t> payload_span(
|
||||
reinterpret_cast<const uint8_t*>(node::Buffer::Data(buffer)),
|
||||
node::Buffer::Length(buffer));
|
||||
writer.WriteData(base::UTF8ToUTF16(format),
|
||||
mojo_base::BigBuffer(payload_span));
|
||||
writer.WriteUnsafeRawData(base::UTF8ToUTF16(format),
|
||||
mojo_base::BigBuffer(payload_span));
|
||||
}
|
||||
|
||||
void Clipboard::Write(const gin_helper::Dictionary& data,
|
||||
|
||||
@@ -106,6 +106,12 @@ class Promise : public PromiseBase {
|
||||
return resolved.GetHandle();
|
||||
}
|
||||
|
||||
// Convert to another type.
|
||||
template <typename NT>
|
||||
Promise<NT> As() {
|
||||
return Promise<NT>(isolate(), GetInner());
|
||||
}
|
||||
|
||||
// Promise resolution is a microtask
|
||||
// We use the MicrotasksRunner to trigger the running of pending microtasks
|
||||
v8::Maybe<bool> Resolve(const RT& value) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { expect } from 'chai';
|
||||
import { assert, expect } from 'chai';
|
||||
import * as cp from 'child_process';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
@@ -207,6 +207,11 @@ describe('app module', () => {
|
||||
});
|
||||
|
||||
describe('app.requestSingleInstanceLock', () => {
|
||||
interface SingleInstanceLockTestArgs {
|
||||
args: string[];
|
||||
expectedAdditionalData: unknown;
|
||||
}
|
||||
|
||||
it('prevents the second launch of app', async function () {
|
||||
this.timeout(120000);
|
||||
const appPath = path.join(fixturesPath, 'api', 'singleton-data');
|
||||
@@ -220,9 +225,9 @@ describe('app module', () => {
|
||||
expect(code1).to.equal(0);
|
||||
});
|
||||
|
||||
async function testArgumentPassing (fixtureName: string, expectedSecondInstanceData: unknown) {
|
||||
const appPath = path.join(fixturesPath, 'api', fixtureName);
|
||||
const first = cp.spawn(process.execPath, [appPath]);
|
||||
async function testArgumentPassing (testArgs: SingleInstanceLockTestArgs) {
|
||||
const appPath = path.join(fixturesPath, 'api', 'singleton-data');
|
||||
const first = cp.spawn(process.execPath, [appPath, ...testArgs.args]);
|
||||
const firstExited = emittedOnce(first, 'exit');
|
||||
|
||||
// Wait for the first app to boot.
|
||||
@@ -230,16 +235,18 @@ describe('app module', () => {
|
||||
while ((await emittedOnce(firstStdoutLines, 'data')).toString() !== 'started') {
|
||||
// wait.
|
||||
}
|
||||
const data2Promise = emittedOnce(firstStdoutLines, 'data');
|
||||
const additionalDataPromise = emittedOnce(firstStdoutLines, 'data');
|
||||
|
||||
const secondInstanceArgs = [process.execPath, appPath, '--some-switch', 'some-arg'];
|
||||
const secondInstanceArgs = [process.execPath, appPath, ...testArgs.args, '--some-switch', 'some-arg'];
|
||||
const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1));
|
||||
const [code2] = await emittedOnce(second, 'exit');
|
||||
const secondExited = emittedOnce(second, 'exit');
|
||||
|
||||
const [code2] = await secondExited;
|
||||
expect(code2).to.equal(1);
|
||||
const [code1] = await firstExited;
|
||||
expect(code1).to.equal(0);
|
||||
const received = await data2Promise;
|
||||
const [args, additionalData] = received[0].toString('ascii').split('||');
|
||||
const dataFromSecondInstance = await additionalDataPromise;
|
||||
const [args, additionalData] = dataFromSecondInstance[0].toString('ascii').split('||');
|
||||
const secondInstanceArgsReceived: string[] = JSON.parse(args.toString('ascii'));
|
||||
const secondInstanceDataReceived = JSON.parse(additionalData.toString('ascii'));
|
||||
|
||||
@@ -248,12 +255,19 @@ describe('app module', () => {
|
||||
expect(secondInstanceArgsReceived).to.include(arg,
|
||||
`argument ${arg} is missing from received second args`);
|
||||
}
|
||||
expect(secondInstanceDataReceived).to.be.deep.equal(expectedSecondInstanceData,
|
||||
`received data ${JSON.stringify(secondInstanceDataReceived)} is not equal to expected data ${JSON.stringify(expectedSecondInstanceData)}.`);
|
||||
expect(secondInstanceDataReceived).to.be.deep.equal(testArgs.expectedAdditionalData,
|
||||
`received data ${JSON.stringify(secondInstanceDataReceived)} is not equal to expected data ${JSON.stringify(testArgs.expectedAdditionalData)}.`);
|
||||
}
|
||||
|
||||
it('passes arguments to the second-instance event', async () => {
|
||||
const expectedSecondInstanceData = {
|
||||
it('passes arguments to the second-instance event no additional data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: [],
|
||||
expectedAdditionalData: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives JSON object data', async () => {
|
||||
const expectedAdditionalData = {
|
||||
level: 1,
|
||||
testkey: 'testvalue1',
|
||||
inner: {
|
||||
@@ -261,11 +275,64 @@ describe('app module', () => {
|
||||
testkey: 'testvalue2'
|
||||
}
|
||||
};
|
||||
await testArgumentPassing('singleton-data', expectedSecondInstanceData);
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data'],
|
||||
expectedAdditionalData
|
||||
});
|
||||
});
|
||||
|
||||
it('passes arguments to the second-instance event no additional data', async () => {
|
||||
await testArgumentPassing('singleton', null);
|
||||
it('sends and receives numerical data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data', '--data-content=2'],
|
||||
expectedAdditionalData: 2
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives string data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data', '--data-content="data"'],
|
||||
expectedAdditionalData: 'data'
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives boolean data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data', '--data-content=false'],
|
||||
expectedAdditionalData: false
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives array data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data', '--data-content=[2, 3, 4]'],
|
||||
expectedAdditionalData: [2, 3, 4]
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives mixed array data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data', '--data-content=["2", true, 4]'],
|
||||
expectedAdditionalData: ['2', true, 4]
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives null data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data', '--data-content=null'],
|
||||
expectedAdditionalData: null
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot send or receive undefined data', async () => {
|
||||
try {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content="undefined"', '--prevent-default', '--send-data', '--data-content="undefined"'],
|
||||
expectedAdditionalData: undefined
|
||||
});
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
// This is expected.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -110,6 +110,11 @@ describe('BrowserWindow module', () => {
|
||||
await closed;
|
||||
});
|
||||
|
||||
it('should not crash if called after webContents is destroyed', () => {
|
||||
w.webContents.destroy();
|
||||
w.webContents.on('destroyed', () => w.close());
|
||||
});
|
||||
|
||||
it('should emit unload handler', async () => {
|
||||
await w.loadFile(path.join(fixtures, 'api', 'unload.html'));
|
||||
const closed = emittedOnce(w, 'closed');
|
||||
@@ -4157,8 +4162,6 @@ describe('BrowserWindow module', () => {
|
||||
const leaveFullScreen = emittedOnce(w, 'leave-full-screen');
|
||||
w.setFullScreen(false);
|
||||
await leaveFullScreen;
|
||||
|
||||
w.close();
|
||||
});
|
||||
|
||||
it('can be changed with setFullScreen method', async () => {
|
||||
|
||||
@@ -138,460 +138,426 @@ function waitForNewFileInDir (dir: string): Promise<string[]> {
|
||||
|
||||
// TODO(nornagon): Fix tests on linux/arm.
|
||||
ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS)('crashReporter module', function () {
|
||||
// TODO(nornagon): remove linux/breakpad tests once breakpad support is fully
|
||||
// removed.
|
||||
for (const enableLinuxCrashpad of (process.platform === 'linux' ? [false] : [false])) {
|
||||
const withLinuxCrashpad = enableLinuxCrashpad || (process.platform === 'linux');
|
||||
const crashpadExtraArgs = enableLinuxCrashpad ? ['--enable-crashpad'] : [];
|
||||
describe(withLinuxCrashpad ? '(with crashpad)' : '', () => {
|
||||
describe('should send minidump', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
describe('should send minidump', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
// TODO(nornagon): Minidump generation in main/node process on Linux/Arm is
|
||||
// broken (//components/crash prints "Failed to generate minidump"). Figure
|
||||
// out why.
|
||||
ifit(!isLinuxOnArm)('when main process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('browser', crash);
|
||||
expect(crash.mainProcessSpecific).to.equal('mps');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('when a node process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('node', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('node', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
describe('with guid', () => {
|
||||
for (const processType of ['main', 'renderer', 'sandboxed-renderer']) {
|
||||
it(`when ${processType} crashes`, async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, crashpadExtraArgs);
|
||||
runCrashApp(processType, port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.guid).to.be.a('string');
|
||||
});
|
||||
}
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
it('is a consistent id', async () => {
|
||||
let crash1Guid;
|
||||
let crash2Guid;
|
||||
{
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, crashpadExtraArgs);
|
||||
runCrashApp('main', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
// TODO(nornagon): Minidump generation in main/node process on Linux/Arm is
|
||||
// broken (//components/crash prints "Failed to generate minidump"). Figure
|
||||
// out why.
|
||||
ifit(!isLinuxOnArm)('when main process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('browser', crash);
|
||||
expect(crash.mainProcessSpecific).to.equal('mps');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('when a node process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('node', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('node', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
describe('with guid', () => {
|
||||
for (const processType of ['main', 'renderer', 'sandboxed-renderer']) {
|
||||
it(`when ${processType} crashes`, async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp(processType, port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.guid).to.be.a('string');
|
||||
});
|
||||
}
|
||||
|
||||
it('is a consistent id', async () => {
|
||||
let crash1Guid;
|
||||
let crash2Guid;
|
||||
{
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
crash1Guid = crash.guid;
|
||||
}
|
||||
{
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
crash2Guid = crash.guid;
|
||||
}
|
||||
expect(crash2Guid).to.equal(crash1Guid);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with extra parameters', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
it('contains v8 crash keys when a v8 crash occurs', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process._linkedBinding(\'electron_common_v8_util\').triggerFatalErrorForTesting()');
|
||||
});
|
||||
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.prod).to.equal('Electron');
|
||||
expect(crash._productName).to.equal('electron-test-remote-control');
|
||||
expect(crash.process_type).to.equal('renderer');
|
||||
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
|
||||
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
|
||||
function stitchLongCrashParam (crash: any, paramKey: string) {
|
||||
if (crash[paramKey]) return crash[paramKey];
|
||||
let chunk = 1;
|
||||
let stitched = '';
|
||||
while (crash[`${paramKey}__${chunk}`]) {
|
||||
stitched += crash[`${paramKey}__${chunk}`];
|
||||
chunk++;
|
||||
}
|
||||
return stitched;
|
||||
crash1Guid = crash.guid;
|
||||
}
|
||||
|
||||
it('should truncate extra values longer than 5 * 4096 characters', async () => {
|
||||
{
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: { longParam: 'a'.repeat(100000) }
|
||||
});
|
||||
setTimeout(() => process.crash());
|
||||
}, port);
|
||||
runCrashApp('main', port);
|
||||
const crash = await waitForCrash();
|
||||
expect(stitchLongCrashParam(crash, 'longParam')).to.have.lengthOf(160 * 127 + (withLinuxCrashpad ? 159 : 0), 'crash should have truncated longParam');
|
||||
});
|
||||
|
||||
it('should omit extra keys with names longer than the maximum', async () => {
|
||||
const kKeyLengthMax = 39;
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
remotely((port: number, kKeyLengthMax: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: {
|
||||
['a'.repeat(kKeyLengthMax + 10)]: 'value',
|
||||
['b'.repeat(kKeyLengthMax)]: 'value',
|
||||
'not-long': 'not-long-value'
|
||||
}
|
||||
});
|
||||
require('electron').crashReporter.addExtraParameter('c'.repeat(kKeyLengthMax + 10), 'value');
|
||||
setTimeout(() => process.crash());
|
||||
}, port, kKeyLengthMax);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax));
|
||||
expect(crash).to.have.property('b'.repeat(kKeyLengthMax), 'value');
|
||||
expect(crash).to.have.property('not-long', 'not-long-value');
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax));
|
||||
});
|
||||
});
|
||||
|
||||
describe('globalExtra', () => {
|
||||
ifit(!isLinuxOnArm)('should be sent with main process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=globalParam:globalValue', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
it('should be sent with renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--add-global-param=globalParam:globalValue', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
it('should be sent with sandboxed renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--add-global-param=globalParam:globalValue', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in main process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=mainProcessSpecific:global', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.mainProcessSpecific).to.equal('global');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in renderer process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=rendererSpecific:global', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.rendererSpecific).to.equal('global');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): also test crashing main / sandboxed renderers.
|
||||
ifit(!isWindowsOnArm)('should not send a minidump when uploadToServer is false', async () => {
|
||||
const { port, waitForCrash, getCrashes } = await startServer();
|
||||
waitForCrash().then(() => expect.fail('expected not to receive a dump'));
|
||||
await runCrashApp('renderer', port, ['--no-upload', ...crashpadExtraArgs]);
|
||||
// wait a sec in case the crash reporter is about to upload a crash
|
||||
await delay(1000);
|
||||
expect(getCrashes()).to.have.length(0);
|
||||
});
|
||||
|
||||
describe('getUploadedReports', () => {
|
||||
it('returns an array of reports', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
});
|
||||
const reports = await remotely(() => require('electron').crashReporter.getUploadedReports());
|
||||
expect(reports).to.be.an('array');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): re-enable on woa
|
||||
ifdescribe(!isWindowsOnArm)('getLastCrashReport', () => {
|
||||
it('returns the last uploaded report', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
// 0. clear the crash reports directory.
|
||||
const dir = await remotely(() => require('electron').app.getPath('crashDumps'));
|
||||
try {
|
||||
fs.rmdirSync(dir, { recursive: true });
|
||||
fs.mkdirSync(dir);
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// 1. start the crash reporter.
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
// 2. generate a crash in the renderer.
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
await waitForCrash();
|
||||
// 3. get the crash from getLastCrashReport.
|
||||
const firstReport = await repeatedly(
|
||||
() => remotely(() => require('electron').crashReporter.getLastCrashReport())
|
||||
);
|
||||
expect(firstReport).to.not.be.null();
|
||||
expect(firstReport.date).to.be.an.instanceOf(Date);
|
||||
expect((+new Date()) - (+firstReport.date)).to.be.lessThan(30000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getParameters', () => {
|
||||
it('returns all of the current parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: 'http://127.0.0.1',
|
||||
extra: { extra1: 'hi' }
|
||||
});
|
||||
});
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('extra1', 'hi');
|
||||
});
|
||||
|
||||
it('reflects added and removed parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
require('electron').crashReporter.addExtraParameter('hello', 'world');
|
||||
});
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('hello', 'world');
|
||||
}
|
||||
|
||||
await remotely(() => { require('electron').crashReporter.removeExtraParameter('hello'); });
|
||||
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).not.to.have.property('hello');
|
||||
}
|
||||
});
|
||||
|
||||
it('can be called in the renderer', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const rendererParameters = await remotely(async () => {
|
||||
const { crashReporter, BrowserWindow } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://' });
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
await bw.webContents.executeJavaScript('require(\'electron\').crashReporter.addExtraParameter(\'hello\', \'world\')');
|
||||
return bw.webContents.executeJavaScript('require(\'electron\').crashReporter.getParameters()');
|
||||
});
|
||||
if (process.platform === 'linux') {
|
||||
// On Linux, 'getParameters' will also include the global parameters,
|
||||
// because breakpad doesn't support global parameters.
|
||||
expect(rendererParameters).to.have.property('hello', 'world');
|
||||
} else {
|
||||
expect(rendererParameters).to.deep.equal({ hello: 'world' });
|
||||
}
|
||||
});
|
||||
|
||||
it('can be called in a node child process', async () => {
|
||||
function slurp (stream: NodeJS.ReadableStream): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks: Buffer[] = [];
|
||||
stream.on('data', chunk => { chunks.push(chunk); });
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
||||
stream.on('error', e => reject(e));
|
||||
});
|
||||
}
|
||||
// TODO(nornagon): how to enable crashpad in a node child process...?
|
||||
const child = childProcess.fork(path.join(__dirname, 'fixtures', 'module', 'print-crash-parameters.js'), [], { silent: true });
|
||||
const output = await slurp(child.stdout!);
|
||||
expect(JSON.parse(output)).to.deep.equal({ hello: 'world' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('crash dumps directory', () => {
|
||||
it('is set by default', () => {
|
||||
expect(app.getPath('crashDumps')).to.be.a('string');
|
||||
});
|
||||
|
||||
it('is inside the user data dir', () => {
|
||||
expect(app.getPath('crashDumps')).to.include(app.getPath('userData'));
|
||||
});
|
||||
|
||||
function crash (processType: string, remotely: Function) {
|
||||
if (processType === 'main') {
|
||||
return remotely(() => {
|
||||
setTimeout(() => { process.crash(); });
|
||||
});
|
||||
} else if (processType === 'renderer') {
|
||||
return remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
} else if (processType === 'sandboxed-renderer') {
|
||||
const preloadPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'sandbox-preload.js');
|
||||
return remotely((preload: string) => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
}, preloadPath);
|
||||
} else if (processType === 'node') {
|
||||
const crashScriptPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'node-crash.js');
|
||||
return remotely((crashScriptPath: string) => {
|
||||
const { app } = require('electron');
|
||||
const childProcess = require('child_process');
|
||||
const version = app.getVersion();
|
||||
const url = 'http://127.0.0.1';
|
||||
childProcess.fork(crashScriptPath, [url, version], { silent: true });
|
||||
}, crashScriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
const processList = process.platform === 'linux' ? ['main', 'renderer', 'sandboxed-renderer']
|
||||
: ['main', 'renderer', 'sandboxed-renderer', 'node'];
|
||||
for (const crashingProcess of processList) {
|
||||
describe(`when ${crashingProcess} crashes`, () => {
|
||||
it('stores crashes in the crash dump directory when uploadToServer: false', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const crashesDir = await remotely(() => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
});
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin' || (process.platform === 'linux' && withLinuxCrashpad)) {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
if (process.platform === 'linux' && !withLinuxCrashpad) {
|
||||
if (crashingProcess === 'main') {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{8}\.dmp$/);
|
||||
} else {
|
||||
const process = crashingProcess === 'sandboxed-renderer' ? 'renderer' : crashingProcess;
|
||||
const regex = RegExp(`chromium-${process}-minidump-[0-9a-f]{16}.dmp`);
|
||||
expect(newFiles[0]).to.match(regex);
|
||||
}
|
||||
} else {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
}
|
||||
});
|
||||
|
||||
it('respects an overridden crash dump directory', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const crashesDir = path.join(app.getPath('temp'), uuid.v4());
|
||||
const remoteCrashesDir = await remotely((crashesDir: string) => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
app.setPath('crashDumps', crashesDir);
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
}, crashesDir);
|
||||
expect(remoteCrashesDir).to.equal(crashesDir);
|
||||
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin' || (process.platform === 'linux' && withLinuxCrashpad)) {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
if (process.platform === 'linux' && !withLinuxCrashpad) {
|
||||
if (crashingProcess === 'main') {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{8}\.dmp$/);
|
||||
} else {
|
||||
const process = crashingProcess !== 'sandboxed-renderer' ? crashingProcess : 'renderer';
|
||||
const regex = RegExp(`chromium-${process}-minidump-[0-9a-f]{16}.dmp`);
|
||||
expect(newFiles[0]).to.match(regex);
|
||||
}
|
||||
} else {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
crash2Guid = crash.guid;
|
||||
}
|
||||
expect(crash2Guid).to.equal(crash1Guid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('with extra parameters', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer']);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer']);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
it('contains v8 crash keys when a v8 crash occurs', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process._linkedBinding(\'electron_common_v8_util\').triggerFatalErrorForTesting()');
|
||||
});
|
||||
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.prod).to.equal('Electron');
|
||||
expect(crash._productName).to.equal('electron-test-remote-control');
|
||||
expect(crash.process_type).to.equal('renderer');
|
||||
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
|
||||
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
|
||||
function stitchLongCrashParam (crash: any, paramKey: string) {
|
||||
if (crash[paramKey]) return crash[paramKey];
|
||||
let chunk = 1;
|
||||
let stitched = '';
|
||||
while (crash[`${paramKey}__${chunk}`]) {
|
||||
stitched += crash[`${paramKey}__${chunk}`];
|
||||
chunk++;
|
||||
}
|
||||
return stitched;
|
||||
}
|
||||
|
||||
it('should truncate extra values longer than 5 * 4096 characters', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: { longParam: 'a'.repeat(100000) }
|
||||
});
|
||||
setTimeout(() => process.crash());
|
||||
}, port);
|
||||
const crash = await waitForCrash();
|
||||
expect(stitchLongCrashParam(crash, 'longParam')).to.have.lengthOf(160 * 127 + (process.platform === 'linux' ? 159 : 0), 'crash should have truncated longParam');
|
||||
});
|
||||
|
||||
it('should omit extra keys with names longer than the maximum', async () => {
|
||||
const kKeyLengthMax = 39;
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
remotely((port: number, kKeyLengthMax: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: {
|
||||
['a'.repeat(kKeyLengthMax + 10)]: 'value',
|
||||
['b'.repeat(kKeyLengthMax)]: 'value',
|
||||
'not-long': 'not-long-value'
|
||||
}
|
||||
});
|
||||
require('electron').crashReporter.addExtraParameter('c'.repeat(kKeyLengthMax + 10), 'value');
|
||||
setTimeout(() => process.crash());
|
||||
}, port, kKeyLengthMax);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax));
|
||||
expect(crash).to.have.property('b'.repeat(kKeyLengthMax), 'value');
|
||||
expect(crash).to.have.property('not-long', 'not-long-value');
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax));
|
||||
});
|
||||
});
|
||||
|
||||
describe('globalExtra', () => {
|
||||
ifit(!isLinuxOnArm)('should be sent with main process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=globalParam:globalValue']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
it('should be sent with renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--add-global-param=globalParam:globalValue']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
it('should be sent with sandboxed renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--add-global-param=globalParam:globalValue']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in main process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=mainProcessSpecific:global']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.mainProcessSpecific).to.equal('global');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in renderer process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=rendererSpecific:global']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.rendererSpecific).to.equal('global');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): also test crashing main / sandboxed renderers.
|
||||
ifit(!isWindowsOnArm)('should not send a minidump when uploadToServer is false', async () => {
|
||||
const { port, waitForCrash, getCrashes } = await startServer();
|
||||
waitForCrash().then(() => expect.fail('expected not to receive a dump'));
|
||||
await runCrashApp('renderer', port, ['--no-upload']);
|
||||
// wait a sec in case the crash reporter is about to upload a crash
|
||||
await delay(1000);
|
||||
expect(getCrashes()).to.have.length(0);
|
||||
});
|
||||
|
||||
describe('getUploadedReports', () => {
|
||||
it('returns an array of reports', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
});
|
||||
const reports = await remotely(() => require('electron').crashReporter.getUploadedReports());
|
||||
expect(reports).to.be.an('array');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): re-enable on woa
|
||||
ifdescribe(!isWindowsOnArm)('getLastCrashReport', () => {
|
||||
it('returns the last uploaded report', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
// 0. clear the crash reports directory.
|
||||
const dir = await remotely(() => require('electron').app.getPath('crashDumps'));
|
||||
try {
|
||||
fs.rmdirSync(dir, { recursive: true });
|
||||
fs.mkdirSync(dir);
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// 1. start the crash reporter.
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
// 2. generate a crash in the renderer.
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
await waitForCrash();
|
||||
// 3. get the crash from getLastCrashReport.
|
||||
const firstReport = await repeatedly(
|
||||
() => remotely(() => require('electron').crashReporter.getLastCrashReport())
|
||||
);
|
||||
expect(firstReport).to.not.be.null();
|
||||
expect(firstReport.date).to.be.an.instanceOf(Date);
|
||||
expect((+new Date()) - (+firstReport.date)).to.be.lessThan(30000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getParameters', () => {
|
||||
it('returns all of the current parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: 'http://127.0.0.1',
|
||||
extra: { extra1: 'hi' }
|
||||
});
|
||||
});
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('extra1', 'hi');
|
||||
});
|
||||
|
||||
it('reflects added and removed parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
require('electron').crashReporter.addExtraParameter('hello', 'world');
|
||||
});
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('hello', 'world');
|
||||
}
|
||||
|
||||
await remotely(() => { require('electron').crashReporter.removeExtraParameter('hello'); });
|
||||
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).not.to.have.property('hello');
|
||||
}
|
||||
});
|
||||
|
||||
it('can be called in the renderer', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const rendererParameters = await remotely(async () => {
|
||||
const { crashReporter, BrowserWindow } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://' });
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
await bw.webContents.executeJavaScript('require(\'electron\').crashReporter.addExtraParameter(\'hello\', \'world\')');
|
||||
return bw.webContents.executeJavaScript('require(\'electron\').crashReporter.getParameters()');
|
||||
});
|
||||
expect(rendererParameters).to.deep.equal({ hello: 'world' });
|
||||
});
|
||||
|
||||
it('can be called in a node child process', async () => {
|
||||
function slurp (stream: NodeJS.ReadableStream): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks: Buffer[] = [];
|
||||
stream.on('data', chunk => { chunks.push(chunk); });
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
||||
stream.on('error', e => reject(e));
|
||||
});
|
||||
}
|
||||
// TODO(nornagon): how to enable crashpad in a node child process...?
|
||||
const child = childProcess.fork(path.join(__dirname, 'fixtures', 'module', 'print-crash-parameters.js'), [], { silent: true });
|
||||
const output = await slurp(child.stdout!);
|
||||
expect(JSON.parse(output)).to.deep.equal({ hello: 'world' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('crash dumps directory', () => {
|
||||
it('is set by default', () => {
|
||||
expect(app.getPath('crashDumps')).to.be.a('string');
|
||||
});
|
||||
|
||||
it('is inside the user data dir', () => {
|
||||
expect(app.getPath('crashDumps')).to.include(app.getPath('userData'));
|
||||
});
|
||||
|
||||
function crash (processType: string, remotely: Function) {
|
||||
if (processType === 'main') {
|
||||
return remotely(() => {
|
||||
setTimeout(() => { process.crash(); });
|
||||
});
|
||||
} else if (processType === 'renderer') {
|
||||
return remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
} else if (processType === 'sandboxed-renderer') {
|
||||
const preloadPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'sandbox-preload.js');
|
||||
return remotely((preload: string) => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
}, preloadPath);
|
||||
} else if (processType === 'node') {
|
||||
const crashScriptPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'node-crash.js');
|
||||
return remotely((crashScriptPath: string) => {
|
||||
const { app } = require('electron');
|
||||
const childProcess = require('child_process');
|
||||
const version = app.getVersion();
|
||||
const url = 'http://127.0.0.1';
|
||||
childProcess.fork(crashScriptPath, [url, version], { silent: true });
|
||||
}, crashScriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
const processList = process.platform === 'linux' ? ['main', 'renderer', 'sandboxed-renderer']
|
||||
: ['main', 'renderer', 'sandboxed-renderer', 'node'];
|
||||
for (const crashingProcess of processList) {
|
||||
describe(`when ${crashingProcess} crashes`, () => {
|
||||
it('stores crashes in the crash dump directory when uploadToServer: false', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const crashesDir = await remotely(() => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
});
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin' || process.platform === 'linux') {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
});
|
||||
|
||||
it('respects an overridden crash dump directory', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const crashesDir = path.join(app.getPath('temp'), uuid.v4());
|
||||
const remoteCrashesDir = await remotely((crashesDir: string) => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
app.setPath('crashDumps', crashesDir);
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
}, crashesDir);
|
||||
expect(remoteCrashesDir).to.equal(crashesDir);
|
||||
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin' || process.platform === 'linux') {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('start() option validation', () => {
|
||||
it('requires that the submitURL option be specified', () => {
|
||||
|
||||
@@ -354,7 +354,7 @@ describe('webRequest module', () => {
|
||||
|
||||
it('does not change content-disposition header by default', async () => {
|
||||
ses.webRequest.onHeadersReceived((details, callback) => {
|
||||
expect(details.responseHeaders!['content-disposition']).to.deep.equal([' attachment; filename=aa中aa.txt']);
|
||||
expect(details.responseHeaders!['content-disposition']).to.deep.equal([' attachment; filename="aa中aa.txt"']);
|
||||
callback({});
|
||||
});
|
||||
const { data, headers } = await ajax(defaultURL + 'contentDisposition');
|
||||
|
||||
@@ -54,24 +54,26 @@ testStorage(function (
|
||||
syncForRemove, localForRemove,
|
||||
syncForClear, localForClear
|
||||
) {
|
||||
const message = JSON.stringify({
|
||||
runtimeId: chrome.runtime.id,
|
||||
tabId: chrome.devtools.inspectedWindow.tabId,
|
||||
i18nString: chrome.i18n.getMessage('foo', ['bar', 'baz']),
|
||||
storageItems: {
|
||||
local: {
|
||||
set: localForSet,
|
||||
remove: localForRemove,
|
||||
clear: localForClear
|
||||
},
|
||||
sync: {
|
||||
set: syncForSet,
|
||||
remove: syncForRemove,
|
||||
clear: syncForClear
|
||||
setTimeout(() => {
|
||||
const message = JSON.stringify({
|
||||
runtimeId: chrome.runtime.id,
|
||||
tabId: chrome.devtools.inspectedWindow.tabId,
|
||||
i18nString: chrome.i18n.getMessage('foo', ['bar', 'baz']),
|
||||
storageItems: {
|
||||
local: {
|
||||
set: localForSet,
|
||||
remove: localForRemove,
|
||||
clear: localForClear
|
||||
},
|
||||
sync: {
|
||||
set: syncForSet,
|
||||
remove: syncForRemove,
|
||||
clear: syncForClear
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sendMessage = `require('electron').ipcRenderer.send('answer', ${message})`;
|
||||
window.chrome.devtools.inspectedWindow.eval(sendMessage, function () {});
|
||||
const sendMessage = `require('electron').ipcRenderer.send('answer', ${message})`;
|
||||
window.chrome.devtools.inspectedWindow.eval(sendMessage, function () {});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { expect } = require('chai');
|
||||
const path = require('path');
|
||||
const { Buffer } = require('buffer');
|
||||
const { ifdescribe } = require('./spec-helpers');
|
||||
const { ifdescribe, ifit } = require('./spec-helpers');
|
||||
|
||||
const { clipboard, nativeImage } = require('electron');
|
||||
|
||||
@@ -62,14 +62,20 @@ ifdescribe(process.platform !== 'win32' || process.arch !== 'arm64')('clipboard
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform !== 'linux')('clipboard.read()', () => {
|
||||
it('does not crash when reading various custom clipboard types', () => {
|
||||
describe('clipboard.read()', () => {
|
||||
ifit(process.platform !== 'linux')('does not crash when reading various custom clipboard types', () => {
|
||||
const type = process.platform === 'darwin' ? 'NSFilenamesPboardType' : 'FileNameW';
|
||||
|
||||
expect(() => {
|
||||
const result = clipboard.read(type);
|
||||
}).to.not.throw();
|
||||
});
|
||||
it('can read data written with writeBuffer', () => {
|
||||
const testText = 'Testing read';
|
||||
const buffer = Buffer.from(testText, 'utf8');
|
||||
clipboard.writeBuffer('public/utf8-plain-text', buffer);
|
||||
expect(clipboard.read('public/utf8-plain-text')).to.equal(testText);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clipboard.write()', () => {
|
||||
@@ -123,5 +129,16 @@ ifdescribe(process.platform !== 'win32' || process.arch !== 'arm64')('clipboard
|
||||
clipboard.writeBuffer('public/utf8-plain-text', 'hello');
|
||||
}).to.throw(/buffer must be a node Buffer/);
|
||||
});
|
||||
|
||||
ifit(process.platform !== 'win32')('writes a Buffer using a raw format that is used by native apps', function () {
|
||||
const message = 'Hello from Electron!';
|
||||
const buffer = Buffer.from(message);
|
||||
let rawFormat = 'text/plain';
|
||||
if (process.platform === 'darwin') {
|
||||
rawFormat = 'public.utf8-plain-text';
|
||||
}
|
||||
clipboard.writeBuffer(rawFormat, buffer);
|
||||
expect(clipboard.readText()).to.equal(message);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
15
spec/fixtures/api/singleton-data/main.js
vendored
15
spec/fixtures/api/singleton-data/main.js
vendored
@@ -1,10 +1,13 @@
|
||||
const { app } = require('electron');
|
||||
|
||||
// Send data from the second instance to the first instance.
|
||||
const sendAdditionalData = app.commandLine.hasSwitch('send-data');
|
||||
|
||||
app.whenReady().then(() => {
|
||||
console.log('started'); // ping parent
|
||||
});
|
||||
|
||||
const obj = {
|
||||
let obj = {
|
||||
level: 1,
|
||||
testkey: 'testvalue1',
|
||||
inner: {
|
||||
@@ -12,7 +15,15 @@ const obj = {
|
||||
testkey: 'testvalue2'
|
||||
}
|
||||
};
|
||||
const gotTheLock = app.requestSingleInstanceLock(obj);
|
||||
if (app.commandLine.hasSwitch('data-content')) {
|
||||
obj = JSON.parse(app.commandLine.getSwitchValue('data-content'));
|
||||
if (obj === 'undefined') {
|
||||
obj = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const gotTheLock = sendAdditionalData
|
||||
? app.requestSingleInstanceLock(obj) : app.requestSingleInstanceLock();
|
||||
|
||||
app.on('second-instance', (event, args, workingDirectory, data) => {
|
||||
setImmediate(() => {
|
||||
|
||||
4
spec/fixtures/api/singleton/main.js
vendored
4
spec/fixtures/api/singleton/main.js
vendored
@@ -6,9 +6,9 @@ app.whenReady().then(() => {
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
|
||||
app.on('second-instance', (event, args, workingDirectory, data) => {
|
||||
app.on('second-instance', (event, args, workingDirectory) => {
|
||||
setImmediate(() => {
|
||||
console.log([JSON.stringify(args), JSON.stringify(data)].join('||'));
|
||||
console.log(JSON.stringify(args));
|
||||
app.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user