mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
202 Commits
v10.0.0-ni
...
v11.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66d65a6d35 | ||
|
|
4305707ca8 | ||
|
|
8d25847318 | ||
|
|
ad16e6c647 | ||
|
|
80a6adb597 | ||
|
|
7dd365784d | ||
|
|
e59370f541 | ||
|
|
6cfbee9f34 | ||
|
|
2aeaca6f7b | ||
|
|
cadc142d05 | ||
|
|
446100cdfe | ||
|
|
7fd96cd188 | ||
|
|
25a36a43c1 | ||
|
|
b0be3fbb7f | ||
|
|
71a7e1b2e3 | ||
|
|
2a3437e5b5 | ||
|
|
cf74ee3d83 | ||
|
|
99079c3bc7 | ||
|
|
1f23807271 | ||
|
|
82af855579 | ||
|
|
f6e21d59e7 | ||
|
|
4398ecb6c8 | ||
|
|
9106d0c6d5 | ||
|
|
92d9de93a4 | ||
|
|
fd0eaf4507 | ||
|
|
2a6d6d6ea7 | ||
|
|
d330c6f9fe | ||
|
|
82ae18dd1c | ||
|
|
c157311373 | ||
|
|
354ea00f17 | ||
|
|
4c449fbc75 | ||
|
|
3cb833821a | ||
|
|
a69a655386 | ||
|
|
dc3905cef4 | ||
|
|
22e64dc328 | ||
|
|
6bd30bf09e | ||
|
|
c6db47182a | ||
|
|
451086d7f2 | ||
|
|
ee61eb9aa4 | ||
|
|
2cbd091e89 | ||
|
|
af4876296c | ||
|
|
d9d07c65b2 | ||
|
|
6e069a7df8 | ||
|
|
ef3579eae3 | ||
|
|
80e5007c47 | ||
|
|
1c49e4e376 | ||
|
|
8eb198bab4 | ||
|
|
16a3f41fd3 | ||
|
|
605e50269e | ||
|
|
9b4572de44 | ||
|
|
5b80556da2 | ||
|
|
4290555a0d | ||
|
|
1429a8961c | ||
|
|
46f58b28c3 | ||
|
|
6369748a2a | ||
|
|
abf2e9c93d | ||
|
|
b2b5a6a3de | ||
|
|
acb64a2d3c | ||
|
|
3853fd3e99 | ||
|
|
659e79fc08 | ||
|
|
4c77fe318d | ||
|
|
72a089262e | ||
|
|
61a05caa78 | ||
|
|
563a8c83ea | ||
|
|
046a05944a | ||
|
|
0629c6c2ea | ||
|
|
48d1a70b52 | ||
|
|
5c6d8c2b7d | ||
|
|
0f44682be0 | ||
|
|
2376c45443 | ||
|
|
b665eb6d43 | ||
|
|
4e66f1b2c6 | ||
|
|
a54fcde3f4 | ||
|
|
7cc780d077 | ||
|
|
59f9d75324 | ||
|
|
cb6476fd12 | ||
|
|
e1e73fa5f5 | ||
|
|
09c0ee8f87 | ||
|
|
cf284991d8 | ||
|
|
f9fe8a67b9 | ||
|
|
7bf0af7d96 | ||
|
|
7a216a8fab | ||
|
|
673169a7ba | ||
|
|
9c7d73c6d6 | ||
|
|
0b830ba9e5 | ||
|
|
c0182bca15 | ||
|
|
8412aae231 | ||
|
|
b9ac334efc | ||
|
|
379bb174e9 | ||
|
|
178e46cd23 | ||
|
|
66744ecb4d | ||
|
|
a7599a0838 | ||
|
|
d5f642938d | ||
|
|
7274467f73 | ||
|
|
7c7ea141f0 | ||
|
|
c66282a460 | ||
|
|
78fe545d18 | ||
|
|
81d09bea44 | ||
|
|
130b176796 | ||
|
|
2e2e9a3702 | ||
|
|
04be1ac4a1 | ||
|
|
a33c10c3ed | ||
|
|
ce042184de | ||
|
|
b1574f9e59 | ||
|
|
9554d063a5 | ||
|
|
3eade2c228 | ||
|
|
71e2b7151c | ||
|
|
d08cfce6cb | ||
|
|
b2ef299efc | ||
|
|
fc8b49485a | ||
|
|
aa32fbf1d6 | ||
|
|
980e592271 | ||
|
|
c6c022dc46 | ||
|
|
261f385b5e | ||
|
|
6e710a20a6 | ||
|
|
46c9e15182 | ||
|
|
5185501b85 | ||
|
|
27d629abb9 | ||
|
|
9d960e29eb | ||
|
|
a1c55a13e1 | ||
|
|
491caf59c1 | ||
|
|
2d47daa8c4 | ||
|
|
25e4a9431c | ||
|
|
3fa4299939 | ||
|
|
90caa5eac9 | ||
|
|
b1adbf7fd7 | ||
|
|
2fadb73032 | ||
|
|
ac81a39e09 | ||
|
|
83247c48c3 | ||
|
|
5de6adfb84 | ||
|
|
d8594f7a78 | ||
|
|
087567655a | ||
|
|
512e1541c6 | ||
|
|
c36981da8b | ||
|
|
d3fa5ed1e8 | ||
|
|
edc5050a2e | ||
|
|
a5efc387c7 | ||
|
|
0062b126b4 | ||
|
|
969f46a48f | ||
|
|
c6f4573a13 | ||
|
|
95037a7d2a | ||
|
|
5218a6e6ab | ||
|
|
5918dd6e65 | ||
|
|
fbf397e15d | ||
|
|
066b8c5ab3 | ||
|
|
634ab45095 | ||
|
|
a4de915b74 | ||
|
|
4a551bde16 | ||
|
|
b809d104d0 | ||
|
|
4133c71105 | ||
|
|
f67aff2704 | ||
|
|
aa8d81511f | ||
|
|
236c1334e3 | ||
|
|
9f21d09dfd | ||
|
|
9dec16d81e | ||
|
|
b086197968 | ||
|
|
e8ea007104 | ||
|
|
e5da2caa34 | ||
|
|
a1edf5cf94 | ||
|
|
6516110c70 | ||
|
|
3a7775fa73 | ||
|
|
5d88d0ee74 | ||
|
|
7b0a84477a | ||
|
|
f78504515b | ||
|
|
ca6a415626 | ||
|
|
f445e0a797 | ||
|
|
204f001c5d | ||
|
|
dc72f74020 | ||
|
|
2b39111e30 | ||
|
|
2bc85b2579 | ||
|
|
0a026cde0c | ||
|
|
db5cf816b4 | ||
|
|
08f288faf1 | ||
|
|
0dabd5e8c7 | ||
|
|
2029ff1903 | ||
|
|
9bc5e98238 | ||
|
|
762f7bcca2 | ||
|
|
75847a0c5b | ||
|
|
e3c2ec9f7c | ||
|
|
fdf40ce07a | ||
|
|
82924679fe | ||
|
|
f373cc770f | ||
|
|
78d74bf8b4 | ||
|
|
5ed2512881 | ||
|
|
ce77e3327c | ||
|
|
7b55a70a36 | ||
|
|
5bdf97e7ae | ||
|
|
3137d5f011 | ||
|
|
4114281b62 | ||
|
|
21a6062a31 | ||
|
|
df53816eea | ||
|
|
4b23a85475 | ||
|
|
33d6a99d40 | ||
|
|
9d851b8791 | ||
|
|
0a481242f0 | ||
|
|
84888bb1f1 | ||
|
|
8de06f0c57 | ||
|
|
bca071f428 | ||
|
|
de44d28c8e | ||
|
|
3f3a760a01 | ||
|
|
27d7eb3880 | ||
|
|
393b637e9e |
@@ -418,6 +418,7 @@ step-electron-build: &step-electron-build
|
||||
fi
|
||||
cd src
|
||||
ninja -C out/Default electron -j $NUMBER_OF_NINJA_PROCESSES
|
||||
node electron/script/check-symlinks.js
|
||||
|
||||
step-native-unittests-build: &step-native-unittests-build
|
||||
run:
|
||||
@@ -706,6 +707,7 @@ step-hunspell-store: &step-hunspell-store
|
||||
step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
|
||||
run:
|
||||
name: Generate breakpad symbols
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
if [ "$GENERATE_SYMBOLS" == "true" ]; then
|
||||
cd src
|
||||
@@ -830,7 +832,7 @@ step-restore-out-cache: &step-restore-out-cache
|
||||
paths:
|
||||
- ./src/out/Default
|
||||
keys:
|
||||
- v7-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
- v9-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
name: Restoring out cache
|
||||
|
||||
step-set-git-cache-path: &step-set-git-cache-path
|
||||
@@ -854,7 +856,7 @@ step-save-out-cache: &step-save-out-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- ./src/out/Default
|
||||
key: v7-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
key: v9-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
name: Persisting out cache
|
||||
|
||||
step-run-electron-only-hooks: &step-run-electron-only-hooks
|
||||
@@ -1164,8 +1166,8 @@ steps-tests: &steps-tests
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
command: |
|
||||
cd src
|
||||
(cd electron && node script/yarn test --runners=main --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split))
|
||||
(cd electron && node script/yarn test --runners=remote --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split))
|
||||
(cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split))
|
||||
(cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split))
|
||||
- run:
|
||||
name: Check test results existence
|
||||
command: |
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -15,6 +15,7 @@ Contributors guide: https://github.com/electron/electron/blob/master/CONTRIBUTIN
|
||||
- [ ] relevant documentation is changed or added
|
||||
- [ ] PR title follows semantic [commit guidelines](https://github.com/electron/electron/blob/master/docs/development/pull-requests.md#commit-message-guidelines)
|
||||
- [ ] [PR release notes](https://github.com/electron/clerk/blob/master/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/master/README.md#examples).
|
||||
- [ ] This is **NOT A BREAKING CHANGE**. Breaking changes may not be merged to master until 11-x-y is branched.
|
||||
|
||||
#### Release Notes
|
||||
|
||||
|
||||
125
BUILD.gn
125
BUILD.gn
@@ -322,10 +322,10 @@ source_set("electron_lib") {
|
||||
"shell/common/api:mojo",
|
||||
"//base:base_static",
|
||||
"//base/allocator:buildflags",
|
||||
"//chrome/app:command_ids",
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//chrome/services/printing/public/mojom",
|
||||
"//components/certificate_transparency",
|
||||
"//components/crash/core/app",
|
||||
"//components/language/core/browser",
|
||||
"//components/net_log",
|
||||
"//components/network_hints/browser",
|
||||
@@ -347,6 +347,7 @@ source_set("electron_lib") {
|
||||
"//device/bluetooth",
|
||||
"//device/bluetooth/public/cpp",
|
||||
"//gin",
|
||||
"//media/blink:blink",
|
||||
"//media/capture/mojom:video_capture",
|
||||
"//media/mojo/mojom",
|
||||
"//net:extras",
|
||||
@@ -432,6 +433,9 @@ source_set("electron_lib") {
|
||||
"*\bviews/*",
|
||||
]
|
||||
}
|
||||
if (!is_mas_build) {
|
||||
deps += [ "//components/crash/core/app" ]
|
||||
}
|
||||
|
||||
set_sources_assignment_filter(
|
||||
sources_assignment_filter + extra_source_filters)
|
||||
@@ -457,10 +461,13 @@ source_set("electron_lib") {
|
||||
deps += [
|
||||
"//components/remote_cocoa/app_shim",
|
||||
"//content/common:mac_helpers",
|
||||
"//third_party/crashpad/crashpad/client",
|
||||
"//ui/accelerated_widget_mac",
|
||||
]
|
||||
|
||||
if (!is_mas_build) {
|
||||
deps += [ "//third_party/crashpad/crashpad/client" ]
|
||||
}
|
||||
|
||||
libs = [
|
||||
"AVFoundation.framework",
|
||||
"Carbon.framework",
|
||||
@@ -481,19 +488,26 @@ source_set("electron_lib") {
|
||||
sources += [ "shell/browser/api/electron_api_app_mas.mm" ]
|
||||
sources -= [ "shell/browser/auto_updater_mac.mm" ]
|
||||
defines += [ "MAS_BUILD" ]
|
||||
sources -= [
|
||||
"shell/app/electron_crash_reporter_client.cc",
|
||||
"shell/app/electron_crash_reporter_client.h",
|
||||
"shell/common/crash_keys.cc",
|
||||
"shell/common/crash_keys.h",
|
||||
]
|
||||
} else {
|
||||
libs += [
|
||||
"Squirrel.framework",
|
||||
"ReactiveCocoa.framework",
|
||||
"ReactiveObjC.framework",
|
||||
"Mantle.framework",
|
||||
]
|
||||
cflags_objcc = [
|
||||
"-F",
|
||||
rebase_path("external_binaries", root_build_dir),
|
||||
|
||||
deps += [
|
||||
"//third_party/squirrel.mac:reactiveobjc_framework+link",
|
||||
"//third_party/squirrel.mac:squirrel_framework+link",
|
||||
]
|
||||
|
||||
# ReactiveCocoa which is used by Squirrel requires using __weak.
|
||||
cflags_objcc += [ "-fobjc-weak" ]
|
||||
# ReactiveObjC which is used by Squirrel requires using __weak.
|
||||
cflags_objcc = [ "-fobjc-weak" ]
|
||||
}
|
||||
}
|
||||
if (is_linux) {
|
||||
@@ -523,6 +537,16 @@ source_set("electron_lib") {
|
||||
|
||||
sources += filenames.lib_sources_nss
|
||||
sources += [
|
||||
"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",
|
||||
"shell/browser/ui/gtk/app_indicator_icon_menu.h",
|
||||
"shell/browser/ui/gtk/gtk_status_icon.cc",
|
||||
"shell/browser/ui/gtk/gtk_status_icon.h",
|
||||
"shell/browser/ui/gtk/menu_util.cc",
|
||||
"shell/browser/ui/gtk/menu_util.h",
|
||||
"shell/browser/ui/gtk/status_icon.cc",
|
||||
"shell/browser/ui/gtk/status_icon.h",
|
||||
"shell/browser/ui/gtk_util.cc",
|
||||
"shell/browser/ui/gtk_util.h",
|
||||
]
|
||||
@@ -579,15 +603,6 @@ source_set("electron_lib") {
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_remote_module) {
|
||||
sources += [
|
||||
"shell/common/api/remote/remote_callback_freer.cc",
|
||||
"shell/common/api/remote/remote_callback_freer.h",
|
||||
"shell/common/api/remote/remote_object_freer.cc",
|
||||
"shell/common/api/remote/remote_object_freer.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
if (is_component_build && !is_linux) {
|
||||
# On windows the implementation relies on unexported
|
||||
@@ -763,8 +778,10 @@ if (is_mac) {
|
||||
framework_contents = [
|
||||
"Resources",
|
||||
"Libraries",
|
||||
"Helpers",
|
||||
]
|
||||
if (!is_mas_build) {
|
||||
framework_contents += [ "Helpers" ]
|
||||
}
|
||||
public_deps = [
|
||||
":electron_framework_libraries",
|
||||
":electron_lib",
|
||||
@@ -795,8 +812,6 @@ if (is_mac) {
|
||||
}
|
||||
|
||||
ldflags = [
|
||||
"-F",
|
||||
rebase_path("external_binaries", root_build_dir),
|
||||
"-Wl,-install_name,@rpath/$output_name.framework/$output_name",
|
||||
"-rpath",
|
||||
"@loader_path/Libraries",
|
||||
@@ -847,17 +862,49 @@ if (is_mac) {
|
||||
}
|
||||
}
|
||||
|
||||
template("stripped_framework") {
|
||||
action(target_name) {
|
||||
assert(defined(invoker.framework))
|
||||
|
||||
script = "//electron/build/strip_framework.py"
|
||||
|
||||
forward_variables_from(invoker, [ "deps" ])
|
||||
inputs = [ "$root_out_dir/" + invoker.framework ]
|
||||
outputs = [ "$target_out_dir/stripped_frameworks/" + invoker.framework ]
|
||||
|
||||
args = rebase_path(inputs) + rebase_path(outputs)
|
||||
}
|
||||
}
|
||||
|
||||
stripped_framework("stripped_mantle_framework") {
|
||||
framework = "Mantle.framework"
|
||||
deps = [ "//third_party/squirrel.mac:mantle_framework" ]
|
||||
}
|
||||
|
||||
stripped_framework("stripped_reactiveobjc_framework") {
|
||||
framework = "ReactiveObjC.framework"
|
||||
deps = [ "//third_party/squirrel.mac:reactiveobjc_framework" ]
|
||||
}
|
||||
|
||||
stripped_framework("stripped_squirrel_framework") {
|
||||
framework = "Squirrel.framework"
|
||||
deps = [ "//third_party/squirrel.mac:squirrel_framework" ]
|
||||
}
|
||||
|
||||
bundle_data("electron_app_framework_bundle_data") {
|
||||
sources = [ "$root_out_dir/$electron_framework_name.framework" ]
|
||||
if (!is_mas_build) {
|
||||
sources += [
|
||||
"external_binaries/Mantle.framework",
|
||||
"external_binaries/ReactiveCocoa.framework",
|
||||
"external_binaries/Squirrel.framework",
|
||||
]
|
||||
sources += get_target_outputs(":stripped_mantle_framework") +
|
||||
get_target_outputs(":stripped_reactiveobjc_framework") +
|
||||
get_target_outputs(":stripped_squirrel_framework")
|
||||
}
|
||||
outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
|
||||
public_deps = [ ":electron_framework+link" ]
|
||||
public_deps = [
|
||||
":electron_framework+link",
|
||||
":stripped_mantle_framework",
|
||||
":stripped_reactiveobjc_framework",
|
||||
":stripped_squirrel_framework",
|
||||
]
|
||||
|
||||
foreach(helper_params, content_mac_helpers) {
|
||||
sources +=
|
||||
@@ -995,13 +1042,16 @@ if (is_mac) {
|
||||
|
||||
group("electron_symbols") {
|
||||
deps = [
|
||||
":crashpad_handler_syms",
|
||||
":electron_app_syms",
|
||||
":electron_framework_syms",
|
||||
":swiftshader_egl_syms",
|
||||
":swiftshader_gles_syms",
|
||||
]
|
||||
|
||||
if (!is_mas_build) {
|
||||
deps += [ ":crashpad_handler_syms" ]
|
||||
}
|
||||
|
||||
foreach(helper_params, content_mac_helpers) {
|
||||
_helper_target = helper_params[0]
|
||||
deps += [ ":electron_helper_syms_${_helper_target}" ]
|
||||
@@ -1052,7 +1102,9 @@ if (is_mac) {
|
||||
data += [ "$root_out_dir/resources/default_app.asar" ]
|
||||
}
|
||||
|
||||
public_deps = [ "//tools/v8_context_snapshot:v8_context_snapshot" ]
|
||||
if (use_v8_context_snapshot) {
|
||||
public_deps = [ "//tools/v8_context_snapshot:v8_context_snapshot" ]
|
||||
}
|
||||
|
||||
if (is_win) {
|
||||
sources += [
|
||||
@@ -1160,18 +1212,6 @@ test("shell_browser_ui_unittests") {
|
||||
"//ui/base",
|
||||
"//ui/strings",
|
||||
]
|
||||
|
||||
if (is_mac) {
|
||||
# Resolve paths owing to different test executable locations
|
||||
ldflags = [
|
||||
"-F",
|
||||
rebase_path("external_binaries", root_build_dir),
|
||||
"-rpath",
|
||||
"@loader_path",
|
||||
"-rpath",
|
||||
"@executable_path/" + rebase_path("external_binaries", root_build_dir),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
template("dist_zip") {
|
||||
@@ -1269,10 +1309,13 @@ dist_zip("electron_chromedriver_zip") {
|
||||
|
||||
mksnapshot_deps = [
|
||||
":licenses",
|
||||
"//tools/v8_context_snapshot:v8_context_snapshot_generator($v8_snapshot_toolchain)",
|
||||
"//v8:mksnapshot($v8_snapshot_toolchain)",
|
||||
]
|
||||
|
||||
if (use_v8_context_snapshot) {
|
||||
mksnapshot_deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_generator($v8_snapshot_toolchain)" ]
|
||||
}
|
||||
|
||||
group("electron_mksnapshot") {
|
||||
public_deps = mksnapshot_deps
|
||||
}
|
||||
|
||||
30
DEPS
30
DEPS
@@ -5,6 +5,7 @@ gclient_gn_args = [
|
||||
'checkout_android_native_support',
|
||||
'checkout_libaom',
|
||||
'checkout_nacl',
|
||||
'checkout_pgo_profiles',
|
||||
'checkout_oculus_sdk',
|
||||
'checkout_openxr',
|
||||
'checkout_google_benchmark'
|
||||
@@ -12,11 +13,13 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'17da1064b910f4170320ff37e971064916aed1dc',
|
||||
'9ae03ef8f7d4f6ac663f725bcfe70311987652f3',
|
||||
'node_version':
|
||||
'v12.16.3',
|
||||
'v12.18.2',
|
||||
'nan_version':
|
||||
'2c4ee8a32a299eada3cd6e468bbd0a473bfea96d',
|
||||
'squirrel.mac_version':
|
||||
'44468f858ce0d25c27bd5e674abfa104e0119738',
|
||||
|
||||
'boto_version': 'f7574aa6cc2c819430c1f05e9a1a1a666ef8169b',
|
||||
'pyyaml_version': '3.12',
|
||||
@@ -28,6 +31,7 @@ vars = {
|
||||
'nodejs_git': 'https://github.com/nodejs',
|
||||
'requests_git': 'https://github.com/kennethreitz',
|
||||
'yaml_git': 'https://github.com/yaml',
|
||||
'squirrel_git': 'https://github.com/Squirrel',
|
||||
|
||||
# KEEP IN SYNC WITH utils.js FILE
|
||||
'yarn_version': '1.15.2',
|
||||
@@ -42,6 +46,7 @@ vars = {
|
||||
'checkout_chromium': True,
|
||||
'checkout_node': True,
|
||||
'checkout_nan': True,
|
||||
'checkout_pgo_profiles': True,
|
||||
|
||||
# It's only needed to parse the native tests configurations.
|
||||
'checkout_pyyaml': False,
|
||||
@@ -99,6 +104,18 @@ deps = {
|
||||
'url': Var('requests_git') + '/requests.git' + '@' + Var('requests_version'),
|
||||
'condition': 'checkout_requests and process_deps',
|
||||
},
|
||||
'src/third_party/squirrel.mac': {
|
||||
'url': Var("squirrel_git") + '/Squirrel.Mac.git@' + Var("squirrel.mac_version"),
|
||||
'condition': 'process_deps',
|
||||
},
|
||||
'src/third_party/squirrel.mac/vendor/ReactiveObjC': {
|
||||
'url': 'https://github.com/ReactiveCocoa/ReactiveObjC.git@74ab5baccc6f7202c8ac69a8d1e152c29dc1ea76',
|
||||
'condition': 'process_deps'
|
||||
},
|
||||
'src/third_party/squirrel.mac/vendor/Mantle': {
|
||||
'url': 'https://github.com/Mantle/Mantle.git@78d3966b3c331292ea29ec38661b25df0a245948',
|
||||
'condition': 'process_deps',
|
||||
}
|
||||
}
|
||||
|
||||
hooks = [
|
||||
@@ -107,7 +124,7 @@ hooks = [
|
||||
'condition': '(checkout_chromium and apply_patches) and process_deps',
|
||||
'pattern': 'src/electron',
|
||||
'action': [
|
||||
'python',
|
||||
'python3',
|
||||
'src/electron/script/apply_all_patches.py',
|
||||
'src/electron/patches/config.json',
|
||||
],
|
||||
@@ -125,7 +142,7 @@ hooks = [
|
||||
'name': 'electron_npm_deps',
|
||||
'pattern': 'src/electron/package.json',
|
||||
'action': [
|
||||
'python',
|
||||
'python3',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["python", "script/lib/npx.py", "yarn@' + (Var("yarn_version")) + '", "install", "--frozen-lockfile"]);',
|
||||
],
|
||||
@@ -135,7 +152,7 @@ hooks = [
|
||||
'pattern': 'src/electron',
|
||||
'condition': 'checkout_boto and process_deps',
|
||||
'action': [
|
||||
'python',
|
||||
'python3',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron", "vendor", "boto")); subprocess.check_call(["python", "setup.py", "build"]);',
|
||||
],
|
||||
@@ -145,7 +162,7 @@ hooks = [
|
||||
'pattern': 'src/electron',
|
||||
'condition': 'checkout_requests and process_deps',
|
||||
'action': [
|
||||
'python',
|
||||
'python3',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron", "vendor", "requests")); subprocess.check_call(["python", "setup.py", "build"]);',
|
||||
],
|
||||
@@ -154,4 +171,5 @@ hooks = [
|
||||
|
||||
recursedeps = [
|
||||
'src',
|
||||
'src/third_party/squirrel.mac',
|
||||
]
|
||||
|
||||
@@ -1 +1 @@
|
||||
10.0.0-nightly.20200519
|
||||
11.0.0-nightly.20200709
|
||||
@@ -204,7 +204,7 @@ test_script:
|
||||
echo "Skipping tests for $env:GN_CONFIG build"
|
||||
}
|
||||
- cd electron
|
||||
- if "%RUN_TESTS%"=="true" ( echo Running test suite & node script/yarn test -- --enable-logging)
|
||||
- if "%RUN_TESTS%"=="true" ( echo Running test suite & node script/yarn test -- --trace-uncaught --enable-logging)
|
||||
- cd ..
|
||||
- if "%RUN_TESTS%"=="true" ( echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg )
|
||||
- echo "About to verify mksnapshot"
|
||||
|
||||
@@ -71,7 +71,6 @@ steps:
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_TIMEOUT: 120000
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
|
||||
16
build/strip_framework.py
Executable file
16
build/strip_framework.py
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
source = sys.argv[1]
|
||||
dest = sys.argv[2]
|
||||
|
||||
# Ensure any existing framework is removed
|
||||
subprocess.check_output(["rm", "-rf", dest])
|
||||
|
||||
subprocess.check_output(["cp", "-a", source, dest])
|
||||
|
||||
# Strip headers, we do not need to ship them
|
||||
subprocess.check_output(["rm", "-r", os.path.join(dest, 'Headers')])
|
||||
subprocess.check_output(["rm", "-r", os.path.join(dest, 'Versions', 'Current', 'Headers')])
|
||||
@@ -1,3 +1,4 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
@@ -9,6 +10,9 @@ config.output = {
|
||||
filename: path.basename(outPath)
|
||||
}
|
||||
|
||||
const { wrapInitWithProfilingTimeout } = config;
|
||||
delete config.wrapInitWithProfilingTimeout;
|
||||
|
||||
webpack(config, (err, stats) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
@@ -17,6 +21,18 @@ webpack(config, (err, stats) => {
|
||||
console.error(stats.toString('normal'))
|
||||
process.exit(1)
|
||||
} else {
|
||||
if (wrapInitWithProfilingTimeout) {
|
||||
const contents = fs.readFileSync(outPath, 'utf8');
|
||||
const newContents = `function ___electron_webpack_init__() {
|
||||
${contents}
|
||||
};
|
||||
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {
|
||||
setTimeout(___electron_webpack_init__, 0);
|
||||
} else {
|
||||
___electron_webpack_init__();
|
||||
}`;
|
||||
fs.writeFileSync(outPath, newContents);
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '../..')
|
||||
|
||||
@@ -21,7 +22,7 @@ class AccessDependenciesPlugin {
|
||||
}
|
||||
|
||||
const defines = {
|
||||
BUILDFLAG: ''
|
||||
BUILDFLAG: onlyPrintingGraph ? '(a => a)' : ''
|
||||
}
|
||||
|
||||
const buildFlagsPrefix = '--buildflags='
|
||||
@@ -34,8 +35,7 @@ if (buildFlagArg) {
|
||||
for (const line of flagFile.split(/(\r\n|\r|\n)/g)) {
|
||||
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/)
|
||||
if (flagMatch) {
|
||||
const flagName = flagMatch[1];
|
||||
const flagValue = flagMatch[2];
|
||||
const [, flagName, flagValue] = flagMatch;
|
||||
defines[flagName] = JSON.stringify(Boolean(parseInt(flagValue, 10)));
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ const ignoredModules = []
|
||||
if (defines['ENABLE_DESKTOP_CAPTURER'] === 'false') {
|
||||
ignoredModules.push(
|
||||
'@electron/internal/browser/desktop-capturer',
|
||||
'@electron/internal/browser/api/desktop-capturer',
|
||||
'@electron/internal/renderer/api/desktop-capturer'
|
||||
)
|
||||
}
|
||||
@@ -63,16 +64,12 @@ if (defines['ENABLE_VIEWS_API'] === 'false') {
|
||||
)
|
||||
}
|
||||
|
||||
const alias = {}
|
||||
for (const ignoredModule of ignoredModules) {
|
||||
alias[ignoredModule] = path.resolve(electronRoot, 'lib/common/dummy.js')
|
||||
}
|
||||
|
||||
module.exports = ({
|
||||
alwaysHasNode,
|
||||
loadElectronFromAlternateTarget,
|
||||
targetDeletesNodeGlobals,
|
||||
target
|
||||
target,
|
||||
wrapInitWithProfilingTimeout
|
||||
}) => {
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts')
|
||||
if (!fs.existsSync(entry)) {
|
||||
@@ -81,30 +78,36 @@ module.exports = ({
|
||||
|
||||
return ({
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
devtool: false,
|
||||
entry,
|
||||
target: alwaysHasNode ? 'node' : 'web',
|
||||
output: {
|
||||
filename: `${target}.bundle.js`
|
||||
},
|
||||
wrapInitWithProfilingTimeout,
|
||||
resolve: {
|
||||
alias: {
|
||||
...alias,
|
||||
'@electron/internal': path.resolve(electronRoot, 'lib'),
|
||||
'electron': path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts'),
|
||||
// Force timers to resolve to our dependency that doens't use window.postMessage
|
||||
// Force timers to resolve to our dependency that doesn't use window.postMessage
|
||||
'timers': path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
|
||||
},
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
|
||||
loader: 'null-loader',
|
||||
}, {
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve(electronRoot, 'tsconfig.electron.json'),
|
||||
transpileOnly: onlyPrintingGraph,
|
||||
ignoreDiagnostics: [6059]
|
||||
ignoreDiagnostics: [
|
||||
// File '{0}' is not under 'rootDir' '{1}'.
|
||||
6059,
|
||||
]
|
||||
}
|
||||
}]
|
||||
},
|
||||
@@ -115,6 +118,17 @@ module.exports = ({
|
||||
// one of our renderer bundles should import it from the 'timers' package
|
||||
setImmediate: false,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
keep_classnames: true,
|
||||
keep_fnames: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new AccessDependenciesPlugin(),
|
||||
...(targetDeletesNodeGlobals ? [
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithProfilingTimeout: true
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'sandboxed_renderer',
|
||||
alwaysHasNode: false
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ template("webpack_build") {
|
||||
inputs = [
|
||||
invoker.config_file,
|
||||
"//electron/build/webpack/webpack.config.base.js",
|
||||
"//electron/build/webpack/run-compiler.js",
|
||||
"//electron/tsconfig.json",
|
||||
"//electron/yarn.lock",
|
||||
"//electron/typings/internal-ambient.d.ts",
|
||||
|
||||
@@ -72,6 +72,7 @@ static_library("chrome") {
|
||||
]
|
||||
deps = [
|
||||
"//chrome/browser:resource_prefetch_predictor_proto",
|
||||
"//chrome/services/speech:buildflags",
|
||||
"//components/feature_engagement:buildflags",
|
||||
"//components/optimization_guide/proto:optimization_guide_proto",
|
||||
]
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
|
||||
@@ -24,18 +24,18 @@ GlobalMenuBarRegistrarX11* GlobalMenuBarRegistrarX11::GetInstance() {
|
||||
return base::Singleton<GlobalMenuBarRegistrarX11>::get();
|
||||
}
|
||||
|
||||
void GlobalMenuBarRegistrarX11::OnWindowMapped(unsigned long xid) {
|
||||
live_xids_.insert(xid);
|
||||
void GlobalMenuBarRegistrarX11::OnWindowMapped(x11::Window window) {
|
||||
live_windows_.insert(window);
|
||||
|
||||
if (registrar_proxy_)
|
||||
RegisterXID(xid);
|
||||
RegisterXWindow(window);
|
||||
}
|
||||
|
||||
void GlobalMenuBarRegistrarX11::OnWindowUnmapped(unsigned long xid) {
|
||||
void GlobalMenuBarRegistrarX11::OnWindowUnmapped(x11::Window window) {
|
||||
if (registrar_proxy_)
|
||||
UnregisterXID(xid);
|
||||
UnregisterXWindow(window);
|
||||
|
||||
live_xids_.erase(xid);
|
||||
live_windows_.erase(window);
|
||||
}
|
||||
|
||||
GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11()
|
||||
@@ -63,9 +63,9 @@ GlobalMenuBarRegistrarX11::~GlobalMenuBarRegistrarX11() {
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) {
|
||||
void GlobalMenuBarRegistrarX11::RegisterXWindow(x11::Window window) {
|
||||
DCHECK(registrar_proxy_);
|
||||
std::string path = electron::GlobalMenuBarX11::GetPathForWindow(xid);
|
||||
std::string path = electron::GlobalMenuBarX11::GetPathForWindow(window);
|
||||
|
||||
ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
|
||||
// TODO(erg): The mozilla implementation goes to a lot of callback trouble
|
||||
@@ -76,13 +76,13 @@ void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) {
|
||||
// I don't see any reason why we should care if "RegisterWindow" completes or
|
||||
// not.
|
||||
g_dbus_proxy_call(registrar_proxy_, "RegisterWindow",
|
||||
g_variant_new("(uo)", xid, path.c_str()),
|
||||
g_variant_new("(uo)", window, path.c_str()),
|
||||
G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) {
|
||||
void GlobalMenuBarRegistrarX11::UnregisterXWindow(x11::Window window) {
|
||||
DCHECK(registrar_proxy_);
|
||||
std::string path = electron::GlobalMenuBarX11::GetPathForWindow(xid);
|
||||
std::string path = electron::GlobalMenuBarX11::GetPathForWindow(window);
|
||||
|
||||
ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
|
||||
// TODO(erg): The mozilla implementation goes to a lot of callback trouble
|
||||
@@ -93,7 +93,7 @@ void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) {
|
||||
// I don't see any reason why we should care if "UnregisterWindow" completes
|
||||
// or not.
|
||||
g_dbus_proxy_call(registrar_proxy_, "UnregisterWindow",
|
||||
g_variant_new("(u)", xid), G_DBUS_CALL_FLAGS_NONE, -1,
|
||||
g_variant_new("(u)", window), G_DBUS_CALL_FLAGS_NONE, -1,
|
||||
nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
@@ -120,10 +120,10 @@ void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source,
|
||||
|
||||
void GlobalMenuBarRegistrarX11::OnNameOwnerChanged(GObject* /* ignored */,
|
||||
GParamSpec* /* ignored */) {
|
||||
// If the name owner changed, we need to reregister all the live xids with
|
||||
// the system.
|
||||
for (std::set<unsigned long>::const_iterator it = live_xids_.begin();
|
||||
it != live_xids_.end(); ++it) {
|
||||
RegisterXID(*it);
|
||||
// If the name owner changed, we need to reregister all the live x11::Window
|
||||
// with the system.
|
||||
for (std::set<x11::Window>::const_iterator it = live_windows_.begin();
|
||||
it != live_windows_.end(); ++it) {
|
||||
RegisterXWindow(*it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,20 @@
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "ui/base/glib/glib_signal.h"
|
||||
#include "ui/gfx/x/xproto.h"
|
||||
|
||||
// Advertises our menu bars to Unity.
|
||||
//
|
||||
// GlobalMenuBarX11 is responsible for managing the DbusmenuServer for each
|
||||
// XID. We need a separate object to own the dbus channel to
|
||||
// x11::Window. We need a separate object to own the dbus channel to
|
||||
// com.canonical.AppMenu.Registrar and to register/unregister the mapping
|
||||
// between a XID and the DbusmenuServer instance we are offering.
|
||||
// between a x11::Window and the DbusmenuServer instance we are offering.
|
||||
class GlobalMenuBarRegistrarX11 {
|
||||
public:
|
||||
static GlobalMenuBarRegistrarX11* GetInstance();
|
||||
|
||||
void OnWindowMapped(unsigned long xid);
|
||||
void OnWindowUnmapped(unsigned long xid);
|
||||
void OnWindowMapped(x11::Window window);
|
||||
void OnWindowUnmapped(x11::Window window);
|
||||
|
||||
private:
|
||||
friend struct base::DefaultSingletonTraits<GlobalMenuBarRegistrarX11>;
|
||||
@@ -33,8 +34,8 @@ class GlobalMenuBarRegistrarX11 {
|
||||
~GlobalMenuBarRegistrarX11();
|
||||
|
||||
// Sends the actual message.
|
||||
void RegisterXID(unsigned long xid);
|
||||
void UnregisterXID(unsigned long xid);
|
||||
void RegisterXWindow(x11::Window window);
|
||||
void UnregisterXWindow(x11::Window window);
|
||||
|
||||
CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11,
|
||||
void,
|
||||
@@ -49,9 +50,9 @@ class GlobalMenuBarRegistrarX11 {
|
||||
|
||||
GDBusProxy* registrar_proxy_;
|
||||
|
||||
// Window XIDs which want to be registered, but haven't yet been because
|
||||
// x11::Window which want to be registered, but haven't yet been because
|
||||
// we're waiting for the proxy to become available.
|
||||
std::set<unsigned long> live_xids_;
|
||||
std::set<x11::Window> live_windows_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GlobalMenuBarRegistrarX11);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
@@ -29,12 +30,11 @@ function isTrustedSender (webContents: Electron.WebContents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedUrl = new URL(webContents.getURL());
|
||||
const urlPath = process.platform === 'win32'
|
||||
// Strip the prefixed "/" that occurs on windows
|
||||
? path.resolve(parsedUrl.pathname.substr(1))
|
||||
: parsedUrl.pathname;
|
||||
return parsedUrl.protocol === 'file:' && urlPath === indexPath;
|
||||
try {
|
||||
return url.fileURLToPath(webContents.getURL()) === indexPath;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.handle('bootstrap', (event) => {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { app, dialog } from 'electron';
|
||||
import * as electron from 'electron';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
const { app, dialog } = electron;
|
||||
|
||||
type DefaultAppOptions = {
|
||||
file: null | string;
|
||||
@@ -145,13 +146,90 @@ function startRepl () {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// prevent quitting
|
||||
// Prevent quitting.
|
||||
app.on('window-all-closed', () => {});
|
||||
|
||||
const repl = require('repl');
|
||||
repl.start('> ').on('exit', () => {
|
||||
const GREEN = '32';
|
||||
const colorize = (color: string, s: string) => `\x1b[${color}m${s}\x1b[0m`;
|
||||
const electronVersion = colorize(GREEN, `v${process.versions.electron}`);
|
||||
const nodeVersion = colorize(GREEN, `v${process.versions.node}`);
|
||||
|
||||
console.info(`
|
||||
Welcome to the Electron.js REPL \\[._.]/
|
||||
|
||||
You can access all Electron.js modules here as well as Node.js modules.
|
||||
Using: Node.js ${nodeVersion} and Electron.js ${electronVersion}
|
||||
`);
|
||||
|
||||
const { REPLServer } = require('repl');
|
||||
const repl = new REPLServer({
|
||||
prompt: '> '
|
||||
}).on('exit', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
function defineBuiltin (context: any, name: string, getter: Function) {
|
||||
const setReal = (val: any) => {
|
||||
// Deleting the property before re-assigning it disables the
|
||||
// getter/setter mechanism.
|
||||
delete context[name];
|
||||
context[name] = val;
|
||||
};
|
||||
|
||||
Object.defineProperty(context, name, {
|
||||
get: () => {
|
||||
const lib = getter();
|
||||
|
||||
delete context[name];
|
||||
Object.defineProperty(context, name, {
|
||||
get: () => lib,
|
||||
set: setReal,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
return lib;
|
||||
},
|
||||
set: setReal,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
defineBuiltin(repl.context, 'electron', () => electron);
|
||||
for (const api of Object.keys(electron) as (keyof typeof electron)[]) {
|
||||
defineBuiltin(repl.context, api, () => electron[api]);
|
||||
}
|
||||
|
||||
// Copied from node/lib/repl.js. For better DX, we don't want to
|
||||
// show e.g 'contentTracing' at a higher priority than 'const', so
|
||||
// we only trigger custom tab-completion when no common words are
|
||||
// potentially matches.
|
||||
const commonWords = [
|
||||
'async', 'await', 'break', 'case', 'catch', 'const', 'continue',
|
||||
'debugger', 'default', 'delete', 'do', 'else', 'export', 'false',
|
||||
'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'let',
|
||||
'new', 'null', 'return', 'switch', 'this', 'throw', 'true', 'try',
|
||||
'typeof', 'var', 'void', 'while', 'with', 'yield'
|
||||
];
|
||||
|
||||
const electronBuiltins = [...Object.keys(electron), 'original-fs', 'electron'];
|
||||
|
||||
const defaultComplete = repl.completer;
|
||||
repl.completer = (line: string, callback: Function) => {
|
||||
const lastSpace = line.lastIndexOf(' ');
|
||||
const currentSymbol = line.substring(lastSpace + 1, repl.cursor);
|
||||
|
||||
const filterFn = (c: string) => c.startsWith(currentSymbol);
|
||||
const ignores = commonWords.filter(filterFn);
|
||||
const hits = electronBuiltins.filter(filterFn);
|
||||
|
||||
if (!ignores.length && hits.length) {
|
||||
callback(null, [hits, currentSymbol]);
|
||||
} else {
|
||||
defaultComplete.apply(repl, [line, callback]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
|
||||
@@ -92,7 +92,6 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* Electron Releases & Developer Feedback
|
||||
* [Versioning Policy](tutorial/electron-versioning.md)
|
||||
* [Release Timelines](tutorial/electron-timelines.md)
|
||||
* [App Feedback Program](tutorial/app-feedback-program.md)
|
||||
* [Packaging App Source Code with asar](tutorial/application-packaging.md)
|
||||
* [Generating asar Archives](tutorial/application-packaging.md#generating-asar-archives)
|
||||
* [Using asar Archives](tutorial/application-packaging.md#using-asar-archives)
|
||||
|
||||
@@ -140,6 +140,16 @@ this event, such as launching the application for the first time, attempting
|
||||
to re-launch the application when it's already running, or clicking on the
|
||||
application's dock or taskbar icon.
|
||||
|
||||
### Event: 'did-become-active' _macOS_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when mac application become active. Difference from `activate` event is
|
||||
that `did-become-active` is emitted every time the app becomes active, not only
|
||||
when Dock icon is clicked or application is re-launched.
|
||||
|
||||
### Event: 'continue-activity' _macOS_
|
||||
|
||||
Returns:
|
||||
@@ -359,7 +369,7 @@ Returns:
|
||||
|
||||
Emitted when the GPU process crashes or is killed.
|
||||
|
||||
### Event: 'renderer-process-crashed'
|
||||
### Event: 'renderer-process-crashed' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -805,6 +815,20 @@ Returns `String` - Name of the application handling the protocol, or an empty
|
||||
This method returns the application name of the default handler for the protocol
|
||||
(aka URI scheme) of a URL.
|
||||
|
||||
### `app.getApplicationInfoForProtocol(url)` _macOS_ _Windows_
|
||||
|
||||
* `url` String - a URL with the protocol name to check. Unlike the other
|
||||
methods in this family, this accepts an entire URL, including `://` at a
|
||||
minimum (e.g. `https://`).
|
||||
|
||||
Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
* `icon` NativeImage - the display icon of the app handling the protocol.
|
||||
* `path` String - installation path of the app handling the protocol.
|
||||
* `name` String - display name of the app handling the protocol.
|
||||
|
||||
This method returns a promise that contains the application name, icon and path of the default handler for the protocol
|
||||
(aka URI scheme) of a URL.
|
||||
|
||||
### `app.setUserTasks(tasks)` _Windows_
|
||||
|
||||
* `tasks` [Task[]](structures/task.md) - Array of `Task` objects
|
||||
@@ -1222,7 +1246,7 @@ Show the app's about panel options. These options can be overridden with `app.se
|
||||
* `credits` String (optional) _macOS_ _Windows_ - Credit information.
|
||||
* `authors` String[] (optional) _Linux_ - List of app authors.
|
||||
* `website` String (optional) _Linux_ - The app's website.
|
||||
* `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
|
||||
* `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon in a JPEG or PNG file format. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
|
||||
|
||||
Set the about panel options. This will override the values defined in the app's `.plist` file on macOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults.
|
||||
|
||||
@@ -1304,6 +1328,25 @@ app.moveToApplicationsFolder({
|
||||
|
||||
Would mean that if an app already exists in the user directory, if the user chooses to 'Continue Move' then the function would continue with its default behavior and the existing app will be trashed and the active app moved into its place.
|
||||
|
||||
### `app.isSecureKeyboardEntryEnabled()` _macOS_
|
||||
|
||||
Returns `Boolean` - whether `Secure Keyboard Entry` is enabled.
|
||||
|
||||
By default this API will return `false`.
|
||||
|
||||
### `app.setSecureKeyboardEntryEnabled(enabled)` _macOS_
|
||||
|
||||
* `enabled` Boolean - Enable or disable `Secure Keyboard Entry`
|
||||
|
||||
Set the `Secure Keyboard Entry` is enabled in your application.
|
||||
|
||||
By using this API, important information such as password and other sensitive information can be prevented from being intercepted by other processes.
|
||||
|
||||
See [Apple's documentation](https://developer.apple.com/library/archive/technotes/tn2150/_index.html) for more
|
||||
details.
|
||||
|
||||
**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed.
|
||||
|
||||
## Properties
|
||||
|
||||
### `app.accessibilitySupportEnabled` _macOS_ _Windows_
|
||||
@@ -1384,7 +1427,7 @@ in your app's initialization to ensure that your overridden value is used.
|
||||
|
||||
A `Boolean` which when `true` disables the overrides that Electron has in place
|
||||
to ensure renderer processes are restarted on every navigation. The current
|
||||
default value for this property is `false`.
|
||||
default value for this property is `true`.
|
||||
|
||||
The intention is for these overrides to become disabled by default and then at
|
||||
some point in the future this property will be removed. This property impacts
|
||||
|
||||
@@ -387,6 +387,13 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
Default is `true`.
|
||||
* `enableWebSQL` Boolean (optional) - Whether to enable the [WebSQL api](https://www.w3.org/TR/webdatabase/).
|
||||
Default is `true`.
|
||||
* `v8CacheOptions` String (optional) - Enforces the v8 code caching policy
|
||||
used by blink. Accepted values are
|
||||
* `none` - Disables code caching
|
||||
* `code` - Heuristic based code caching
|
||||
* `bypassHeatCheck` - Bypass code caching heuristics but with lazy compilation
|
||||
* `bypassHeatCheckAndEagerCompile` - Same as above except compilation is eager.
|
||||
Default policy is `code`.
|
||||
|
||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||
@@ -685,7 +692,7 @@ Returns `BrowserWindow | null` - The window that owns the given `browserView`. I
|
||||
|
||||
* `id` Integer
|
||||
|
||||
Returns `BrowserWindow` - The window with the given `id`.
|
||||
Returns `BrowserWindow | null` - The window with the given `id`.
|
||||
|
||||
#### `BrowserWindow.addExtension(path)` _Deprecated_
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ for integrated authentication. Without `*` prefix the URL has to match exactly.
|
||||
A comma-separated list of servers for which delegation of user credentials is required.
|
||||
Without `*` prefix the URL has to match exactly.
|
||||
|
||||
### --disable-ntlm-v2
|
||||
|
||||
Disables NTLM v2 for posix platforms, no effect elsewhere.
|
||||
|
||||
### --disable-http-cache
|
||||
|
||||
Disables the disk cache for HTTP requests.
|
||||
|
||||
@@ -52,6 +52,8 @@ Returns:
|
||||
* `method` String - Method name.
|
||||
* `params` any - Event parameters defined by the 'parameters'
|
||||
attribute in the remote debugging protocol.
|
||||
* `sessionId` String - Unique identifier of attached debugging session,
|
||||
will match the value sent from `debugger.sendCommand`.
|
||||
|
||||
Emitted whenever the debugging target issues an instrumentation event.
|
||||
|
||||
@@ -74,11 +76,16 @@ Returns `Boolean` - Whether a debugger is attached to the `webContents`.
|
||||
|
||||
Detaches the debugger from the `webContents`.
|
||||
|
||||
#### `debugger.sendCommand(method[, commandParams])`
|
||||
#### `debugger.sendCommand(method[, commandParams, sessionId])`
|
||||
|
||||
* `method` String - Method name, should be one of the methods defined by the
|
||||
[remote debugging protocol][rdp].
|
||||
* `commandParams` any (optional) - JSON object with request parameters.
|
||||
* `sessionId` String (optional) - send command to the target with associated
|
||||
debugging session id. The initial value can be obtained by sending
|
||||
[Target.attachToTarget][attachToTarget] message.
|
||||
|
||||
[attachToTarget]: https://chromedevtools.github.io/devtools-protocol/tot/Target/#method-attachToTarget
|
||||
|
||||
Returns `Promise<any>` - A promise that resolves with the response defined by
|
||||
the 'returns' attribute of the command description in the remote debugging protocol
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> Access information about media sources that can be used to capture audio and
|
||||
> video from the desktop using the [`navigator.mediaDevices.getUserMedia`] API.
|
||||
|
||||
Process: [Renderer](../glossary.md#renderer-process)
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
The following example shows how to capture video from a desktop window whose
|
||||
title is `Electron`:
|
||||
@@ -72,6 +72,50 @@ const constraints = {
|
||||
}
|
||||
```
|
||||
|
||||
This example shows how to capture a video from a [WebContents](web-contents.md)
|
||||
|
||||
```javascript
|
||||
// In the renderer process.
|
||||
const { desktopCapturer, remote } = require('electron')
|
||||
|
||||
desktopCapturer.getMediaSourceIdForWebContents(remote.getCurrentWebContents().id).then(async mediaSourceId => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'tab',
|
||||
chromeMediaSourceId: mediaSourceId
|
||||
}
|
||||
},
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'tab',
|
||||
chromeMediaSourceId: mediaSourceId,
|
||||
minWidth: 1280,
|
||||
maxWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxHeight: 720
|
||||
}
|
||||
}
|
||||
})
|
||||
handleStream(stream)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
})
|
||||
|
||||
function handleStream (stream) {
|
||||
const video = document.querySelector('video')
|
||||
video.srcObject = stream
|
||||
video.onloadedmetadata = (e) => video.play()
|
||||
}
|
||||
|
||||
function handleError (e) {
|
||||
console.log(e)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
The `desktopCapturer` module has the following methods:
|
||||
@@ -94,6 +138,15 @@ Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`Desktop
|
||||
**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher,
|
||||
which can detected by [`systemPreferences.getMediaAccessStatus`].
|
||||
|
||||
### `desktopCapturer.getMediaSourceIdForWebContents(webContentsId)`
|
||||
|
||||
* `webContentsId` number - Id of the WebContents to get stream of
|
||||
|
||||
Returns `Promise<string>` - Resolves with the identifier of a WebContents stream, this identifier can be
|
||||
used with [`navigator.mediaDevices.getUserMedia`].
|
||||
The identifier is **only valid for 10 seconds**.
|
||||
The identifier may be empty if not requested from a renderer process.
|
||||
|
||||
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
|
||||
[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-macos
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ See [`Menu`](menu.md) for examples.
|
||||
* `click` Function (optional) - Will be called with
|
||||
`click(menuItem, browserWindow, event)` when the menu item is clicked.
|
||||
* `menuItem` MenuItem
|
||||
* `browserWindow` [BrowserWindow](browser-window.md)
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) | undefined - This will not be defined if no window is open.
|
||||
* `event` [KeyboardEvent](structures/keyboard-event.md)
|
||||
* `role` String (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
`click` property will be ignored. See [roles](#roles).
|
||||
@@ -69,6 +69,7 @@ a `type`.
|
||||
The `role` property can have following values:
|
||||
|
||||
* `undo`
|
||||
* `about` - Trigger a native about panel (custom message box on Window, which does not provide its own).
|
||||
* `redo`
|
||||
* `cut`
|
||||
* `copy`
|
||||
@@ -94,7 +95,6 @@ The `role` property can have following values:
|
||||
The following additional roles are available on _macOS_:
|
||||
|
||||
* `appMenu` - Whole default "App" menu (About, Services, etc.)
|
||||
* `about` - Map to the `orderFrontStandardAboutPanel` action.
|
||||
* `hide` - Map to the `hide` action.
|
||||
* `hideOthers` - Map to the `hideOtherApplications` action.
|
||||
* `unhide` - Map to the `unhideAllApplications` action.
|
||||
|
||||
@@ -96,7 +96,7 @@ Appends the `menuItem` to the menu.
|
||||
|
||||
* `id` String
|
||||
|
||||
Returns `MenuItem` the item with the specified `id`
|
||||
Returns `MenuItem | null` the item with the specified `id`
|
||||
|
||||
#### `menu.insert(pos, menuItem)`
|
||||
|
||||
|
||||
@@ -266,9 +266,13 @@ image instead of a copy, so you _must_ ensure that the associated
|
||||
|
||||
Returns `Boolean` - Whether the image is empty.
|
||||
|
||||
#### `image.getSize()`
|
||||
#### `image.getSize([scaleFactor])`
|
||||
|
||||
Returns [`Size`](structures/size.md)
|
||||
* `scaleFactor` Double (optional) - Defaults to 1.0.
|
||||
|
||||
Returns [`Size`](structures/size.md).
|
||||
|
||||
If `scaleFactor` is passed, this will return the size corresponding to the image representation most closely matching the passed value.
|
||||
|
||||
#### `image.setTemplateImage(option)`
|
||||
|
||||
@@ -303,10 +307,18 @@ Returns `NativeImage` - The resized image.
|
||||
If only the `height` or the `width` are specified then the current aspect ratio
|
||||
will be preserved in the resized image.
|
||||
|
||||
#### `image.getAspectRatio()`
|
||||
#### `image.getAspectRatio([scaleFactor])`
|
||||
|
||||
* `scaleFactor` Double (optional) - Defaults to 1.0.
|
||||
|
||||
Returns `Float` - The image's aspect ratio.
|
||||
|
||||
If `scaleFactor` is passed, this will return the aspect ratio corresponding to the image representation most closely matching the passed value.
|
||||
|
||||
#### `image.getScaleFactors()`
|
||||
|
||||
Returns `Float[]` - An array of all scale factors corresponding to representations for a given nativeImage.
|
||||
|
||||
#### `image.addRepresentation(options)`
|
||||
|
||||
* `options` Object
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
# protocol (NetworkService) (Draft)
|
||||
|
||||
This document describes the new protocol APIs based on the [NetworkService](https://www.chromium.org/servicification).
|
||||
|
||||
We don't currently have an estimate of when we will enable the `NetworkService` by
|
||||
default in Electron, but as Chromium is already removing non-`NetworkService`
|
||||
code, we will probably switch before Electron 10.
|
||||
|
||||
The content of this document should be moved to `protocol.md` after we have
|
||||
enabled the `NetworkService` by default in Electron.
|
||||
|
||||
> Register a custom protocol and intercept existing protocol requests.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)
|
||||
|
||||
An example of implementing a protocol that has the same effect as the
|
||||
`file://` protocol:
|
||||
|
||||
```javascript
|
||||
const { app, protocol } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const url = request.url.substr(7)
|
||||
callback({ path: path.normalize(`${__dirname}/${url}`) })
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Note:** All methods unless specified can only be used after the `ready` event
|
||||
of the `app` module gets emitted.
|
||||
|
||||
## Using `protocol` with a custom `partition` or `session`
|
||||
|
||||
A protocol is registered to a specific Electron [`session`](./session.md)
|
||||
object. If you don't specify a session, then your `protocol` will be applied to
|
||||
the default session that Electron uses. However, if you define a `partition` or
|
||||
`session` on your `browserWindow`'s `webPreferences`, then that window will use
|
||||
a different session and your custom protocol will not work if you just use
|
||||
`electron.protocol.XXX`.
|
||||
|
||||
To have your custom protocol work in combination with a custom session, you need
|
||||
to register it to that session explicitly.
|
||||
|
||||
```javascript
|
||||
const { session, app, protocol } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const partition = 'persist:example'
|
||||
const ses = session.fromPartition(partition)
|
||||
|
||||
ses.protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const url = request.url.substr(7)
|
||||
callback({ path: path.normalize(`${__dirname}/${url}`) })
|
||||
})
|
||||
|
||||
mainWindow = new BrowserWindow({ webPreferences: { partition } })
|
||||
})
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
The `protocol` module has the following methods:
|
||||
|
||||
### `protocol.registerSchemesAsPrivileged(customSchemes)`
|
||||
|
||||
* `customSchemes` [CustomScheme[]](structures/custom-scheme.md)
|
||||
|
||||
**Note:** This method can only be used before the `ready` event of the `app`
|
||||
module gets emitted and can be called only once.
|
||||
|
||||
Registers the `scheme` as standard, secure, bypasses content security policy for
|
||||
resources, allows registering ServiceWorker and supports fetch API. Specify a
|
||||
privilege with the value of `true` to enable the capability.
|
||||
|
||||
An example of registering a privileged scheme, that bypasses Content Security
|
||||
Policy:
|
||||
|
||||
```javascript
|
||||
const { protocol } = require('electron')
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'foo', privileges: { bypassCSP: true } }
|
||||
])
|
||||
```
|
||||
|
||||
A standard scheme adheres to what RFC 3986 calls [generic URI
|
||||
syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and
|
||||
`https` are standard schemes, while `file` is not.
|
||||
|
||||
Registering a scheme as standard allows relative and absolute resources to
|
||||
be resolved correctly when served. Otherwise the scheme will behave like the
|
||||
`file` protocol, but without the ability to resolve relative URLs.
|
||||
|
||||
For example when you load following page with custom protocol without
|
||||
registering it as standard scheme, the image will not be loaded because
|
||||
non-standard schemes can not recognize relative URLs:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<img src='test.png'>
|
||||
</body>
|
||||
```
|
||||
|
||||
Registering a scheme as standard will allow access to files through the
|
||||
[FileSystem API][file-system-api]. Otherwise the renderer will throw a security
|
||||
error for the scheme.
|
||||
|
||||
By default web storage apis (localStorage, sessionStorage, webSQL, indexedDB,
|
||||
cookies) are disabled for non standard schemes. So in general if you want to
|
||||
register a custom protocol to replace the `http` protocol, you have to register
|
||||
it as a standard scheme.
|
||||
|
||||
### `protocol.registerFileProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Registers a protocol of `scheme` that will send a file as the response. The
|
||||
`handler` will be called with `request` and `callback` where `request` is
|
||||
an incoming request for the `scheme`.
|
||||
|
||||
To handle the `request`, the `callback` should be called with either the file's
|
||||
path or an object that has a `path` property, e.g. `callback(filePath)` or
|
||||
`callback({ path: filePath })`. The `filePath` must be an absolute path.
|
||||
|
||||
By default the `scheme` is treated like `http:`, which is parsed differently
|
||||
from protocols that follow the "generic URI syntax" like `file:`.
|
||||
|
||||
### `protocol.registerBufferProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (Buffer | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Registers a protocol of `scheme` that will send a `Buffer` as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with either a `Buffer` object or an object that has the `data`
|
||||
property.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
protocol.registerBufferProtocol('atom', (request, callback) => {
|
||||
callback({ mimeType: 'text/html', data: Buffer.from('<h5>Response</h5>') })
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.registerStringProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Registers a protocol of `scheme` that will send a `String` as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with either a `String` or an object that has the `data`
|
||||
property.
|
||||
|
||||
### `protocol.registerHttpProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` ProtocolResponse
|
||||
|
||||
Registers a protocol of `scheme` that will send an HTTP request as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with an object that has the `url` property.
|
||||
|
||||
### `protocol.registerStreamProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Registers a protocol of `scheme` that will send a stream as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the
|
||||
`callback` should be called with either a [`ReadableStream`](https://nodejs.org/api/stream.html#stream_class_stream_readable) object or an object that
|
||||
has the `data` property.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const { protocol } = require('electron')
|
||||
const { PassThrough } = require('stream')
|
||||
|
||||
function createStream (text) {
|
||||
const rv = new PassThrough() // PassThrough is also a Readable stream
|
||||
rv.push(text)
|
||||
rv.push(null)
|
||||
return rv
|
||||
}
|
||||
|
||||
protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||
callback({
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'content-type': 'text/html'
|
||||
},
|
||||
data: createStream('<h5>Response</h5>')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
It is possible to pass any object that implements the readable stream API (emits
|
||||
`data`/`end`/`error` events). For example, here's how a file could be returned:
|
||||
|
||||
```javascript
|
||||
protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||
callback(fs.createReadStream('index.html'))
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.unregisterProtocol(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Unregisters the custom protocol of `scheme`.
|
||||
|
||||
### `protocol.isProtocolRegistered(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Returns `Boolean` - Whether `scheme` is already registered.
|
||||
|
||||
### `protocol.interceptFileProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a file as a response.
|
||||
|
||||
### `protocol.interceptStringProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a `String` as a response.
|
||||
|
||||
### `protocol.interceptBufferProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (Buffer | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a `Buffer` as a response.
|
||||
|
||||
### `protocol.interceptHttpProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` ProtocolResponse
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a new HTTP request as a response.
|
||||
|
||||
### `protocol.interceptStreamProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Same as `protocol.registerStreamProtocol`, except that it replaces an existing
|
||||
protocol handler.
|
||||
|
||||
### `protocol.uninterceptProtocol(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Remove the interceptor installed for `scheme` and restore its original handler.
|
||||
|
||||
### `protocol.isProtocolIntercepted(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Returns `Boolean` - Whether `scheme` is already intercepted.
|
||||
|
||||
[file-system-api]: https://developer.mozilla.org/en-US/docs/Web/API/LocalFileSystem
|
||||
@@ -15,8 +15,6 @@ app.whenReady().then(() => {
|
||||
protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const url = request.url.substr(7)
|
||||
callback({ path: path.normalize(`${__dirname}/${url}`) })
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
})
|
||||
```
|
||||
@@ -26,9 +24,15 @@ of the `app` module gets emitted.
|
||||
|
||||
## Using `protocol` with a custom `partition` or `session`
|
||||
|
||||
A protocol is registered to a specific Electron [`session`](./session.md) object. If you don't specify a session, then your `protocol` will be applied to the default session that Electron uses. However, if you define a `partition` or `session` on your `browserWindow`'s `webPreferences`, then that window will use a different session and your custom protocol will not work if you just use `electron.protocol.XXX`.
|
||||
A protocol is registered to a specific Electron [`session`](./session.md)
|
||||
object. If you don't specify a session, then your `protocol` will be applied to
|
||||
the default session that Electron uses. However, if you define a `partition` or
|
||||
`session` on your `browserWindow`'s `webPreferences`, then that window will use
|
||||
a different session and your custom protocol will not work if you just use
|
||||
`electron.protocol.XXX`.
|
||||
|
||||
To have your custom protocol work in combination with a custom session, you need to register it to that session explicitly.
|
||||
To have your custom protocol work in combination with a custom session, you need
|
||||
to register it to that session explicitly.
|
||||
|
||||
```javascript
|
||||
const { session, app, protocol } = require('electron')
|
||||
@@ -41,17 +45,9 @@ app.whenReady().then(() => {
|
||||
ses.protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
const url = request.url.substr(7)
|
||||
callback({ path: path.normalize(`${__dirname}/${url}`) })
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
partition: partition
|
||||
}
|
||||
})
|
||||
mainWindow = new BrowserWindow({ webPreferences: { partition } })
|
||||
})
|
||||
```
|
||||
|
||||
@@ -63,15 +59,15 @@ The `protocol` module has the following methods:
|
||||
|
||||
* `customSchemes` [CustomScheme[]](structures/custom-scheme.md)
|
||||
|
||||
|
||||
**Note:** This method can only be used before the `ready` event of the `app`
|
||||
module gets emitted and can be called only once.
|
||||
|
||||
Registers the `scheme` as standard, secure, bypasses content security policy for resources,
|
||||
allows registering ServiceWorker and supports fetch API.
|
||||
Registers the `scheme` as standard, secure, bypasses content security policy for
|
||||
resources, allows registering ServiceWorker, supports fetch API, and streaming
|
||||
video/audio. Specify a privilege with the value of `true` to enable the capability.
|
||||
|
||||
Specify a privilege with the value of `true` to enable the capability.
|
||||
An example of registering a privileged scheme, with bypassing Content Security Policy:
|
||||
An example of registering a privileged scheme, that bypasses Content Security
|
||||
Policy:
|
||||
|
||||
```javascript
|
||||
const { protocol } = require('electron')
|
||||
@@ -84,7 +80,7 @@ A standard scheme adheres to what RFC 3986 calls [generic URI
|
||||
syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and
|
||||
`https` are standard schemes, while `file` is not.
|
||||
|
||||
Registering a scheme as standard, will allow relative and absolute resources to
|
||||
Registering a scheme as standard allows relative and absolute resources to
|
||||
be resolved correctly when served. Otherwise the scheme will behave like the
|
||||
`file` protocol, but without the ability to resolve relative URLs.
|
||||
|
||||
@@ -102,168 +98,107 @@ Registering a scheme as standard will allow access to files through the
|
||||
[FileSystem API][file-system-api]. Otherwise the renderer will throw a security
|
||||
error for the scheme.
|
||||
|
||||
By default web storage apis (localStorage, sessionStorage, webSQL, indexedDB, cookies)
|
||||
are disabled for non standard schemes. So in general if you want to register a
|
||||
custom protocol to replace the `http` protocol, you have to register it as a standard scheme.
|
||||
By default web storage apis (localStorage, sessionStorage, webSQL, indexedDB,
|
||||
cookies) are disabled for non standard schemes. So in general if you want to
|
||||
register a custom protocol to replace the `http` protocol, you have to register
|
||||
it as a standard scheme.
|
||||
|
||||
`protocol.registerSchemesAsPrivileged` can be used to replicate the functionality of the previous `protocol.registerStandardSchemes`, `webFrame.registerURLSchemeAs*` and `protocol.registerServiceWorkerSchemes` functions that existed prior to Electron 5.0.0, for example:
|
||||
Protocols that use streams (http and stream protocols) should set `stream: true`.
|
||||
The `<video>` and `<audio>` HTML elements expect protocols to buffer their
|
||||
responses by default. The `stream` flag configures those elements to correctly
|
||||
expect streaming responses.
|
||||
|
||||
**before (<= v4.x)**
|
||||
```javascript
|
||||
// Main
|
||||
protocol.registerStandardSchemes(['scheme1', 'scheme2'], { secure: true })
|
||||
// Renderer
|
||||
webFrame.registerURLSchemeAsPrivileged('scheme1', { secure: true })
|
||||
webFrame.registerURLSchemeAsPrivileged('scheme2', { secure: true })
|
||||
```
|
||||
|
||||
**after (>= v5.x)**
|
||||
```javascript
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'scheme1', privileges: { standard: true, secure: true } },
|
||||
{ scheme: 'scheme2', privileges: { standard: true, secure: true } }
|
||||
])
|
||||
```
|
||||
|
||||
### `protocol.registerFileProtocol(scheme, handler[, completion])`
|
||||
### `protocol.registerFileProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `filePath` String | [FilePathWithHeaders](structures/file-path-with-headers.md) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Registers a protocol of `scheme` that will send the file as a response. The
|
||||
`handler` will be called with `handler(request, callback)` when a `request` is
|
||||
going to be created with `scheme`. `completion` will be called with
|
||||
`completion(null)` when `scheme` is successfully registered or
|
||||
`completion(error)` when failed.
|
||||
Returns `Boolean` - Whether the protocol was successfully registered
|
||||
|
||||
Registers a protocol of `scheme` that will send a file as the response. The
|
||||
`handler` will be called with `request` and `callback` where `request` is
|
||||
an incoming request for the `scheme`.
|
||||
|
||||
To handle the `request`, the `callback` should be called with either the file's
|
||||
path or an object that has a `path` property, e.g. `callback(filePath)` or
|
||||
`callback({ path: filePath })`. The object may also have a `headers` property
|
||||
which gives a map of headers to values for the response headers, e.g.
|
||||
`callback({ path: filePath, headers: {"Content-Security-Policy": "default-src 'none'"]})`.
|
||||
|
||||
When `callback` is called with nothing, a number, or an object that has an
|
||||
`error` property, the `request` will fail with the `error` number you
|
||||
specified. For the available error numbers you can use, please see the
|
||||
[net error list][net-error].
|
||||
`callback({ path: filePath })`. The `filePath` must be an absolute path.
|
||||
|
||||
By default the `scheme` is treated like `http:`, which is parsed differently
|
||||
than protocols that follow the "generic URI syntax" like `file:`.
|
||||
from protocols that follow the "generic URI syntax" like `file:`.
|
||||
|
||||
### `protocol.registerBufferProtocol(scheme, handler[, completion])`
|
||||
### `protocol.registerBufferProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `buffer` (Buffer | [MimeTypedBuffer](structures/mime-typed-buffer.md)) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (Buffer | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully registered
|
||||
|
||||
Registers a protocol of `scheme` that will send a `Buffer` as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with either a `Buffer` object or an object that has the `data`,
|
||||
`mimeType`, and `charset` properties.
|
||||
should be called with either a `Buffer` object or an object that has the `data`
|
||||
property.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const { protocol } = require('electron')
|
||||
|
||||
protocol.registerBufferProtocol('atom', (request, callback) => {
|
||||
callback({ mimeType: 'text/html', data: Buffer.from('<h5>Response</h5>') })
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.registerStringProtocol(scheme, handler[, completion])`
|
||||
### `protocol.registerStringProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `data` (String | [StringProtocolResponse](structures/string-protocol-response.md)) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully registered
|
||||
|
||||
Registers a protocol of `scheme` that will send a `String` as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with either a `String` or an object that has the `data`,
|
||||
`mimeType`, and `charset` properties.
|
||||
should be called with either a `String` or an object that has the `data`
|
||||
property.
|
||||
|
||||
### `protocol.registerHttpProtocol(scheme, handler[, completion])`
|
||||
### `protocol.registerHttpProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `redirectRequest` Object
|
||||
* `url` String
|
||||
* `method` String (optional)
|
||||
* `session` Session | null (optional)
|
||||
* `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` ProtocolResponse
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully registered
|
||||
|
||||
Registers a protocol of `scheme` that will send an HTTP request as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the `callback`
|
||||
should be called with a `redirectRequest` object that has the `url`, `method`,
|
||||
`referrer`, `uploadData` and `session` properties.
|
||||
should be called with an object that has the `url` property.
|
||||
|
||||
By default the HTTP request will reuse the current session. If you want the
|
||||
request to have a different session you should set `session` to `null`.
|
||||
|
||||
For POST requests the `uploadData` object must be provided.
|
||||
|
||||
### `protocol.registerStreamProtocol(scheme, handler[, completion])`
|
||||
### `protocol.registerStreamProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `stream` (ReadableStream | [StreamProtocolResponse](structures/stream-protocol-response.md)) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Registers a protocol of `scheme` that will send a `Readable` as a response.
|
||||
Returns `Boolean` - Whether the protocol was successfully registered
|
||||
|
||||
The usage is similar to the other `register{Any}Protocol`, except that the
|
||||
`callback` should be called with either a `Readable` object or an object that
|
||||
has the `data`, `statusCode`, and `headers` properties.
|
||||
Registers a protocol of `scheme` that will send a stream as a response.
|
||||
|
||||
The usage is the same with `registerFileProtocol`, except that the
|
||||
`callback` should be called with either a [`ReadableStream`](https://nodejs.org/api/stream.html#stream_class_stream_readable) object or an object that
|
||||
has the `data` property.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -286,8 +221,6 @@ protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||
},
|
||||
data: createStream('<h5>Response</h5>')
|
||||
})
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
```
|
||||
|
||||
@@ -295,132 +228,102 @@ It is possible to pass any object that implements the readable stream API (emits
|
||||
`data`/`end`/`error` events). For example, here's how a file could be returned:
|
||||
|
||||
```javascript
|
||||
const { protocol } = require('electron')
|
||||
const fs = require('fs')
|
||||
|
||||
protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||
callback(fs.createReadStream('index.html'))
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
})
|
||||
```
|
||||
|
||||
### `protocol.unregisterProtocol(scheme[, completion])`
|
||||
### `protocol.unregisterProtocol(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully unregistered
|
||||
|
||||
Unregisters the custom protocol of `scheme`.
|
||||
|
||||
### `protocol.isProtocolHandled(scheme)`
|
||||
### `protocol.isProtocolRegistered(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Returns `Promise<Boolean>` - fulfilled with a boolean that indicates whether there is
|
||||
already a handler for `scheme`.
|
||||
Returns `Boolean` - Whether `scheme` is already registered.
|
||||
|
||||
### `protocol.interceptFileProtocol(scheme, handler[, completion])`
|
||||
### `protocol.interceptFileProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `filePath` String
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully intercepted
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a file as a response.
|
||||
|
||||
### `protocol.interceptStringProtocol(scheme, handler[, completion])`
|
||||
### `protocol.interceptStringProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `data` (String | [StringProtocolResponse](structures/string-protocol-response.md)) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (String | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully intercepted
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a `String` as a response.
|
||||
|
||||
### `protocol.interceptBufferProtocol(scheme, handler[, completion])`
|
||||
### `protocol.interceptBufferProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `buffer` Buffer (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (Buffer | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully intercepted
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a `Buffer` as a response.
|
||||
|
||||
### `protocol.interceptHttpProtocol(scheme, handler[, completion])`
|
||||
### `protocol.interceptHttpProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `redirectRequest` Object
|
||||
* `url` String
|
||||
* `method` String (optional)
|
||||
* `session` Session | null (optional)
|
||||
* `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` [ProtocolResponse](structures/protocol-response.md)
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully intercepted
|
||||
|
||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||
which sends a new HTTP request as a response.
|
||||
|
||||
### `protocol.interceptStreamProtocol(scheme, handler[, completion])`
|
||||
### `protocol.interceptStreamProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `request` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String>
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||
* `request` ProtocolRequest
|
||||
* `callback` Function
|
||||
* `stream` (ReadableStream | [StreamProtocolResponse](structures/stream-protocol-response.md)) (optional)
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
* `response` (ReadableStream | [ProtocolResponse](structures/protocol-response.md))
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully intercepted
|
||||
|
||||
Same as `protocol.registerStreamProtocol`, except that it replaces an existing
|
||||
protocol handler.
|
||||
|
||||
### `protocol.uninterceptProtocol(scheme[, completion])`
|
||||
### `protocol.uninterceptProtocol(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
* `completion` Function (optional)
|
||||
* `error` Error
|
||||
|
||||
Returns `Boolean` - Whether the protocol was successfully unintercepted
|
||||
|
||||
Remove the interceptor installed for `scheme` and restore its original handler.
|
||||
|
||||
[net-error]: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h
|
||||
### `protocol.isProtocolIntercepted(scheme)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Returns `Boolean` - Whether `scheme` is already intercepted.
|
||||
|
||||
[file-system-api]: https://developer.mozilla.org/en-US/docs/Web/API/LocalFileSystem
|
||||
|
||||
@@ -154,24 +154,43 @@ More may be added as needed to expose more Electron APIs in the sandbox, but any
|
||||
module in the main process can already be used through
|
||||
`electron.remote.require`.
|
||||
|
||||
## Status
|
||||
## Rendering untrusted content
|
||||
|
||||
Please use the `sandbox` option with care, as it is still an experimental
|
||||
feature. We are still not aware of the security implications of exposing some
|
||||
Electron renderer APIs to the preload script, but here are some things to
|
||||
consider before rendering untrusted content:
|
||||
Rendering untrusted content in Electron is still somewhat uncharted territory,
|
||||
though some apps are finding success (e.g. Beaker Browser). Our goal is to get
|
||||
as close to Chrome as we can in terms of the security of sandboxed content, but
|
||||
ultimately we will always be behind due to a few fundamental issues:
|
||||
|
||||
1. We do not have the dedicated resources or expertise that Chromium has to
|
||||
apply to the security of its product. We do our best to make use of what we
|
||||
have, to inherit everything we can from Chromium, and to respond quickly to
|
||||
security issues, but Electron cannot be as secure as Chromium without the
|
||||
resources that Chromium is able to dedicate.
|
||||
2. Some security features in Chrome (such as Safe Browsing and Certificate
|
||||
Transparency) require a centralized authority and dedicated servers, both of
|
||||
which run counter to the goals of the Electron project. As such, we disable
|
||||
those features in Electron, at the cost of the associated security they
|
||||
would otherwise bring.
|
||||
3. There is only one Chromium, whereas there are many thousands of apps built
|
||||
on Electron, all of which behave slightly differently. Accounting for those
|
||||
differences can yield a huge possibility space, and make it challenging to
|
||||
ensure the security of the platform in unusual use cases.
|
||||
4. We can't push security updates to users directly, so we rely on app vendors
|
||||
to upgrade the version of Electron underlying their app in order for
|
||||
security updates to reach users.
|
||||
|
||||
Here are some things to consider before rendering untrusted content:
|
||||
|
||||
- A preload script can accidentally leak privileged APIs to untrusted code,
|
||||
unless [`contextIsolation`](../tutorial/security.md#3-enable-context-isolation-for-remote-content)
|
||||
is also enabled.
|
||||
- Some bug in V8 engine may allow malicious code to access the renderer preload
|
||||
APIs, effectively granting full access to the system through the `remote`
|
||||
module. Therefore, it is highly recommended to
|
||||
[disable the `remote` module](../tutorial/security.md#15-disable-the-remote-module).
|
||||
If disabling is not feasible, you should selectively
|
||||
[filter the `remote` module](../tutorial/security.md#16-filter-the-remote-module).
|
||||
|
||||
Since rendering untrusted content in Electron is still uncharted territory,
|
||||
the APIs exposed to the sandbox preload script should be considered more
|
||||
unstable than the rest of Electron APIs, and may have breaking changes to fix
|
||||
security issues.
|
||||
- Some bug in the V8 engine may allow malicious code to access the renderer
|
||||
preload APIs, effectively granting full access to the system through the
|
||||
`remote` module. Therefore, it is highly recommended to [disable the `remote`
|
||||
module](../tutorial/security.md#15-disable-the-remote-module).
|
||||
If disabling is not feasible, you should selectively [filter the `remote`
|
||||
module](../tutorial/security.md#16-filter-the-remote-module).
|
||||
- While we make our best effort to backport Chromium security fixes to older
|
||||
versions of Electron, we do not make a guarantee that every fix will be
|
||||
backported. Your best chance at staying secure is to be on the latest stable
|
||||
version of Electron.
|
||||
|
||||
@@ -39,7 +39,7 @@ Returns:
|
||||
* `message` String - The actual console message
|
||||
* `versionId` Number - The version ID of the service worker that sent the log message
|
||||
* `source` String - The type of source for this message. Can be `javascript`, `xml`, `network`, `console-api`, `storage`, `app-cache`, `rendering`, `security`, `deprecation`, `worker`, `violation`, `intervention`, `recommendation` or `other`.
|
||||
* `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `sourceUrl` String - The URL the message came from
|
||||
* `lineNumber` Number - The line number of the source that triggered this console message
|
||||
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
* `allowServiceWorkers` Boolean (optional) - Default false.
|
||||
* `supportFetchAPI` Boolean (optional) - Default false.
|
||||
* `corsEnabled` Boolean (optional) - Default false.
|
||||
* `stream` Boolean (optional) - Default false.
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
* `Pepper Plugin`
|
||||
* `Pepper Plugin Broker`
|
||||
* `Unknown`
|
||||
* `name` String (optional) - The name of the process. i.e. for plugins it might be Flash.
|
||||
Examples for utility: `Audio Service`, `Content Decryption Module Service`, `Network Service`, `Video Capture`, etc.
|
||||
* `cpu` [CPUUsage](cpu-usage.md) - CPU usage of the process.
|
||||
* `creationTime` Number - Creation time for this process.
|
||||
The time is represented as number of milliseconds since epoch.
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
* `referrer` String
|
||||
* `method` String
|
||||
* `uploadData` [UploadData[]](upload-data.md) (optional)
|
||||
* `headers` Record<String, String>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* `trace_buffer_size_in_events` number (optional) - maximum size of the trace
|
||||
recording buffer in events.
|
||||
* `enable_argument_filter` boolean (optional) - if true, filter event data
|
||||
according to a whitelist of events that have been manually vetted to not
|
||||
according to a specific list of events that have been manually vetted to not
|
||||
include any PII. See [the implementation in
|
||||
Chromium][trace_event_args_whitelist.cc] for specifics.
|
||||
* `included_categories` String[] (optional) - a list of tracing categories to
|
||||
|
||||
@@ -416,7 +416,7 @@ This API itself will not protect your user data; rather, it is a mechanism to al
|
||||
|
||||
Returns `Boolean` - `true` if the current process is a trusted accessibility client and `false` if it is not.
|
||||
|
||||
### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_
|
||||
### `systemPreferences.getMediaAccessStatus(mediaType)` _Windows_ _macOS_
|
||||
|
||||
* `mediaType` String - Can be `microphone`, `camera` or `screen`.
|
||||
|
||||
@@ -426,6 +426,9 @@ This user consent was not required on macOS 10.13 High Sierra or lower so this m
|
||||
macOS 10.14 Mojave or higher requires consent for `microphone` and `camera` access.
|
||||
macOS 10.15 Catalina or higher requires consent for `screen` access.
|
||||
|
||||
Windows 10 has a global setting controlling `microphone` and `camera` access for all win32 applications.
|
||||
It will always return `granted` for `screen` and for all media types on older versions of Windows.
|
||||
|
||||
### `systemPreferences.askForMediaAccess(mediaType)` _macOS_
|
||||
|
||||
* `mediaType` String - the type of media being requested; can be `microphone`, `camera`.
|
||||
|
||||
@@ -402,6 +402,7 @@ Returns:
|
||||
* `key` String - Equivalent to [KeyboardEvent.key][keyboardevent].
|
||||
* `code` String - Equivalent to [KeyboardEvent.code][keyboardevent].
|
||||
* `isAutoRepeat` Boolean - Equivalent to [KeyboardEvent.repeat][keyboardevent].
|
||||
* `isComposing` Boolean - Equivalent to [KeyboardEvent.isComposing][keyboardevent].
|
||||
* `shift` Boolean - Equivalent to [KeyboardEvent.shiftKey][keyboardevent].
|
||||
* `control` Boolean - Equivalent to [KeyboardEvent.controlKey][keyboardevent].
|
||||
* `alt` Boolean - Equivalent to [KeyboardEvent.altKey][keyboardevent].
|
||||
@@ -737,9 +738,9 @@ Emitted when a `<webview>` has been attached to this web contents.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `level` Integer
|
||||
* `message` String
|
||||
* `line` Integer
|
||||
* `level` Integer - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `message` String - The actual console message
|
||||
* `line` Integer - The line number of the source that triggered this console message
|
||||
* `sourceId` String
|
||||
|
||||
Emitted when the associated window logs a console message.
|
||||
@@ -907,10 +908,10 @@ Returns `String` - The URL of the current web page.
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
let currentURL = win.webContents.getURL()
|
||||
console.log(currentURL)
|
||||
win.loadURL('http://github.com').then(() => {
|
||||
const currentURL = win.webContents.getURL()
|
||||
console.log(currentURL)
|
||||
})
|
||||
```
|
||||
|
||||
#### `contents.getTitle()`
|
||||
|
||||
@@ -162,10 +162,14 @@ this limitation.
|
||||
* `error` Error
|
||||
|
||||
Returns `Promise<any>` - A promise that resolves with the result of the executed
|
||||
code or is rejected if execution throws or results in a rejected promise.
|
||||
code or is rejected if execution could not start.
|
||||
|
||||
Works like `executeJavaScript` but evaluates `scripts` in an isolated context.
|
||||
|
||||
Note that when the execution of script fails, the returned promise will not
|
||||
reject and the `result` would be `undefined`. This is because Chromium does not
|
||||
dispatch errors of isolated worlds to foreign worlds.
|
||||
|
||||
### `webFrame.setIsolatedWorldInfo(worldId, info)`
|
||||
* `worldId` Integer - The ID of the world to run the javascript in, `0` is the default world, `999` is the world used by Electrons `contextIsolation` feature. Chrome extensions reserve the range of IDs in `[1 << 20, 1 << 29)`. You can provide any integer here.
|
||||
* `info` Object
|
||||
|
||||
@@ -755,9 +755,9 @@ Fired when page leaves fullscreen triggered by HTML API.
|
||||
|
||||
Returns:
|
||||
|
||||
* `level` Integer
|
||||
* `message` String
|
||||
* `line` Integer
|
||||
* `level` Integer - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `message` String - The actual console message
|
||||
* `line` Integer - The line number of the source that triggered this console message
|
||||
* `sourceId` String
|
||||
|
||||
Fired when the guest window logs a console message.
|
||||
|
||||
@@ -14,6 +14,15 @@ This document uses the following convention to categorize breaking changes:
|
||||
|
||||
## Planned Breaking API Changes (12.0)
|
||||
|
||||
### Default Changed: `contextIsolation` defaults to `true`
|
||||
|
||||
In Electron 12, `contextIsolation` will be enabled by default. To restore
|
||||
the previous behavior, `contextIsolation: false` must be specified in WebPreferences.
|
||||
|
||||
We [recommend having contextIsolation enabled](https://github.com/electron/electron/blob/master/docs/tutorial/security.md#3-enable-context-isolation-for-remote-content) for the security of your application.
|
||||
|
||||
For more details see: https://github.com/electron/electron/issues/23506
|
||||
|
||||
### Removed: `crashReporter` methods in the renderer process
|
||||
|
||||
The following `crashReporter` methods are no longer available in the renderer
|
||||
@@ -30,8 +39,21 @@ They should be called only from the main process.
|
||||
|
||||
See [#23265](https://github.com/electron/electron/pull/23265) for more details.
|
||||
|
||||
### Default Changed: `crashReporter.start({ compress: true })`
|
||||
|
||||
The default value of the `compress` option to `crashReporter.start` has changed
|
||||
from `false` to `true`. This means that crash dumps will be uploaded to the
|
||||
crash ingestion server with the `Content-Encoding: gzip` header, and the body
|
||||
will be compressed.
|
||||
|
||||
If your crash ingestion server does not support compressed payloads, you can
|
||||
turn off compression by specifying `{ compress: false }` in the crash reporter
|
||||
options.
|
||||
|
||||
## Planned Breaking API Changes (11.0)
|
||||
|
||||
There are no breaking changes planned for 11.0.
|
||||
|
||||
## Planned Breaking API Changes (10.0)
|
||||
|
||||
### Deprecated: `companyName` argument to `crashReporter.start()`
|
||||
@@ -79,6 +101,12 @@ All above methods remain non-deprecated when called from the main process.
|
||||
|
||||
See [#23265](https://github.com/electron/electron/pull/23265) for more details.
|
||||
|
||||
### Deprecated: `crashReporter.start({ compress: false })`
|
||||
|
||||
Setting `{ compress: false }` in `crashReporter.start` is deprecated. Nearly
|
||||
all crash ingestion servers support gzip compression. This option will be
|
||||
removed in a future version of Electron.
|
||||
|
||||
### Removed: Browser Window Affinity
|
||||
|
||||
The `affinity` option when constructing a new `BrowserWindow` will be removed
|
||||
|
||||
@@ -23,4 +23,3 @@ For guides on Electron app development, see
|
||||
* [Debugging on macOS](debugging-instructions-macos.md)
|
||||
* [Setting Up Symbol Server in Debugger](setting-up-symbol-server.md)
|
||||
* [Patches](patches.md)
|
||||
* [Upgrading Node](upgrading-node.md)
|
||||
|
||||
@@ -10,6 +10,10 @@ Check the build prerequisites for your platform before proceeding
|
||||
* [Linux](build-instructions-linux.md#prerequisites)
|
||||
* [Windows](build-instructions-windows.md#prerequisites)
|
||||
|
||||
## Build Tools
|
||||
|
||||
[Electron's Build Tools](https://github.com/electron/build-tools) automate much of the setup for compiling Electron from source with different configurations and build targets. If you wish to set up the environment manually, the instructions are listed below.
|
||||
|
||||
## GN prerequisites
|
||||
|
||||
You'll need to install [`depot_tools`][depot-tools], the toolset
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
# Upgrading Node
|
||||
|
||||
## Discussion
|
||||
|
||||
Chromium and Node.js both depend on V8, and Electron contains
|
||||
only a single copy of V8, so it's important to ensure that the
|
||||
version of V8 chosen is compatible with the build's version of
|
||||
Node.js and Chromium.
|
||||
|
||||
Upgrading Node is much easier than upgrading Chromium, so fewer
|
||||
conflicts arise if one upgrades Chromium first, then chooses the
|
||||
upstream Node release whose version of V8 is closest to the one
|
||||
Chromium contains.
|
||||
|
||||
Electron has its own [Node fork](https://github.com/electron/node)
|
||||
with modifications for the V8 build details mentioned above
|
||||
and for exposing API needed by Electron. Once an upstream Node
|
||||
release is chosen, it's placed in a branch in Electron's Node fork
|
||||
and any Electron Node patches are applied there.
|
||||
|
||||
Another factor is that the Node project patches its version of V8.
|
||||
As mentioned above, Electron builds everything with a single copy
|
||||
of V8, so Node's V8 patches must be ported to that copy.
|
||||
|
||||
Once all of Electron's dependencies are building and using the same
|
||||
copy of V8, the next step is to fix any Electron code issues caused
|
||||
by the Node upgrade.
|
||||
|
||||
[FIXME] something about a Node debugger in Atom that we (e.g. deepak)
|
||||
use and need to confirm doesn't break with the Node upgrade?
|
||||
|
||||
So in short, the primary steps are:
|
||||
|
||||
1. Update Electron's Node fork to the desired version
|
||||
2. Backport Node's V8 patches to our copy of V8
|
||||
3. Update the GN build files, porting changes from node's GYP files
|
||||
4. Update Electron's DEPS to use new version of Node
|
||||
|
||||
## Updating Electron's Node [fork](https://github.com/electron/node)
|
||||
|
||||
1. Ensure that `master` on `electron/node` has updated release tags from `nodejs/node`
|
||||
2. Create a branch in https://github.com/electron/node: `electron-node-vX.X.X` where the base that you're branching from is the tag for the desired update
|
||||
- `vX.X.X` Must use a version of Node compatible with our current version of Chromium
|
||||
3. Re-apply our commits from the previous version of Node we were using (`vY.Y.Y`) to `v.X.X.X`
|
||||
- Check release tag and select the range of commits we need to re-apply
|
||||
- Cherry-pick commit range:
|
||||
1. Checkout both `vY.Y.Y` & `v.X.X.X`
|
||||
2. `git cherry-pick FIRST_COMMIT_HASH..LAST_COMMIT_HASH`
|
||||
- Resolve merge conflicts in each file encountered, then:
|
||||
1. `git add <conflict-file>`
|
||||
2. `git cherry-pick --continue`
|
||||
3. Repeat until finished
|
||||
|
||||
|
||||
## Updating [V8](https://github.com/electron/node/src/V8) Patches
|
||||
|
||||
We need to generate a patch file from each patch that Node
|
||||
applies to V8.
|
||||
|
||||
```sh
|
||||
$ cd third_party/electron_node
|
||||
$ CURRENT_NODE_VERSION=vX.Y.Z
|
||||
# Find the last commit with the message "deps: update V8 to <some version>"
|
||||
# This commit corresponds to Node resetting V8 to its pristine upstream
|
||||
# state at the stated version.
|
||||
$ LAST_V8_UPDATE="$(git log --grep='^deps: update V8' --format='%H' -1 deps/v8)"
|
||||
# This creates a patch file containing all changes in deps/v8 from
|
||||
# $LAST_V8_UPDATE up to the current Node version, formatted in a way that
|
||||
# it will apply cleanly to the V8 repository (i.e. with `deps/v8`
|
||||
# stripped off the path and excluding the v8/gypfiles directory, which
|
||||
# isn't present in V8.
|
||||
$ git format-patch \
|
||||
--relative=deps/v8 \
|
||||
$LAST_V8_UPDATE..$CURRENT_NODE_VERSION \
|
||||
deps/v8 \
|
||||
':(exclude)deps/v8/gypfiles' \
|
||||
--stdout \
|
||||
> ../../electron/common/patches/v8/node_v8_patches.patch
|
||||
```
|
||||
|
||||
This list of patches will probably include one that claims to
|
||||
make the V8 API backwards-compatible with a previous version of
|
||||
V8. Unfortunately, those patches almost always change the V8 API
|
||||
in a way that is incompatible with Chromium.
|
||||
|
||||
It's usually easier to update Node to work without the
|
||||
compatibility patch than to update Chromium to work with the
|
||||
compatibility patch, so it's recommended to revert the
|
||||
compatibility patch and fix any errors that arise when compiling
|
||||
Node.
|
||||
|
||||
## Update Electron's `DEPS` file
|
||||
|
||||
Update the `DEPS` file in the root of
|
||||
[electron/electron](https://github.com/electron/electron) to
|
||||
point to the git hash of the updated Node.
|
||||
|
||||
## Notes
|
||||
|
||||
- Node maintains its own fork of V8
|
||||
- They backport a small amount of things as needed
|
||||
- Documentation in Node about how [they work with V8](https://nodejs.org/api/v8.html)
|
||||
- We update code such that we only use one copy of V8 across all of Electron
|
||||
- E.g Electron, Chromium, and Node.js
|
||||
- We don’t track upstream closely due to logistics:
|
||||
- Upstream uses multiple repos and so merging into a single repo
|
||||
would result in lost history. So we only update when we’re planning
|
||||
a Node version bump in Electron.
|
||||
- Chromium is large and time-consuming to update, so we typically
|
||||
choose the Node version based on which of its releases has a version
|
||||
of V8 that’s closest to the version in Chromium that we’re using.
|
||||
- We sometimes have to wait for the next periodic Node release
|
||||
because it will sync more closely with the version of V8 in the new Chromium
|
||||
- Electron keeps all its patches in the repo because it’s simpler than
|
||||
maintaining different repos for patches for each upstream project.
|
||||
- Crashpad, Node.js, Chromium, Skia etc. patches are all kept in the same place
|
||||
- Building Node:
|
||||
- We maintain our own GN build files for Node.js to make it easier to ensure
|
||||
that eevrything is built with the same compiler flags.
|
||||
This means that every time we upgrade Node.js we have to do a modest amount of
|
||||
work to synchronize the GN files with the upstream GYP files.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Electron App Feedback Program
|
||||
|
||||
Electron is working on building a streamlined release process and having faster releases. To help with that, we have the App Feedback Program for large-scale Electron apps to test our beta releases and report app-specific issues to the Electron team. We use this program to help us prioritize work and get applications upgraded to the next stable release as soon as possible. There are a few requirements we expect from participants, such as attending short, online weekly check-ins. Please visit the [blog post](https://electronjs.org/blog/app-feedback-program) for details and sign-up.
|
||||
@@ -100,6 +100,25 @@ To see all of this in action, check out Electron Fiddle's source code,
|
||||
[especially its `electron-forge` configuration
|
||||
file](https://github.com/electron/fiddle/blob/master/forge.config.js).
|
||||
|
||||
If you plan to access the microphone or camera within your app using Electron's APIs, you'll also
|
||||
need to add the following entitlements:
|
||||
|
||||
```xml
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
```
|
||||
|
||||
If these are not present in your app's entitlements when you invoke, for example:
|
||||
|
||||
```js
|
||||
const { systemPreferences } = require('electron')
|
||||
|
||||
const microphone = systemPreferences.askForMediaAccess('microphone')
|
||||
```
|
||||
|
||||
Your app may crash. See the Resource Access section in [Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime) for more information and entitlements you may need.
|
||||
|
||||
## `electron-builder`
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ e.g. `2.0.1`.
|
||||
|
||||
Specifically, the above means:
|
||||
|
||||
1. Admitting non-breaking-API changes before Week 3 in the beta cycle is okay, even if those changes have the potential to cause moderate side-affects
|
||||
1. Admitting non-breaking-API changes before Week 3 in the beta cycle is okay, even if those changes have the potential to cause moderate side-effects
|
||||
2. Admitting feature-flagged changes, that do not otherwise alter existing code paths, at most points in the beta cycle is okay. Users can explicitly enable those flags in their apps.
|
||||
3. Admitting features of any sort after Week 3 in the beta cycle is 👎 without a very good reason.
|
||||
|
||||
@@ -139,7 +139,7 @@ We seek to increase clarity at all levels of the update and releases process. St
|
||||
* Commits that would result in a semver **minor** bump must start with `feat:`.
|
||||
* Commits that would result in a semver **patch** bump must start with `fix:`.
|
||||
|
||||
* We allow squashing of commits, provided that the squashed message adheres the the above message format.
|
||||
* We allow squashing of commits, provided that the squashed message adheres to the above message format.
|
||||
* It is acceptable for some commits in a pull request to not include a semantic prefix, as long as the pull request title contains a meaningful encompassing semantic message.
|
||||
|
||||
# Versioned `master`
|
||||
|
||||
@@ -44,23 +44,10 @@ run the main process. An example of your `package.json` might look like this:
|
||||
```
|
||||
|
||||
__Note__: If the `main` field is not present in `package.json`, Electron will
|
||||
attempt to load an `index.js` (as Node.js does). If this was actually
|
||||
a simple Node application, you would add a `start` script that instructs `node`
|
||||
to execute the current package:
|
||||
attempt to load an `index.js` (as Node.js does).
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "your-app",
|
||||
"version": "0.1.0",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "node ."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Turning this Node application into an Electron application is quite simple - we
|
||||
merely replace the `node` runtime with the `electron` runtime.
|
||||
By default, `npm start` would run the main script with Node.js. in order to make
|
||||
it run with Electron, you can add a `start` script:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -154,10 +141,10 @@ function createWindow () {
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ template("electron_extra_paks") {
|
||||
output = "${invoker.output_dir}/resources.pak"
|
||||
sources = [
|
||||
"$root_gen_dir/components/components_resources.pak",
|
||||
"$root_gen_dir/content/browser/resources/media/media_internals_resources.pak",
|
||||
"$root_gen_dir/content/browser/tracing/tracing_resources.pak",
|
||||
"$root_gen_dir/content/browser/webrtc/resources/webrtc_internals_resources.pak",
|
||||
"$root_gen_dir/content/content_resources.pak",
|
||||
"$root_gen_dir/content/dev_ui_content_resources.pak",
|
||||
"$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
|
||||
@@ -68,7 +70,9 @@ template("electron_extra_paks") {
|
||||
"//components/resources",
|
||||
"//content:content_resources",
|
||||
"//content:dev_ui_content_resources",
|
||||
"//content/browser/resources/media:media_internals_resources",
|
||||
"//content/browser/tracing:resources",
|
||||
"//content/browser/webrtc/resources",
|
||||
"//electron:resources",
|
||||
"//mojo/public/js:resources",
|
||||
"//net:net_resources",
|
||||
|
||||
@@ -43,7 +43,6 @@ auto_filenames = {
|
||||
"docs/api/power-monitor.md",
|
||||
"docs/api/power-save-blocker.md",
|
||||
"docs/api/process.md",
|
||||
"docs/api/protocol-ns.md",
|
||||
"docs/api/protocol.md",
|
||||
"docs/api/remote.md",
|
||||
"docs/api/sandbox-option.md",
|
||||
@@ -136,13 +135,12 @@ auto_filenames = {
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
@@ -150,7 +148,7 @@ auto_filenames = {
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
@@ -167,61 +165,65 @@ auto_filenames = {
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/sandboxed_renderer/api/exports/electron.ts",
|
||||
"lib/sandboxed_renderer/api/module-list.ts",
|
||||
"lib/sandboxed_renderer/init.js",
|
||||
"lib/sandboxed_renderer/init.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/isolated_renderer/init.js",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
browser_bundle_deps = [
|
||||
"lib/browser/api/app.ts",
|
||||
"lib/browser/api/auto-updater.js",
|
||||
"lib/browser/api/auto-updater/auto-updater-native.js",
|
||||
"lib/browser/api/auto-updater/auto-updater-win.js",
|
||||
"lib/browser/api/auto-updater/squirrel-update-win.js",
|
||||
"lib/browser/api/browser-view.js",
|
||||
"lib/browser/api/browser-window.js",
|
||||
"lib/browser/api/content-tracing.js",
|
||||
"lib/browser/api/auto-updater.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-native.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-win.ts",
|
||||
"lib/browser/api/auto-updater/squirrel-update-win.ts",
|
||||
"lib/browser/api/base-window.ts",
|
||||
"lib/browser/api/browser-view.ts",
|
||||
"lib/browser/api/browser-window.ts",
|
||||
"lib/browser/api/content-tracing.ts",
|
||||
"lib/browser/api/crash-reporter.ts",
|
||||
"lib/browser/api/dialog.js",
|
||||
"lib/browser/api/desktop-capturer.ts",
|
||||
"lib/browser/api/dialog.ts",
|
||||
"lib/browser/api/exports/electron.ts",
|
||||
"lib/browser/api/global-shortcut.js",
|
||||
"lib/browser/api/in-app-purchase.js",
|
||||
"lib/browser/api/global-shortcut.ts",
|
||||
"lib/browser/api/in-app-purchase.ts",
|
||||
"lib/browser/api/ipc-main.ts",
|
||||
"lib/browser/api/menu-item-roles.js",
|
||||
"lib/browser/api/menu-item.js",
|
||||
"lib/browser/api/menu-utils.js",
|
||||
"lib/browser/api/menu.js",
|
||||
"lib/browser/api/menu-item-roles.ts",
|
||||
"lib/browser/api/menu-item.ts",
|
||||
"lib/browser/api/menu-utils.ts",
|
||||
"lib/browser/api/menu.ts",
|
||||
"lib/browser/api/message-channel.ts",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-log.js",
|
||||
"lib/browser/api/net-log.ts",
|
||||
"lib/browser/api/net.ts",
|
||||
"lib/browser/api/notification.js",
|
||||
"lib/browser/api/notification.ts",
|
||||
"lib/browser/api/power-monitor.ts",
|
||||
"lib/browser/api/power-save-blocker.js",
|
||||
"lib/browser/api/power-save-blocker.ts",
|
||||
"lib/browser/api/protocol.ts",
|
||||
"lib/browser/api/screen.ts",
|
||||
"lib/browser/api/session.js",
|
||||
"lib/browser/api/session.ts",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/top-level-window.js",
|
||||
"lib/browser/api/touch-bar.js",
|
||||
"lib/browser/api/tray.js",
|
||||
"lib/browser/api/view.js",
|
||||
"lib/browser/api/views/image-view.js",
|
||||
"lib/browser/api/web-contents-view.js",
|
||||
"lib/browser/api/web-contents.js",
|
||||
"lib/browser/chrome-extension-shim.js",
|
||||
"lib/browser/api/tray.ts",
|
||||
"lib/browser/api/view.ts",
|
||||
"lib/browser/api/views/image-view.ts",
|
||||
"lib/browser/api/web-contents-view.ts",
|
||||
"lib/browser/api/web-contents.ts",
|
||||
"lib/browser/chrome-extension-shim.ts",
|
||||
"lib/browser/default-menu.ts",
|
||||
"lib/browser/desktop-capturer.ts",
|
||||
"lib/browser/devtools.ts",
|
||||
@@ -232,18 +234,17 @@ auto_filenames = {
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
"lib/browser/ipc-main-internal.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
"lib/browser/navigation-controller.js",
|
||||
"lib/browser/navigation-controller.ts",
|
||||
"lib/browser/remote/objects-registry.ts",
|
||||
"lib/browser/remote/server.ts",
|
||||
"lib/browser/rpc-server.js",
|
||||
"lib/browser/rpc-server.ts",
|
||||
"lib/browser/utils.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/parse-features-string.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
@@ -255,17 +256,18 @@ auto_filenames = {
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
renderer_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
@@ -277,7 +279,7 @@ auto_filenames = {
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
@@ -297,17 +299,18 @@ auto_filenames = {
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
worker_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
@@ -318,7 +321,7 @@ auto_filenames = {
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
@@ -328,5 +331,7 @@ auto_filenames = {
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ filenames = {
|
||||
"shell/browser/api/electron_api_app_mac.mm",
|
||||
"shell/browser/api/electron_api_auto_updater.cc",
|
||||
"shell/browser/api/electron_api_auto_updater.h",
|
||||
"shell/browser/api/electron_api_base_window.cc",
|
||||
"shell/browser/api/electron_api_base_window.h",
|
||||
"shell/browser/api/electron_api_browser_view.cc",
|
||||
"shell/browser/api/electron_api_browser_view.h",
|
||||
"shell/browser/api/electron_api_browser_window.cc",
|
||||
@@ -102,8 +104,6 @@ filenames = {
|
||||
"shell/browser/api/electron_api_system_preferences.h",
|
||||
"shell/browser/api/electron_api_system_preferences_mac.mm",
|
||||
"shell/browser/api/electron_api_system_preferences_win.cc",
|
||||
"shell/browser/api/electron_api_top_level_window.cc",
|
||||
"shell/browser/api/electron_api_top_level_window.h",
|
||||
"shell/browser/api/electron_api_tray.cc",
|
||||
"shell/browser/api/electron_api_tray.h",
|
||||
"shell/browser/api/electron_api_url_loader.cc",
|
||||
@@ -525,6 +525,8 @@ filenames = {
|
||||
"shell/common/gin_helper/function_template_extensions.h",
|
||||
"shell/common/gin_helper/locker.cc",
|
||||
"shell/common/gin_helper/locker.h",
|
||||
"shell/common/gin_helper/microtasks_scope.cc",
|
||||
"shell/common/gin_helper/microtasks_scope.h",
|
||||
"shell/common/gin_helper/object_template_builder.cc",
|
||||
"shell/common/gin_helper/object_template_builder.h",
|
||||
"shell/common/gin_helper/persistent_dictionary.cc",
|
||||
|
||||
@@ -4,8 +4,8 @@ import * as path from 'path';
|
||||
import { deprecate, Menu } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const bindings = process.electronBinding('app');
|
||||
const commandLine = process.electronBinding('command_line');
|
||||
const bindings = process._linkedBinding('electron_browser_app');
|
||||
const commandLine = process._linkedBinding('electron_common_command_line');
|
||||
const { app, App } = bindings;
|
||||
|
||||
// Only one app object permitted.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win');
|
||||
} else {
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native');
|
||||
}
|
||||
5
lib/browser/api/auto-updater.ts
Normal file
5
lib/browser/api/auto-updater.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
if (process.platform === 'win32') {
|
||||
module.exports = require('./auto-updater/auto-updater-win');
|
||||
} else {
|
||||
module.exports = require('./auto-updater/auto-updater-native');
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater');
|
||||
|
||||
// AutoUpdater is an EventEmitter.
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(autoUpdater);
|
||||
|
||||
module.exports = autoUpdater;
|
||||
8
lib/browser/api/auto-updater/auto-updater-native.ts
Normal file
8
lib/browser/api/auto-updater/auto-updater-native.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { EventEmitter } from 'events';
|
||||
const { autoUpdater, AutoUpdater } = process._linkedBinding('electron_browser_auto_updater');
|
||||
|
||||
// AutoUpdater is an EventEmitter.
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(autoUpdater);
|
||||
|
||||
export default autoUpdater;
|
||||
@@ -1,13 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win');
|
||||
import { app } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as squirrelUpdate from './squirrel-update-win';
|
||||
|
||||
class AutoUpdater extends EventEmitter {
|
||||
updateAvailable: boolean = false;
|
||||
updateURL: string | null = null;
|
||||
|
||||
quitAndInstall () {
|
||||
if (!this.updateAvailable) {
|
||||
return this.emitError('No update available, can\'t quit and install');
|
||||
return this.emitError(new Error('No update available, can\'t quit and install'));
|
||||
}
|
||||
squirrelUpdate.processStart();
|
||||
app.quit();
|
||||
@@ -17,8 +18,8 @@ class AutoUpdater extends EventEmitter {
|
||||
return this.updateURL;
|
||||
}
|
||||
|
||||
setFeedURL (options) {
|
||||
let updateURL;
|
||||
setFeedURL (options: { url: string } | string) {
|
||||
let updateURL: string;
|
||||
if (typeof options === 'object') {
|
||||
if (typeof options.url === 'string') {
|
||||
updateURL = options.url;
|
||||
@@ -34,14 +35,15 @@ class AutoUpdater extends EventEmitter {
|
||||
}
|
||||
|
||||
checkForUpdates () {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set');
|
||||
const url = this.updateURL;
|
||||
if (!url) {
|
||||
return this.emitError(new Error('Update URL is not set'));
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel');
|
||||
return this.emitError(new Error('Can not find Squirrel'));
|
||||
}
|
||||
this.emit('checking-for-update');
|
||||
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
|
||||
squirrelUpdate.checkForUpdate(url, (error, update) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error);
|
||||
}
|
||||
@@ -50,7 +52,7 @@ class AutoUpdater extends EventEmitter {
|
||||
}
|
||||
this.updateAvailable = true;
|
||||
this.emit('update-available');
|
||||
squirrelUpdate.update(this.updateURL, (error) => {
|
||||
squirrelUpdate.update(url, (error) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error);
|
||||
}
|
||||
@@ -66,9 +68,9 @@ class AutoUpdater extends EventEmitter {
|
||||
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
emitError (message) {
|
||||
this.emit('error', new Error(message), message);
|
||||
emitError (error: Error) {
|
||||
this.emit('error', error, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AutoUpdater();
|
||||
export default new AutoUpdater();
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const spawn = require('child_process').spawn;
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
|
||||
|
||||
// i.e. my-app/app-0.1.13/
|
||||
const appFolder = path.dirname(process.execPath);
|
||||
@@ -10,15 +8,15 @@ const appFolder = path.dirname(process.execPath);
|
||||
// i.e. my-app/Update.exe
|
||||
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
|
||||
const exeName = path.basename(process.execPath);
|
||||
let spawnedArgs = [];
|
||||
let spawnedProcess;
|
||||
let spawnedArgs: string[] = [];
|
||||
let spawnedProcess: ChildProcessWithoutNullStreams | undefined;
|
||||
|
||||
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]);
|
||||
const isSameArgs = (args: string[]) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]);
|
||||
|
||||
// Spawn a command and invoke the callback when it completes with an error
|
||||
// and the output from standard out.
|
||||
const spawnUpdate = function (args, detached, callback) {
|
||||
let error, errorEmitted, stderr, stdout;
|
||||
const spawnUpdate = function (args: string[], detached: boolean, callback: Function) {
|
||||
let error: Error, errorEmitted: boolean, stderr: string, stdout: string;
|
||||
|
||||
try {
|
||||
// Ensure we don't spawn multiple squirrel processes
|
||||
@@ -79,13 +77,13 @@ const spawnUpdate = function (args, detached, callback) {
|
||||
};
|
||||
|
||||
// Start an instance of the installed app.
|
||||
exports.processStart = function () {
|
||||
export function processStart () {
|
||||
return spawnUpdate(['--processStartAndWait', exeName], true, function () {});
|
||||
};
|
||||
}
|
||||
|
||||
// Download the releases specified by the URL and write new results to stdout.
|
||||
exports.checkForUpdate = function (updateURL, callback) {
|
||||
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
|
||||
export function checkForUpdate (updateURL: string, callback: (error: Error | null, update?: any) => void) {
|
||||
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error: Error, stdout: string) {
|
||||
let ref, ref1, update;
|
||||
if (error != null) {
|
||||
return callback(error);
|
||||
@@ -93,27 +91,27 @@ exports.checkForUpdate = function (updateURL, callback) {
|
||||
try {
|
||||
// Last line of output is the JSON details about the releases
|
||||
const json = stdout.trim().split('\n').pop();
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined;
|
||||
update = (ref = JSON.parse(json!)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined;
|
||||
} catch {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`Invalid result:\n${stdout}`);
|
||||
return callback(new Error(`Invalid result:\n${stdout}`));
|
||||
}
|
||||
return callback(null, update);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Update the application to the latest remote version specified by URL.
|
||||
exports.update = function (updateURL, callback) {
|
||||
export function update (updateURL: string, callback: (error: Error) => void) {
|
||||
return spawnUpdate(['--update', updateURL], false, callback);
|
||||
};
|
||||
}
|
||||
|
||||
// Is the Update.exe installed with the current application?
|
||||
exports.supported = function () {
|
||||
export function supported () {
|
||||
try {
|
||||
fs.accessSync(updateExe, fs.R_OK);
|
||||
fs.accessSync(updateExe, fs.constants.R_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
'use strict';
|
||||
import { EventEmitter } from 'events';
|
||||
import type { BaseWindow as TLWT } from 'electron';
|
||||
const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as { BaseWindow: typeof TLWT };
|
||||
|
||||
const electron = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const { TopLevelWindow } = process.electronBinding('top_level_window');
|
||||
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
TopLevelWindow.prototype._init = function () {
|
||||
(BaseWindow.prototype as any)._init = function () {
|
||||
// Avoid recursive require.
|
||||
const { app } = electron;
|
||||
const { app } = require('electron');
|
||||
|
||||
// Simulate the application menu on platforms other than macOS.
|
||||
if (process.platform !== 'darwin') {
|
||||
@@ -19,88 +17,88 @@ TopLevelWindow.prototype._init = function () {
|
||||
|
||||
// Properties
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'autoHideMenuBar', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'autoHideMenuBar', {
|
||||
get: function () { return this.isMenuBarAutoHide(); },
|
||||
set: function (autoHide) { this.setAutoHideMenuBar(autoHide); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'visibleOnAllWorkspaces', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'visibleOnAllWorkspaces', {
|
||||
get: function () { return this.isVisibleOnAllWorkspaces(); },
|
||||
set: function (visible) { this.setVisibleOnAllWorkspaces(visible); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'fullScreen', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'fullScreen', {
|
||||
get: function () { return this.isFullScreen(); },
|
||||
set: function (full) { this.setFullScreen(full); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'simpleFullScreen', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'simpleFullScreen', {
|
||||
get: function () { return this.isSimpleFullScreen(); },
|
||||
set: function (simple) { this.setSimpleFullScreen(simple); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'kiosk', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'kiosk', {
|
||||
get: function () { return this.isKiosk(); },
|
||||
set: function (kiosk) { this.setKiosk(kiosk); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'documentEdited', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'documentEdited', {
|
||||
get: function () { return this.isFullscreen(); },
|
||||
set: function (edited) { this.setDocumentEdited(edited); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'shadow', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'shadow', {
|
||||
get: function () { return this.hasShadow(); },
|
||||
set: function (shadow) { this.setHasShadow(shadow); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'representedFilename', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'representedFilename', {
|
||||
get: function () { return this.getRepresentedFilename(); },
|
||||
set: function (filename) { this.setRepresentedFilename(filename); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'minimizable', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'minimizable', {
|
||||
get: function () { return this.isMinimizable(); },
|
||||
set: function (min) { this.setMinimizable(min); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'title', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'title', {
|
||||
get: function () { return this.getTitle(); },
|
||||
set: function (title) { this.setTitle(title); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'maximizable', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'maximizable', {
|
||||
get: function () { return this.isMaximizable(); },
|
||||
set: function (max) { this.setMaximizable(max); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'resizable', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'resizable', {
|
||||
get: function () { return this.isResizable(); },
|
||||
set: function (res) { this.setResizable(res); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'menuBarVisible', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'menuBarVisible', {
|
||||
get: function () { return this.isMenuBarVisible(); },
|
||||
set: function (visible) { this.setMenuBarVisibility(visible); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'fullScreenable', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'fullScreenable', {
|
||||
get: function () { return this.isFullScreenable(); },
|
||||
set: function (full) { this.setFullScreenable(full); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'closable', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'closable', {
|
||||
get: function () { return this.isClosable(); },
|
||||
set: function (close) { this.setClosable(close); }
|
||||
});
|
||||
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'movable', {
|
||||
Object.defineProperty(BaseWindow.prototype, 'movable', {
|
||||
get: function () { return this.isMovable(); },
|
||||
set: function (move) { this.setMovable(move); }
|
||||
});
|
||||
|
||||
TopLevelWindow.getFocusedWindow = () => {
|
||||
return TopLevelWindow.getAllWindows().find((win) => win.isFocused());
|
||||
BaseWindow.getFocusedWindow = () => {
|
||||
return BaseWindow.getAllWindows().find((win) => win.isFocused());
|
||||
};
|
||||
|
||||
module.exports = TopLevelWindow;
|
||||
module.exports = BaseWindow;
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events');
|
||||
const { BrowserView } = process.electronBinding('browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = BrowserView;
|
||||
15
lib/browser/api/browser-view.ts
Normal file
15
lib/browser/api/browser-view.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { BrowserView } = process._linkedBinding('electron_browser_browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents: Electron.WebContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default BrowserView;
|
||||
@@ -1,168 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const { TopLevelWindow, deprecate } = electron;
|
||||
const { BrowserWindow } = process.electronBinding('window');
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
|
||||
|
||||
BrowserWindow.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
TopLevelWindow.prototype._init.call(this);
|
||||
|
||||
// Avoid recursive require.
|
||||
const { app } = electron;
|
||||
|
||||
const nativeSetBounds = this.setBounds;
|
||||
this.setBounds = (bounds, ...opts) => {
|
||||
bounds = {
|
||||
...this.getBounds(),
|
||||
...bounds
|
||||
};
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// Sometimes the webContents doesn't get focus when window is shown, so we
|
||||
// have to force focusing on webContents in this case. The safest way is to
|
||||
// focus it when we first start to load URL, if we do it earlier it won't
|
||||
// have effect, if we do it later we might move focus in the page.
|
||||
//
|
||||
// Though this hack is only needed on macOS when the app is launched from
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
this.webContents.once('load-url', function () {
|
||||
this.focus();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event) => {
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
}
|
||||
};
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event, visibilityChanged);
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process.electronBinding('event').createEmpty();
|
||||
app.emit('browser-window-created', event, this);
|
||||
|
||||
Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get () {
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isBrowserWindow = (win) => {
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
|
||||
BrowserWindow.fromId = (id) => {
|
||||
const win = TopLevelWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win : null;
|
||||
};
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Helpers.
|
||||
Object.assign(BrowserWindow.prototype, {
|
||||
loadURL (...args) {
|
||||
return this.webContents.loadURL(...args);
|
||||
},
|
||||
getURL (...args) {
|
||||
return this.webContents.getURL();
|
||||
},
|
||||
loadFile (...args) {
|
||||
return this.webContents.loadFile(...args);
|
||||
},
|
||||
reload (...args) {
|
||||
return this.webContents.reload(...args);
|
||||
},
|
||||
send (...args) {
|
||||
return this.webContents.send(...args);
|
||||
},
|
||||
openDevTools (...args) {
|
||||
return this.webContents.openDevTools(...args);
|
||||
},
|
||||
closeDevTools () {
|
||||
return this.webContents.closeDevTools();
|
||||
},
|
||||
isDevToolsOpened () {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
},
|
||||
isDevToolsFocused () {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
},
|
||||
toggleDevTools () {
|
||||
return this.webContents.toggleDevTools();
|
||||
},
|
||||
inspectElement (...args) {
|
||||
return this.webContents.inspectElement(...args);
|
||||
},
|
||||
inspectSharedWorker () {
|
||||
return this.webContents.inspectSharedWorker();
|
||||
},
|
||||
inspectServiceWorker () {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
},
|
||||
showDefinitionForSelection () {
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
},
|
||||
capturePage (...args) {
|
||||
return this.webContents.capturePage(...args);
|
||||
},
|
||||
setTouchBar (touchBar) {
|
||||
electron.TouchBar._setOnWindow(touchBar, this);
|
||||
},
|
||||
getBackgroundThrottling () {
|
||||
return this.webContents.getBackgroundThrottling();
|
||||
},
|
||||
setBackgroundThrottling (allowed) {
|
||||
this.webContents.setBackgroundThrottling(allowed);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BrowserWindow;
|
||||
182
lib/browser/api/browser-window.ts
Normal file
182
lib/browser/api/browser-window.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron';
|
||||
import type { BrowserWindow as BWT } from 'electron';
|
||||
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);
|
||||
|
||||
(BrowserWindow.prototype as any)._init = function (this: BWT) {
|
||||
// Call parent class's _init.
|
||||
(BaseWindow.prototype as any)._init.call(this);
|
||||
|
||||
// Avoid recursive require.
|
||||
const { app } = require('electron');
|
||||
|
||||
const nativeSetBounds = this.setBounds;
|
||||
this.setBounds = (bounds, ...opts) => {
|
||||
bounds = {
|
||||
...this.getBounds(),
|
||||
...bounds
|
||||
};
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// Sometimes the webContents doesn't get focus when window is shown, so we
|
||||
// have to force focusing on webContents in this case. The safest way is to
|
||||
// focus it when we first start to load URL, if we do it earlier it won't
|
||||
// have effect, if we do it later we might move focus in the page.
|
||||
//
|
||||
// Though this hack is only needed on macOS when the app is launched from
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
this.webContents.once('load-url' as any, function (this: WebContents) {
|
||||
this.focus();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event: Event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event: Event) => {
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
}
|
||||
};
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event as any, visibilityChanged);
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process._linkedBinding('electron_browser_event').createEmpty();
|
||||
app.emit('browser-window-created', event, this);
|
||||
|
||||
Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get () {
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isBrowserWindow = (win: any) => {
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
|
||||
BrowserWindow.fromId = (id: number) => {
|
||||
const win = BaseWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win as any as BWT : null;
|
||||
};
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return BaseWindow.getAllWindows().filter(isBrowserWindow) as any[] as BWT[];
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents: WebContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView: BrowserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.setTouchBar = function (touchBar) {
|
||||
(TouchBar as any)._setOnWindow(touchBar, this);
|
||||
};
|
||||
|
||||
// Forwarded to webContents:
|
||||
|
||||
BrowserWindow.prototype.loadURL = function (...args) {
|
||||
return this.webContents.loadURL(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.getURL = function () {
|
||||
return this.webContents.getURL();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.loadFile = function (...args) {
|
||||
return this.webContents.loadFile(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.reload = function (...args) {
|
||||
return this.webContents.reload(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.send = function (...args) {
|
||||
return this.webContents.send(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.openDevTools = function (...args) {
|
||||
return this.webContents.openDevTools(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.closeDevTools = function () {
|
||||
return this.webContents.closeDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsOpened = function () {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsFocused = function () {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.toggleDevTools = function () {
|
||||
return this.webContents.toggleDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectElement = function (...args) {
|
||||
return this.webContents.inspectElement(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectSharedWorker = function () {
|
||||
return this.webContents.inspectSharedWorker();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectServiceWorker = function () {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.showDefinitionForSelection = function () {
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.capturePage = function (...args) {
|
||||
return this.webContents.capturePage(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.getBackgroundThrottling = function () {
|
||||
return this.webContents.getBackgroundThrottling();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.setBackgroundThrottling = function (allowed: boolean) {
|
||||
return this.webContents.setBackgroundThrottling(allowed);
|
||||
};
|
||||
|
||||
module.exports = BrowserWindow;
|
||||
@@ -1,2 +0,0 @@
|
||||
'use strict';
|
||||
module.exports = process.electronBinding('content_tracing');
|
||||
1
lib/browser/api/content-tracing.ts
Normal file
1
lib/browser/api/content-tracing.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default process._linkedBinding('electron_browser_content_tracing');
|
||||
@@ -1,6 +1,6 @@
|
||||
import { app } from 'electron';
|
||||
import { app, deprecate } from 'electron';
|
||||
|
||||
const binding = process.electronBinding('crash_reporter');
|
||||
const binding = process._linkedBinding('electron_browser_crash_reporter');
|
||||
|
||||
class CrashReporter {
|
||||
start (options: Electron.CrashReporterStartOptions) {
|
||||
@@ -18,6 +18,10 @@ class CrashReporter {
|
||||
|
||||
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
|
||||
|
||||
if (!compress) {
|
||||
deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.');
|
||||
}
|
||||
|
||||
const appVersion = app.getVersion();
|
||||
|
||||
if (companyName && globalExtra._companyName == null) globalExtra._companyName = companyName;
|
||||
|
||||
5
lib/browser/api/desktop-capturer.ts
Normal file
5
lib/browser/api/desktop-capturer.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { getSourcesImpl } from '@electron/internal/browser/desktop-capturer';
|
||||
|
||||
export async function getSources (options: Electron.SourcesOptions) {
|
||||
return getSourcesImpl(null, options);
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { app, BrowserWindow, deprecate } = require('electron');
|
||||
const binding = process.electronBinding('dialog');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const DialogType = {
|
||||
OPEN: 'OPEN',
|
||||
SAVE: 'SAVE'
|
||||
};
|
||||
|
||||
const saveFileDialogProperties = {
|
||||
createDirectory: 1 << 0,
|
||||
showHiddenFiles: 1 << 1,
|
||||
treatPackageAsDirectory: 1 << 2,
|
||||
showOverwriteConfirmation: 1 << 3,
|
||||
dontAddToRecent: 1 << 4
|
||||
};
|
||||
|
||||
const openFileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
openDirectory: 1 << 1,
|
||||
multiSelections: 1 << 2,
|
||||
createDirectory: 1 << 3, // macOS
|
||||
showHiddenFiles: 1 << 4,
|
||||
promptToCreate: 1 << 5, // Windows
|
||||
noResolveAliases: 1 << 6, // macOS
|
||||
treatPackageAsDirectory: 1 << 7, // macOS
|
||||
dontAddToRecent: 1 << 8 // Windows
|
||||
};
|
||||
|
||||
const normalizeAccessKey = (text) => {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
// macOS does not have access keys so remove single ampersands
|
||||
// and replace double ampersands with a single ampersand
|
||||
if (process.platform === 'darwin') {
|
||||
return text.replace(/&(&?)/g, '$1');
|
||||
}
|
||||
|
||||
// Linux uses a single underscore as an access key prefix so escape
|
||||
// existing single underscores with a second underscore, replace double
|
||||
// ampersands with a single ampersand, and replace a single ampersand with
|
||||
// a single underscore
|
||||
if (process.platform === 'linux') {
|
||||
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
|
||||
if (after === '&') return after;
|
||||
return `_${after}`;
|
||||
});
|
||||
}
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
const checkAppInitialized = function () {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
};
|
||||
|
||||
const setupDialogProperties = (type, properties) => {
|
||||
const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties;
|
||||
let dialogProperties = 0;
|
||||
for (const prop in dialogPropertiesTypes) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= dialogPropertiesTypes[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const saveDialog = (sync, window, options) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { title: 'Save' };
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = [],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false,
|
||||
nameFieldLabel = '',
|
||||
showsTagField = true
|
||||
} = options;
|
||||
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string');
|
||||
|
||||
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
|
||||
settings.properties = setupDialogProperties(DialogType.SAVE, properties);
|
||||
|
||||
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
|
||||
};
|
||||
|
||||
const openDialog = (sync, window, options) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = ['openFile'],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options;
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
|
||||
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
|
||||
settings.properties = setupDialogProperties(DialogType.OPEN, properties);
|
||||
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
|
||||
};
|
||||
|
||||
const messageBox = (sync, window, options) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { type: 'none' };
|
||||
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
const messageBoxOptions = { noLink: 1 << 0 };
|
||||
|
||||
let {
|
||||
buttons = [],
|
||||
cancelId,
|
||||
checkboxLabel = '',
|
||||
checkboxChecked,
|
||||
defaultId = -1,
|
||||
detail = '',
|
||||
icon = null,
|
||||
noLink = false,
|
||||
message = '',
|
||||
title = '',
|
||||
type = 'none'
|
||||
} = options;
|
||||
|
||||
const messageBoxType = messageBoxTypes.indexOf(type);
|
||||
if (messageBoxType === -1) throw new TypeError('Invalid message box type');
|
||||
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array');
|
||||
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey);
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
|
||||
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
|
||||
|
||||
checkboxChecked = !!checkboxChecked;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
|
||||
}
|
||||
|
||||
// Choose a default button to get selected when dialog is cancelled.
|
||||
if (cancelId == null) {
|
||||
// If the defaultId is set to 0, ensure the cancel button is a different index (1)
|
||||
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = buttons[i].toLowerCase();
|
||||
if (text === 'cancel' || text === 'no') {
|
||||
cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const settings = {
|
||||
window,
|
||||
messageBoxType,
|
||||
buttons,
|
||||
defaultId,
|
||||
cancelId,
|
||||
noLink,
|
||||
title,
|
||||
message,
|
||||
detail,
|
||||
checkboxLabel,
|
||||
checkboxChecked,
|
||||
icon
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
return binding.showMessageBoxSync(settings);
|
||||
} else {
|
||||
return binding.showMessageBox(settings);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function (window, options) {
|
||||
return openDialog(false, window, options);
|
||||
},
|
||||
|
||||
showOpenDialogSync: function (window, options) {
|
||||
return openDialog(true, window, options);
|
||||
},
|
||||
|
||||
showSaveDialog: function (window, options) {
|
||||
return saveDialog(false, window, options);
|
||||
},
|
||||
|
||||
showSaveDialogSync: function (window, options) {
|
||||
return saveDialog(true, window, options);
|
||||
},
|
||||
|
||||
showMessageBox: function (window, options) {
|
||||
return messageBox(false, window, options);
|
||||
},
|
||||
|
||||
showMessageBoxSync: function (window, options) {
|
||||
return messageBox(true, window, options);
|
||||
},
|
||||
|
||||
showErrorBox: function (...args) {
|
||||
return binding.showErrorBox(...args);
|
||||
},
|
||||
|
||||
showCertificateTrustDialog: function (window, options) {
|
||||
if (window && window.constructor !== BrowserWindow) options = window;
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object');
|
||||
}
|
||||
|
||||
const { certificate, message = '' } = options;
|
||||
if (certificate == null || typeof certificate !== 'object') {
|
||||
throw new TypeError('certificate must be an object');
|
||||
}
|
||||
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string');
|
||||
|
||||
return binding.showCertificateTrustDialog(window, certificate, message);
|
||||
}
|
||||
};
|
||||
306
lib/browser/api/dialog.ts
Normal file
306
lib/browser/api/dialog.ts
Normal file
@@ -0,0 +1,306 @@
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import { OpenDialogOptions, OpenDialogReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, MessageBoxReturnValue, CertificateTrustDialogOptions } from 'electron/main';
|
||||
const dialogBinding = process._linkedBinding('electron_browser_dialog');
|
||||
|
||||
const DialogType = {
|
||||
OPEN: 'OPEN' as 'OPEN',
|
||||
SAVE: 'SAVE' as 'SAVE'
|
||||
};
|
||||
|
||||
enum SaveFileDialogProperties {
|
||||
createDirectory = 1 << 0,
|
||||
showHiddenFiles = 1 << 1,
|
||||
treatPackageAsDirectory = 1 << 2,
|
||||
showOverwriteConfirmation = 1 << 3,
|
||||
dontAddToRecent = 1 << 4
|
||||
}
|
||||
|
||||
enum OpenFileDialogProperties {
|
||||
openFile = 1 << 0,
|
||||
openDirectory = 1 << 1,
|
||||
multiSelections = 1 << 2,
|
||||
createDirectory = 1 << 3, // macOS
|
||||
showHiddenFiles = 1 << 4,
|
||||
promptToCreate = 1 << 5, // Windows
|
||||
noResolveAliases = 1 << 6, // macOS
|
||||
treatPackageAsDirectory = 1 << 7, // macOS
|
||||
dontAddToRecent = 1 << 8 // Windows
|
||||
}
|
||||
|
||||
const normalizeAccessKey = (text: string) => {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
// macOS does not have access keys so remove single ampersands
|
||||
// and replace double ampersands with a single ampersand
|
||||
if (process.platform === 'darwin') {
|
||||
return text.replace(/&(&?)/g, '$1');
|
||||
}
|
||||
|
||||
// Linux uses a single underscore as an access key prefix so escape
|
||||
// existing single underscores with a second underscore, replace double
|
||||
// ampersands with a single ampersand, and replace a single ampersand with
|
||||
// a single underscore
|
||||
if (process.platform === 'linux') {
|
||||
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
|
||||
if (after === '&') return after;
|
||||
return `_${after}`;
|
||||
});
|
||||
}
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
const checkAppInitialized = function () {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
};
|
||||
|
||||
const setupOpenDialogProperties = (properties: (keyof typeof OpenFileDialogProperties)[]): number => {
|
||||
let dialogProperties = 0;
|
||||
for (const property of properties) {
|
||||
if (Object.prototype.hasOwnProperty.call(OpenFileDialogProperties, property)) { dialogProperties |= OpenFileDialogProperties[property]; }
|
||||
}
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const setupSaveDialogProperties = (properties: (keyof typeof SaveFileDialogProperties)[]): number => {
|
||||
let dialogProperties = 0;
|
||||
for (const property of properties) {
|
||||
if (Object.prototype.hasOwnProperty.call(SaveFileDialogProperties, property)) { dialogProperties |= SaveFileDialogProperties[property]; }
|
||||
}
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const setupDialogProperties = (type: keyof typeof DialogType, properties: string[]): number => {
|
||||
if (type === DialogType.OPEN) {
|
||||
return setupOpenDialogProperties(properties as (keyof typeof OpenFileDialogProperties)[]);
|
||||
} else if (type === DialogType.SAVE) {
|
||||
return setupSaveDialogProperties(properties as (keyof typeof SaveFileDialogProperties)[]);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const saveDialog = (sync: boolean, window: BrowserWindow | null, options?: SaveDialogOptions) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (options == null) options = { title: 'Save' };
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = [],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false,
|
||||
nameFieldLabel = '',
|
||||
showsTagField = true
|
||||
} = options;
|
||||
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string');
|
||||
|
||||
const settings = {
|
||||
buttonLabel,
|
||||
defaultPath,
|
||||
filters,
|
||||
title,
|
||||
message,
|
||||
securityScopedBookmarks,
|
||||
nameFieldLabel,
|
||||
showsTagField,
|
||||
window,
|
||||
properties: setupDialogProperties(DialogType.SAVE, properties)
|
||||
};
|
||||
|
||||
return sync ? dialogBinding.showSaveDialogSync(settings) : dialogBinding.showSaveDialog(settings);
|
||||
};
|
||||
|
||||
const openDialog = (sync: boolean, window: BrowserWindow | null, options?: OpenDialogOptions) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = ['openFile'],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options;
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
|
||||
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
|
||||
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
|
||||
const settings = {
|
||||
title,
|
||||
buttonLabel,
|
||||
defaultPath,
|
||||
filters,
|
||||
message,
|
||||
securityScopedBookmarks,
|
||||
window,
|
||||
properties: setupDialogProperties(DialogType.OPEN, properties)
|
||||
};
|
||||
|
||||
return (sync) ? dialogBinding.showOpenDialogSync(settings) : dialogBinding.showOpenDialog(settings);
|
||||
};
|
||||
|
||||
const messageBox = (sync: boolean, window: BrowserWindow | null, options?: MessageBoxOptions) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (options == null) options = { type: 'none', message: '' };
|
||||
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
|
||||
let {
|
||||
buttons = [],
|
||||
cancelId,
|
||||
checkboxLabel = '',
|
||||
checkboxChecked,
|
||||
defaultId = -1,
|
||||
detail = '',
|
||||
icon = null,
|
||||
noLink = false,
|
||||
message = '',
|
||||
title = '',
|
||||
type = 'none'
|
||||
} = options;
|
||||
|
||||
const messageBoxType = messageBoxTypes.indexOf(type);
|
||||
if (messageBoxType === -1) throw new TypeError('Invalid message box type');
|
||||
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array');
|
||||
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey);
|
||||
if (typeof title !== 'string') throw new TypeError('Title must be a string');
|
||||
if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
|
||||
if (typeof message !== 'string') throw new TypeError('Message must be a string');
|
||||
if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
|
||||
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
|
||||
|
||||
checkboxChecked = !!checkboxChecked;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
|
||||
}
|
||||
|
||||
// Choose a default button to get selected when dialog is cancelled.
|
||||
if (cancelId == null) {
|
||||
// If the defaultId is set to 0, ensure the cancel button is a different index (1)
|
||||
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = buttons[i].toLowerCase();
|
||||
if (text === 'cancel' || text === 'no') {
|
||||
cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const settings = {
|
||||
window,
|
||||
messageBoxType,
|
||||
buttons,
|
||||
defaultId,
|
||||
cancelId,
|
||||
noLink,
|
||||
title,
|
||||
message,
|
||||
detail,
|
||||
checkboxLabel,
|
||||
checkboxChecked,
|
||||
icon
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
return dialogBinding.showMessageBoxSync(settings);
|
||||
} else {
|
||||
return dialogBinding.showMessageBox(settings);
|
||||
}
|
||||
};
|
||||
|
||||
// eat dirt, eslint
|
||||
/* eslint-disable import/export */
|
||||
export function showOpenDialog(window: BrowserWindow, options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialog(options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialog (windowOrOptions: BrowserWindow | OpenDialogOptions, maybeOptions?: OpenDialogOptions): OpenDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return openDialog(false, window, options);
|
||||
}
|
||||
|
||||
export function showOpenDialogSync(window: BrowserWindow, options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialogSync(options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialogSync (windowOrOptions: BrowserWindow | OpenDialogOptions, maybeOptions?: OpenDialogOptions): OpenDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return openDialog(true, window, options);
|
||||
}
|
||||
|
||||
export function showSaveDialog(window: BrowserWindow, options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialog(options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialog (windowOrOptions: BrowserWindow | SaveDialogOptions, maybeOptions?: SaveDialogOptions): SaveDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return saveDialog(false, window, options);
|
||||
}
|
||||
|
||||
export function showSaveDialogSync(window: BrowserWindow, options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialogSync(options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialogSync (windowOrOptions: BrowserWindow | SaveDialogOptions, maybeOptions?: SaveDialogOptions): SaveDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return saveDialog(true, window, options);
|
||||
}
|
||||
|
||||
export function showMessageBox(window: BrowserWindow, options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBox(options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBox (windowOrOptions: BrowserWindow | MessageBoxOptions, maybeOptions?: MessageBoxOptions): MessageBoxReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return messageBox(false, window, options);
|
||||
}
|
||||
|
||||
export function showMessageBoxSync(window: BrowserWindow, options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBoxSync(options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBoxSync (windowOrOptions: BrowserWindow | MessageBoxOptions, maybeOptions?: MessageBoxOptions): MessageBoxReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return messageBox(true, window, options);
|
||||
}
|
||||
|
||||
export function showErrorBox (...args: any[]) {
|
||||
return dialogBinding.showErrorBox(...args);
|
||||
}
|
||||
|
||||
export function showCertificateTrustDialog (windowOrOptions: BrowserWindow | CertificateTrustDialogOptions, maybeOptions?: CertificateTrustDialogOptions) {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object');
|
||||
}
|
||||
|
||||
const { certificate, message = '' } = options;
|
||||
if (certificate == null || typeof certificate !== 'object') {
|
||||
throw new TypeError('certificate must be an object');
|
||||
}
|
||||
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string');
|
||||
|
||||
return dialogBinding.showCertificateTrustDialog(window, certificate, message);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('global_shortcut').globalShortcut;
|
||||
2
lib/browser/api/global-shortcut.ts
Normal file
2
lib/browser/api/global-shortcut.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
const { globalShortcut } = process._linkedBinding('electron_browser_global_shortcut');
|
||||
export default globalShortcut;
|
||||
@@ -1,22 +1,24 @@
|
||||
'use strict';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { deprecate } = require('electron');
|
||||
let _inAppPurchase;
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const { EventEmitter } = require('events');
|
||||
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase');
|
||||
const { inAppPurchase, InAppPurchase } = process._linkedBinding('electron_browser_in_app_purchase');
|
||||
|
||||
// inAppPurchase is an EventEmitter.
|
||||
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(inAppPurchase);
|
||||
|
||||
module.exports = inAppPurchase;
|
||||
_inAppPurchase = inAppPurchase;
|
||||
} else {
|
||||
module.exports = {
|
||||
purchaseProduct: (productID, quantity, callback) => {
|
||||
_inAppPurchase = new EventEmitter();
|
||||
Object.assign(_inAppPurchase, {
|
||||
purchaseProduct: () => {
|
||||
throw new Error('The inAppPurchase module can only be used on macOS');
|
||||
},
|
||||
canMakePayments: () => false,
|
||||
getReceiptURL: () => ''
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default _inAppPurchase;
|
||||
@@ -1,43 +1,56 @@
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron');
|
||||
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron';
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
const roles = {
|
||||
type RoleId = 'about' | 'close' | 'copy' | 'cut' | 'delete' | 'forcereload' | 'front' | 'help' | 'hide' | 'hideothers' | 'minimize' |
|
||||
'paste' | 'pasteandmatchstyle' | 'quit' | 'redo' | 'reload' | 'resetzoom' | 'selectall' | 'services' | 'recentdocuments' | 'clearrecentdocuments' | 'startspeaking' | 'stopspeaking' |
|
||||
'toggledevtools' | 'togglefullscreen' | 'undo' | 'unhide' | 'window' | 'zoom' | 'zoomin' | 'zoomout' | 'appmenu' | 'filemenu' | 'editmenu' | 'viewmenu' | 'windowmenu'
|
||||
interface Role {
|
||||
label: string;
|
||||
accelerator?: string;
|
||||
windowMethod?: ((window: BrowserWindow) => void);
|
||||
webContentsMethod?: ((webContents: WebContents) => void);
|
||||
appMethod?: () => void;
|
||||
registerAccelerator?: boolean;
|
||||
nonNativeMacOSRole?: boolean;
|
||||
submenu?: MenuItemConstructorOptions[];
|
||||
}
|
||||
|
||||
export const roleList: Record<RoleId, Role> = {
|
||||
about: {
|
||||
get label () {
|
||||
return isLinux ? 'About' : `About ${app.name}`;
|
||||
}
|
||||
},
|
||||
...(isWindows && { appMethod: () => app.showAboutPanel() })
|
||||
},
|
||||
close: {
|
||||
label: isMac ? 'Close Window' : 'Close',
|
||||
accelerator: 'CommandOrControl+W',
|
||||
windowMethod: 'close'
|
||||
windowMethod: w => w.close()
|
||||
},
|
||||
copy: {
|
||||
label: 'Copy',
|
||||
accelerator: 'CommandOrControl+C',
|
||||
webContentsMethod: 'copy',
|
||||
webContentsMethod: wc => wc.copy(),
|
||||
registerAccelerator: false
|
||||
},
|
||||
cut: {
|
||||
label: 'Cut',
|
||||
accelerator: 'CommandOrControl+X',
|
||||
webContentsMethod: 'cut',
|
||||
webContentsMethod: wc => wc.cut(),
|
||||
registerAccelerator: false
|
||||
},
|
||||
delete: {
|
||||
label: 'Delete',
|
||||
webContentsMethod: 'delete'
|
||||
webContentsMethod: wc => wc.delete()
|
||||
},
|
||||
forcereload: {
|
||||
label: 'Force Reload',
|
||||
accelerator: 'Shift+CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: (window) => {
|
||||
windowMethod: (window: BrowserWindow) => {
|
||||
window.webContents.reloadIgnoringCache();
|
||||
}
|
||||
},
|
||||
@@ -60,18 +73,18 @@ const roles = {
|
||||
minimize: {
|
||||
label: 'Minimize',
|
||||
accelerator: 'CommandOrControl+M',
|
||||
windowMethod: 'minimize'
|
||||
windowMethod: w => w.minimize()
|
||||
},
|
||||
paste: {
|
||||
label: 'Paste',
|
||||
accelerator: 'CommandOrControl+V',
|
||||
webContentsMethod: 'paste',
|
||||
webContentsMethod: wc => wc.paste(),
|
||||
registerAccelerator: false
|
||||
},
|
||||
pasteandmatchstyle: {
|
||||
label: 'Paste and Match Style',
|
||||
accelerator: 'Shift+CommandOrControl+V',
|
||||
webContentsMethod: 'pasteAndMatchStyle',
|
||||
accelerator: isMac ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V',
|
||||
webContentsMethod: wc => wc.pasteAndMatchStyle(),
|
||||
registerAccelerator: false
|
||||
},
|
||||
quit: {
|
||||
@@ -83,31 +96,31 @@ const roles = {
|
||||
}
|
||||
},
|
||||
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
|
||||
appMethod: 'quit'
|
||||
appMethod: () => app.quit()
|
||||
},
|
||||
redo: {
|
||||
label: 'Redo',
|
||||
accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
|
||||
webContentsMethod: 'redo'
|
||||
webContentsMethod: wc => wc.redo()
|
||||
},
|
||||
reload: {
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: 'reload'
|
||||
windowMethod: w => w.reload()
|
||||
},
|
||||
resetzoom: {
|
||||
label: 'Actual Size',
|
||||
accelerator: 'CommandOrControl+0',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContentsMethod: (webContents: WebContents) => {
|
||||
webContents.zoomLevel = 0;
|
||||
}
|
||||
},
|
||||
selectall: {
|
||||
label: 'Select All',
|
||||
accelerator: 'CommandOrControl+A',
|
||||
webContentsMethod: 'selectAll'
|
||||
webContentsMethod: wc => wc.selectAll()
|
||||
},
|
||||
services: {
|
||||
label: 'Services'
|
||||
@@ -128,19 +141,19 @@ const roles = {
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: 'toggleDevTools'
|
||||
windowMethod: w => w.webContents.toggleDevTools()
|
||||
},
|
||||
togglefullscreen: {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: isMac ? 'Control+Command+F' : 'F11',
|
||||
windowMethod: (window) => {
|
||||
windowMethod: (window: BrowserWindow) => {
|
||||
window.setFullScreen(!window.isFullScreen());
|
||||
}
|
||||
},
|
||||
undo: {
|
||||
label: 'Undo',
|
||||
accelerator: 'CommandOrControl+Z',
|
||||
webContentsMethod: 'undo'
|
||||
webContentsMethod: wc => wc.undo()
|
||||
},
|
||||
unhide: {
|
||||
label: 'Show All'
|
||||
@@ -155,7 +168,7 @@ const roles = {
|
||||
label: 'Zoom In',
|
||||
accelerator: 'CommandOrControl+Plus',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContentsMethod: (webContents: WebContents) => {
|
||||
webContents.zoomLevel += 0.5;
|
||||
}
|
||||
},
|
||||
@@ -163,7 +176,7 @@ const roles = {
|
||||
label: 'Zoom Out',
|
||||
accelerator: 'CommandOrControl+-',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContentsMethod: (webContents: WebContents) => {
|
||||
webContents.zoomLevel -= 0.5;
|
||||
}
|
||||
},
|
||||
@@ -213,11 +226,11 @@ const roles = {
|
||||
{ role: 'stopSpeaking' }
|
||||
]
|
||||
}
|
||||
] : [
|
||||
] as MenuItemConstructorOptions[] : [
|
||||
{ role: 'delete' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'selectAll' }
|
||||
])
|
||||
] as MenuItemConstructorOptions[])
|
||||
]
|
||||
},
|
||||
// View submenu
|
||||
@@ -244,40 +257,38 @@ const roles = {
|
||||
...(isMac ? [
|
||||
{ type: 'separator' },
|
||||
{ role: 'front' }
|
||||
] : [
|
||||
] as MenuItemConstructorOptions[] : [
|
||||
{ role: 'close' }
|
||||
])
|
||||
] as MenuItemConstructorOptions[])
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
exports.roleList = roles;
|
||||
|
||||
const canExecuteRole = (role) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(roles, role)) return false;
|
||||
const canExecuteRole = (role: keyof typeof roleList) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return false;
|
||||
if (!isMac) return true;
|
||||
|
||||
// macOS handles all roles natively except for a few
|
||||
return roles[role].nonNativeMacOSRole;
|
||||
return roleList[role].nonNativeMacOSRole;
|
||||
};
|
||||
|
||||
exports.getDefaultLabel = (role) => {
|
||||
return Object.prototype.hasOwnProperty.call(roles, role) ? roles[role].label : '';
|
||||
};
|
||||
export function getDefaultLabel (role: RoleId) {
|
||||
return Object.prototype.hasOwnProperty.call(roleList, role) ? roleList[role].label : '';
|
||||
}
|
||||
|
||||
exports.getDefaultAccelerator = (role) => {
|
||||
if (Object.prototype.hasOwnProperty.call(roles, role)) return roles[role].accelerator;
|
||||
};
|
||||
export function getDefaultAccelerator (role: RoleId) {
|
||||
if (Object.prototype.hasOwnProperty.call(roleList, role)) return roleList[role].accelerator;
|
||||
}
|
||||
|
||||
exports.shouldRegisterAccelerator = (role) => {
|
||||
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roles, role) && roles[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true;
|
||||
};
|
||||
export function shouldRegisterAccelerator (role: RoleId) {
|
||||
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roleList, role) && roleList[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roleList[role].registerAccelerator : true;
|
||||
}
|
||||
|
||||
exports.getDefaultSubmenu = (role) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(roles, role)) return;
|
||||
export function getDefaultSubmenu (role: RoleId) {
|
||||
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return;
|
||||
|
||||
let { submenu } = roles[role];
|
||||
let { submenu } = roleList[role];
|
||||
|
||||
// remove null items from within the submenu
|
||||
if (Array.isArray(submenu)) {
|
||||
@@ -285,35 +296,27 @@ exports.getDefaultSubmenu = (role) => {
|
||||
}
|
||||
|
||||
return submenu;
|
||||
};
|
||||
}
|
||||
|
||||
exports.execute = (role, focusedWindow, focusedWebContents) => {
|
||||
export function execute (role: RoleId, focusedWindow: BrowserWindow, focusedWebContents: WebContents) {
|
||||
if (!canExecuteRole(role)) return false;
|
||||
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role];
|
||||
const { appMethod, webContentsMethod, windowMethod } = roleList[role];
|
||||
|
||||
if (appMethod) {
|
||||
app[appMethod]();
|
||||
appMethod();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (windowMethod && focusedWindow != null) {
|
||||
if (typeof windowMethod === 'function') {
|
||||
windowMethod(focusedWindow);
|
||||
} else {
|
||||
focusedWindow[windowMethod]();
|
||||
}
|
||||
windowMethod(focusedWindow);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (webContentsMethod && focusedWebContents != null) {
|
||||
if (typeof webContentsMethod === 'function') {
|
||||
webContentsMethod(focusedWebContents);
|
||||
} else {
|
||||
focusedWebContents[webContentsMethod]();
|
||||
}
|
||||
webContentsMethod(focusedWebContents);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const roles = require('@electron/internal/browser/api/menu-item-roles');
|
||||
import * as roles from './menu-item-roles';
|
||||
import { Menu, Event, BrowserWindow, WebContents } from 'electron';
|
||||
|
||||
let nextCommandId = 0;
|
||||
|
||||
const MenuItem = function (options) {
|
||||
const { Menu } = require('electron');
|
||||
|
||||
const MenuItem = function (this: any, options: any) {
|
||||
// Preserve extra fields specified by user
|
||||
for (const key in options) {
|
||||
if (!(key in this)) this[key] = options[key];
|
||||
@@ -47,7 +44,7 @@ const MenuItem = function (options) {
|
||||
this.overrideReadOnlyProperty('commandId', ++nextCommandId);
|
||||
|
||||
const click = options.click;
|
||||
this.click = (event, focusedWindow, focusedWebContents) => {
|
||||
this.click = (event: Event, focusedWindow: BrowserWindow, focusedWebContents: WebContents) => {
|
||||
// Manually flip the checked flags when clicked.
|
||||
if (this.type === 'checkbox' || this.type === 'radio') {
|
||||
this.checked = !this.checked;
|
||||
@@ -69,13 +66,13 @@ MenuItem.prototype.getDefaultRoleAccelerator = function () {
|
||||
return roles.getDefaultAccelerator(this.role);
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
|
||||
MenuItem.prototype.overrideProperty = function (name: string, defaultValue: any = null) {
|
||||
if (this[name] == null) {
|
||||
this[name] = defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function (name: string, defaultValue: any) {
|
||||
this.overrideProperty(name, defaultValue);
|
||||
Object.defineProperty(this, name, {
|
||||
enumerable: true,
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
function splitArray (arr, predicate) {
|
||||
function splitArray<T> (arr: T[], predicate: (x: T) => boolean) {
|
||||
const result = arr.reduce((multi, item) => {
|
||||
const current = multi[multi.length - 1];
|
||||
if (predicate(item)) {
|
||||
@@ -9,7 +7,7 @@ function splitArray (arr, predicate) {
|
||||
current.push(item);
|
||||
}
|
||||
return multi;
|
||||
}, [[]]);
|
||||
}, [[]] as T[][]);
|
||||
|
||||
if (result[result.length - 1].length === 0) {
|
||||
return result.slice(0, result.length - 1);
|
||||
@@ -17,7 +15,7 @@ function splitArray (arr, predicate) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function joinArrays (arrays, joinIDs) {
|
||||
function joinArrays (arrays: any[][], joinIDs: any[]) {
|
||||
return arrays.reduce((joined, arr, i) => {
|
||||
if (i > 0 && arr.length) {
|
||||
if (joinIDs.length > 0) {
|
||||
@@ -31,14 +29,14 @@ function joinArrays (arrays, joinIDs) {
|
||||
}, []);
|
||||
}
|
||||
|
||||
function pushOntoMultiMap (map, key, value) {
|
||||
function pushOntoMultiMap<K, V> (map: Map<K, V[]>, key: K, value: V) {
|
||||
if (!map.has(key)) {
|
||||
map.set(key, []);
|
||||
}
|
||||
map.get(key).push(value);
|
||||
map.get(key)!.push(value);
|
||||
}
|
||||
|
||||
function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
function indexOfGroupContainingID<T> (groups: {id?: T}[][], id: T, ignoreGroup: {id?: T}[]) {
|
||||
return groups.findIndex(
|
||||
candidateGroup =>
|
||||
candidateGroup !== ignoreGroup &&
|
||||
@@ -50,11 +48,11 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
|
||||
// Sort nodes topologically using a depth-first approach. Encountered cycles
|
||||
// are broken.
|
||||
function sortTopologically (originalOrder, edgesById) {
|
||||
const sorted = [];
|
||||
const marked = new Set();
|
||||
function sortTopologically<T> (originalOrder: T[], edgesById: Map<T, T[]>) {
|
||||
const sorted = [] as T[];
|
||||
const marked = new Set<T>();
|
||||
|
||||
const visit = (mark) => {
|
||||
const visit = (mark: T) => {
|
||||
if (marked.has(mark)) return;
|
||||
marked.add(mark);
|
||||
const edges = edgesById.get(mark);
|
||||
@@ -68,7 +66,7 @@ function sortTopologically (originalOrder, edgesById) {
|
||||
return sorted;
|
||||
}
|
||||
|
||||
function attemptToMergeAGroup (groups) {
|
||||
function attemptToMergeAGroup<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
@@ -87,7 +85,7 @@ function attemptToMergeAGroup (groups) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function mergeGroups (groups) {
|
||||
function mergeGroups<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
|
||||
let merged = true;
|
||||
while (merged) {
|
||||
merged = attemptToMergeAGroup(groups);
|
||||
@@ -95,7 +93,7 @@ function mergeGroups (groups) {
|
||||
return groups;
|
||||
}
|
||||
|
||||
function sortItemsInGroup (group) {
|
||||
function sortItemsInGroup<T> (group: {before?: T[], after?: T[], id?: T}[]) {
|
||||
const originalOrder = group.map((node, i) => i);
|
||||
const edges = new Map();
|
||||
const idToIndex = new Map(group.map((item, i) => [item.id, i]));
|
||||
@@ -123,7 +121,7 @@ function sortItemsInGroup (group) {
|
||||
return sortedNodes.map(i => group[i]);
|
||||
}
|
||||
|
||||
function findEdgesInGroup (groups, i, edges) {
|
||||
function findEdgesInGroup<T> (groups: {beforeGroupContaining?: T[], afterGroupContaining?: T[], id?: T}[][], i: number, edges: Map<any, any>) {
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
if (item.beforeGroupContaining) {
|
||||
@@ -147,7 +145,7 @@ function findEdgesInGroup (groups, i, edges) {
|
||||
}
|
||||
}
|
||||
|
||||
function sortGroups (groups) {
|
||||
function sortGroups<T> (groups: {id?: T}[][]) {
|
||||
const originalOrder = groups.map((item, i) => i);
|
||||
const edges = new Map();
|
||||
|
||||
@@ -159,8 +157,8 @@ function sortGroups (groups) {
|
||||
return sortedGroupIndexes.map(i => groups[i]);
|
||||
}
|
||||
|
||||
function sortMenuItems (menuItems) {
|
||||
const isSeparator = (item) => item.type === 'separator';
|
||||
export function sortMenuItems (menuItems: {type?: string, id?: string}[]) {
|
||||
const isSeparator = (item: {type?: string}) => item.type === 'separator';
|
||||
const separators = menuItems.filter(i => i.type === 'separator');
|
||||
|
||||
// Split the items into their implicit groups based upon separators.
|
||||
@@ -172,5 +170,3 @@ function sortMenuItems (menuItems) {
|
||||
const joined = joinArrays(sortedGroups, separators);
|
||||
return joined;
|
||||
}
|
||||
|
||||
module.exports = { sortMenuItems };
|
||||
@@ -1,13 +1,11 @@
|
||||
'use strict';
|
||||
import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron';
|
||||
import { sortMenuItems } from './menu-utils';
|
||||
|
||||
const { TopLevelWindow, MenuItem, webContents } = require('electron');
|
||||
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
const bindings = process.electronBinding('menu');
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const bindings = process._linkedBinding('electron_browser_menu');
|
||||
|
||||
const { Menu } = bindings;
|
||||
let applicationMenu = null;
|
||||
const { Menu } = bindings as { Menu: typeof MenuType };
|
||||
let applicationMenu: MenuType | null = null;
|
||||
let groupIdIndex = 0;
|
||||
|
||||
/* Instance Methods */
|
||||
@@ -19,17 +17,17 @@ Menu.prototype._init = function () {
|
||||
};
|
||||
|
||||
Menu.prototype._isCommandIdChecked = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].checked : undefined;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].checked : false;
|
||||
};
|
||||
|
||||
Menu.prototype._isCommandIdEnabled = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].enabled : undefined;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].enabled : false;
|
||||
};
|
||||
Menu.prototype._shouldCommandIdWorkWhenHidden = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].acceleratorWorksWhenHidden : undefined;
|
||||
return this.commandsMap[id] ? !!this.commandsMap[id].acceleratorWorksWhenHidden : false;
|
||||
};
|
||||
Menu.prototype._isCommandIdVisible = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].visible : undefined;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].visible : false;
|
||||
};
|
||||
|
||||
Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) {
|
||||
@@ -40,13 +38,14 @@ Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator
|
||||
};
|
||||
|
||||
Menu.prototype._shouldRegisterAcceleratorForCommandId = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : undefined;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : false;
|
||||
};
|
||||
|
||||
Menu.prototype._executeCommand = function (event, id) {
|
||||
const command = this.commandsMap[id];
|
||||
if (!command) return;
|
||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
|
||||
const focusedWindow = BaseWindow.getFocusedWindow();
|
||||
command.click(event, focusedWindow instanceof BrowserWindow ? focusedWindow : undefined, webContents.getFocusedWebContents());
|
||||
};
|
||||
|
||||
Menu.prototype._menuWillShow = function () {
|
||||
@@ -72,23 +71,23 @@ Menu.prototype.popup = function (options = {}) {
|
||||
if (typeof positioningItem !== 'number') positioningItem = -1;
|
||||
|
||||
// find which window to use
|
||||
const wins = TopLevelWindow.getAllWindows();
|
||||
if (!wins || wins.indexOf(window) === -1) {
|
||||
window = TopLevelWindow.getFocusedWindow();
|
||||
const wins = BaseWindow.getAllWindows();
|
||||
if (!wins || wins.indexOf(window as any) === -1) {
|
||||
window = BaseWindow.getFocusedWindow() as any;
|
||||
if (!window && wins && wins.length > 0) {
|
||||
window = wins[0];
|
||||
window = wins[0] as any;
|
||||
}
|
||||
if (!window) {
|
||||
throw new Error('Cannot open Menu without a TopLevelWindow present');
|
||||
throw new Error('Cannot open Menu without a BaseWindow present');
|
||||
}
|
||||
}
|
||||
|
||||
this.popupAt(window, x, y, positioningItem, callback);
|
||||
this.popupAt(window as unknown as BaseWindow, x, y, positioningItem, callback);
|
||||
return { browserWindow: window, x, y, position: positioningItem };
|
||||
};
|
||||
|
||||
Menu.prototype.closePopup = function (window) {
|
||||
if (window instanceof TopLevelWindow) {
|
||||
if (window instanceof BaseWindow) {
|
||||
this.closePopupAt(window.id);
|
||||
} else {
|
||||
// Passing -1 (invalid) would make closePopupAt close the all menu runners
|
||||
@@ -102,8 +101,9 @@ Menu.prototype.getMenuItemById = function (id) {
|
||||
|
||||
let found = items.find(item => item.id === id) || null;
|
||||
for (let i = 0; !found && i < items.length; i++) {
|
||||
if (items[i].submenu) {
|
||||
found = items[i].submenu.getMenuItemById(id);
|
||||
const { submenu } = items[i];
|
||||
if (submenu) {
|
||||
found = submenu.getMenuItemById(id);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
@@ -155,7 +155,7 @@ Menu.getApplicationMenu = () => applicationMenu;
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
|
||||
|
||||
// set application menu with a preexisting menu
|
||||
Menu.setApplicationMenu = function (menu) {
|
||||
Menu.setApplicationMenu = function (menu: MenuType) {
|
||||
if (menu && menu.constructor !== Menu) {
|
||||
throw new TypeError('Invalid menu');
|
||||
}
|
||||
@@ -168,7 +168,7 @@ Menu.setApplicationMenu = function (menu) {
|
||||
menu._callMenuWillShow();
|
||||
bindings.setApplicationMenu(menu);
|
||||
} else {
|
||||
const windows = TopLevelWindow.getAllWindows();
|
||||
const windows = BaseWindow.getAllWindows();
|
||||
return windows.map(w => w.setMenu(menu));
|
||||
}
|
||||
};
|
||||
@@ -200,7 +200,7 @@ Menu.buildFromTemplate = function (template) {
|
||||
/* Helper Functions */
|
||||
|
||||
// validate the template against having the wrong attribute
|
||||
function areValidTemplateItems (template) {
|
||||
function areValidTemplateItems (template: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
return template.every(item =>
|
||||
item != null &&
|
||||
typeof item === 'object' &&
|
||||
@@ -209,7 +209,7 @@ function areValidTemplateItems (template) {
|
||||
item.type === 'separator'));
|
||||
}
|
||||
|
||||
function sortTemplate (template) {
|
||||
function sortTemplate (template: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
const sorted = sortMenuItems(template);
|
||||
for (const item of sorted) {
|
||||
if (Array.isArray(item.submenu)) {
|
||||
@@ -220,15 +220,15 @@ function sortTemplate (template) {
|
||||
}
|
||||
|
||||
// Search between separators to find a radio menu item and return its group id
|
||||
function generateGroupId (items, pos) {
|
||||
function generateGroupId (items: (MenuItemConstructorOptions | MenuItem)[], pos: number) {
|
||||
if (pos > 0) {
|
||||
for (let idx = pos - 1; idx >= 0; idx--) {
|
||||
if (items[idx].type === 'radio') return items[idx].groupId;
|
||||
if (items[idx].type === 'radio') return (items[idx] as any).groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
} else if (pos < items.length) {
|
||||
for (let idx = pos; idx <= items.length - 1; idx++) {
|
||||
if (items[idx].type === 'radio') return items[idx].groupId;
|
||||
if (items[idx].type === 'radio') return (items[idx] as any).groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
}
|
||||
@@ -236,7 +236,7 @@ function generateGroupId (items, pos) {
|
||||
return groupIdIndex;
|
||||
}
|
||||
|
||||
function removeExtraSeparators (items) {
|
||||
function removeExtraSeparators (items: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
// fold adjacent separators together
|
||||
let ret = items.filter((e, idx, arr) => {
|
||||
if (e.visible === false) return true;
|
||||
@@ -252,7 +252,7 @@ function removeExtraSeparators (items) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function insertItemByType (item, pos) {
|
||||
function insertItemByType (this: MenuType, item: MenuItem, pos: number) {
|
||||
const types = {
|
||||
normal: () => this.insertItem(pos, item.commandId, item.label),
|
||||
checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
|
||||
const { createPair } = process.electronBinding('message_port');
|
||||
const { createPair } = process._linkedBinding('electron_browser_message_port');
|
||||
|
||||
export default class MessageChannelMain {
|
||||
port1: MessagePortMain;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'app', loader: () => require('./app') },
|
||||
{ name: 'autoUpdater', loader: () => require('./auto-updater') },
|
||||
{ name: 'BaseWindow', loader: () => require('./base-window') },
|
||||
{ name: 'BrowserView', loader: () => require('./browser-view') },
|
||||
{ name: 'BrowserWindow', loader: () => require('./browser-window') },
|
||||
{ name: 'contentTracing', loader: () => require('./content-tracing') },
|
||||
@@ -25,7 +26,6 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'screen', loader: () => require('./screen') },
|
||||
{ name: 'session', loader: () => require('./session') },
|
||||
{ name: 'systemPreferences', loader: () => require('./system-preferences') },
|
||||
{ name: 'TopLevelWindow', loader: () => require('./top-level-window') },
|
||||
{ name: 'TouchBar', loader: () => require('./touch-bar') },
|
||||
{ name: 'Tray', loader: () => require('./tray') },
|
||||
{ name: 'View', loader: () => require('./view') },
|
||||
@@ -33,6 +33,12 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'WebContentsView', loader: () => require('./web-contents-view') }
|
||||
];
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
browserModuleList.push(
|
||||
{ name: 'desktopCapturer', loader: () => require('./desktop-capturer') }
|
||||
);
|
||||
}
|
||||
|
||||
if (BUILDFLAG(ENABLE_VIEWS_API)) {
|
||||
browserModuleList.push(
|
||||
{ name: 'ImageView', loader: () => require('./views/image-view') }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
export const browserModuleNames = [
|
||||
'app',
|
||||
'autoUpdater',
|
||||
'BaseWindow',
|
||||
'BrowserView',
|
||||
'BrowserWindow',
|
||||
'contentTracing',
|
||||
@@ -28,7 +29,6 @@ export const browserModuleNames = [
|
||||
'screen',
|
||||
'session',
|
||||
'systemPreferences',
|
||||
'TopLevelWindow',
|
||||
'TouchBar',
|
||||
'Tray',
|
||||
'View',
|
||||
@@ -36,6 +36,10 @@ export const browserModuleNames = [
|
||||
'WebContentsView'
|
||||
];
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
browserModuleNames.push('desktopCapturer');
|
||||
}
|
||||
|
||||
if (BUILDFLAG(ENABLE_VIEWS_API)) {
|
||||
browserModuleNames.push(
|
||||
'ImageView'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
|
||||
const { NativeTheme, nativeTheme } = process._linkedBinding('electron_common_native_theme');
|
||||
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(nativeTheme as any);
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
const { app, session } = require('electron');
|
||||
|
||||
// Fallback to default session.
|
||||
Object.setPrototypeOf(module.exports, new Proxy({}, {
|
||||
get (target, property) {
|
||||
if (!app.isReady()) return;
|
||||
|
||||
const netLog = session.defaultSession.netLog;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(netLog), property)) return;
|
||||
|
||||
// check for properties on the prototype chain that aren't functions
|
||||
if (typeof netLog[property] !== 'function') return netLog[property];
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args) => netLog[property](...args);
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return [];
|
||||
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor (target) {
|
||||
return { configurable: true, enumerable: true };
|
||||
}
|
||||
}));
|
||||
22
lib/browser/api/net-log.ts
Normal file
22
lib/browser/api/net-log.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
import { app, session } from 'electron';
|
||||
|
||||
const startLogging: typeof session.defaultSession.netLog.startLogging = async (path, options) => {
|
||||
if (!app.isReady()) return;
|
||||
return session.defaultSession.netLog.startLogging(path, options);
|
||||
};
|
||||
|
||||
const stopLogging: typeof session.defaultSession.netLog.stopLogging = async () => {
|
||||
if (!app.isReady()) return;
|
||||
return session.defaultSession.netLog.stopLogging();
|
||||
};
|
||||
|
||||
export default {
|
||||
startLogging,
|
||||
stopLogging,
|
||||
get currentlyLogging (): boolean {
|
||||
if (!app.isReady()) return false;
|
||||
return session.defaultSession.netLog.currentlyLogging;
|
||||
}
|
||||
};
|
||||
@@ -2,8 +2,13 @@ import * as url from 'url';
|
||||
import { Readable, Writable } from 'stream';
|
||||
import { app } from 'electron';
|
||||
import { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||
const { Session } = process.electronBinding('session');
|
||||
const { net, Net, isValidHeaderName, isValidHeaderValue, createURLLoader } = process.electronBinding('net');
|
||||
const {
|
||||
net,
|
||||
Net,
|
||||
isValidHeaderName,
|
||||
isValidHeaderValue,
|
||||
createURLLoader
|
||||
} = process._linkedBinding('electron_browser_net');
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:']);
|
||||
|
||||
@@ -250,7 +255,8 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||
}
|
||||
}
|
||||
if (options.session) {
|
||||
if (options.session instanceof Session) {
|
||||
// Weak check, but it should be enough to catch 99% of accidental misuses.
|
||||
if (options.session.constructor && options.session.constructor.name === 'Session') {
|
||||
urlLoaderOptions.session = options.session;
|
||||
} else {
|
||||
throw new TypeError('`session` should be an instance of the Session class');
|
||||
@@ -294,6 +300,10 @@ class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
}
|
||||
|
||||
get chunkedEncoding () {
|
||||
return this._chunkedEncoding || false;
|
||||
}
|
||||
|
||||
set chunkedEncoding (value: boolean) {
|
||||
if (this._started) {
|
||||
throw new Error('chunkedEncoding can only be set before the request is started');
|
||||
@@ -386,6 +396,7 @@ class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
this._urlLoaderOptions.referrer = this._urlLoaderOptions.extraHeaders.referer || '';
|
||||
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
|
||||
this._urlLoader = createURLLoader(opts);
|
||||
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { Notification, isSupported } = process.electronBinding('notification');
|
||||
|
||||
Notification.isSupported = isSupported;
|
||||
|
||||
module.exports = Notification;
|
||||
8
lib/browser/api/notification.ts
Normal file
8
lib/browser/api/notification.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
const {
|
||||
Notification: ElectronNotification,
|
||||
isSupported
|
||||
} = process._linkedBinding('electron_common_notification');
|
||||
|
||||
ElectronNotification.isSupported = isSupported;
|
||||
|
||||
export default ElectronNotification;
|
||||
@@ -1,7 +1,11 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { app } from 'electron';
|
||||
|
||||
const { createPowerMonitor, getSystemIdleState, getSystemIdleTime } = process.electronBinding('power_monitor');
|
||||
const {
|
||||
createPowerMonitor,
|
||||
getSystemIdleState,
|
||||
getSystemIdleTime
|
||||
} = process._linkedBinding('electron_browser_power_monitor');
|
||||
|
||||
class PowerMonitor extends EventEmitter {
|
||||
constructor () {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;
|
||||
2
lib/browser/api/power-save-blocker.ts
Normal file
2
lib/browser/api/power-save-blocker.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
const { powerSaveBlocker } = process._linkedBinding('electron_browser_power_save_blocker');
|
||||
export default powerSaveBlocker;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { app, session } from 'electron';
|
||||
|
||||
// Global protocol APIs.
|
||||
const protocol = process.electronBinding('protocol');
|
||||
const protocol = process._linkedBinding('electron_browser_protocol');
|
||||
|
||||
// Fallback protocol APIs of default session.
|
||||
Object.setPrototypeOf(protocol, new Proxy({}, {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { createLazyInstance } from '../utils';
|
||||
const { EventEmitter } = require('events');
|
||||
const { Screen, createScreen } = process.electronBinding('screen');
|
||||
const { Screen, createScreen } = process._linkedBinding('electron_common_screen');
|
||||
|
||||
// Screen is an EventEmitter.
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events');
|
||||
const { app, deprecate } = require('electron');
|
||||
const { fromPartition, Session } = process.electronBinding('session');
|
||||
|
||||
// Public API.
|
||||
Object.defineProperties(exports, {
|
||||
defaultSession: {
|
||||
enumerable: true,
|
||||
get () { return fromPartition(''); }
|
||||
},
|
||||
fromPartition: {
|
||||
enumerable: true,
|
||||
value: fromPartition
|
||||
}
|
||||
});
|
||||
|
||||
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
|
||||
|
||||
Session.prototype._init = function () {
|
||||
app.emit('session-created', this);
|
||||
};
|
||||
8
lib/browser/api/session.ts
Normal file
8
lib/browser/api/session.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
const { fromPartition } = process._linkedBinding('electron_browser_session');
|
||||
|
||||
export default {
|
||||
fromPartition,
|
||||
get defaultSession () {
|
||||
return fromPartition('');
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate } from 'electron';
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
|
||||
const { systemPreferences, SystemPreferences } = process._linkedBinding('electron_browser_system_preferences');
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { Tray } = process.electronBinding('tray');
|
||||
|
||||
module.exports = Tray;
|
||||
3
lib/browser/api/tray.ts
Normal file
3
lib/browser/api/tray.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const { Tray } = process._linkedBinding('electron_browser_tray');
|
||||
|
||||
export default Tray;
|
||||
@@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { View } = process.electronBinding('view');
|
||||
|
||||
module.exports = View;
|
||||
3
lib/browser/api/view.ts
Normal file
3
lib/browser/api/view.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const { View } = process._linkedBinding('electron_browser_view');
|
||||
|
||||
export default View;
|
||||
@@ -1,8 +0,0 @@
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron;
|
||||
const { ImageView } = process.electronBinding('image_view');
|
||||
|
||||
Object.setPrototypeOf(ImageView.prototype, View.prototype);
|
||||
|
||||
module.exports = ImageView;
|
||||
7
lib/browser/api/views/image-view.ts
Normal file
7
lib/browser/api/views/image-view.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { View } from 'electron';
|
||||
|
||||
const { ImageView } = process._linkedBinding('electron_browser_image_view');
|
||||
|
||||
Object.setPrototypeOf(ImageView.prototype, View.prototype);
|
||||
|
||||
export default ImageView;
|
||||
@@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron;
|
||||
const { WebContentsView } = process.electronBinding('web_contents_view');
|
||||
|
||||
Object.setPrototypeOf(WebContentsView.prototype, View.prototype);
|
||||
|
||||
module.exports = WebContentsView;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user