mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
147 Commits
v10.0.0-ni
...
v9.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c791f9056 | ||
|
|
359110a651 | ||
|
|
7894ae9c01 | ||
|
|
ab41460616 | ||
|
|
8a4d41cd46 | ||
|
|
41f33edd9e | ||
|
|
21f78af43f | ||
|
|
c09bfb5a79 | ||
|
|
fb5871ccf0 | ||
|
|
f255d47073 | ||
|
|
050844dc38 | ||
|
|
317f1a3d3a | ||
|
|
b4675ce0ae | ||
|
|
3f6227c61a | ||
|
|
bfc069e5c0 | ||
|
|
6cb495034e | ||
|
|
6d2cf47797 | ||
|
|
e06880a90e | ||
|
|
0600420bac | ||
|
|
a6ff42c190 | ||
|
|
969579070f | ||
|
|
9ee656f856 | ||
|
|
7e4475cf51 | ||
|
|
79d9bd3a29 | ||
|
|
50debf8595 | ||
|
|
2d68a4d2ee | ||
|
|
6ce0062c0e | ||
|
|
86aa0dfc45 | ||
|
|
792fe833d1 | ||
|
|
9b14ae770d | ||
|
|
5a34ad4e21 | ||
|
|
edd7e97dd9 | ||
|
|
b92734d912 | ||
|
|
dc25ad2ef0 | ||
|
|
54a8258c1c | ||
|
|
02eff88e1b | ||
|
|
09ca564bf4 | ||
|
|
54e31956f8 | ||
|
|
c6539f0d01 | ||
|
|
bef8448393 | ||
|
|
27619e8ab0 | ||
|
|
89c23f313f | ||
|
|
a9b9016b99 | ||
|
|
f1c1542958 | ||
|
|
cb90ef47bb | ||
|
|
a345fe2b4f | ||
|
|
0cf02dd78b | ||
|
|
3d941fc464 | ||
|
|
d32e1f8d97 | ||
|
|
b798e1ff54 | ||
|
|
bcb1d529ff | ||
|
|
e217a9416a | ||
|
|
4be52b8f78 | ||
|
|
2bc7aaf2ef | ||
|
|
1114954cbf | ||
|
|
a76ea622b9 | ||
|
|
7ee7890fd8 | ||
|
|
851ed7a6e7 | ||
|
|
16c4d6e487 | ||
|
|
56c0ba138b | ||
|
|
1883edd869 | ||
|
|
9b5f2159a2 | ||
|
|
60f16eaf95 | ||
|
|
0f08c6c874 | ||
|
|
e227578ae0 | ||
|
|
f413cda758 | ||
|
|
c41b543842 | ||
|
|
7bb430dc44 | ||
|
|
231f5af1a1 | ||
|
|
539ca773de | ||
|
|
1fc197bedb | ||
|
|
926bea232d | ||
|
|
db664f3433 | ||
|
|
9ec73a3dcf | ||
|
|
c1c1ac2b2e | ||
|
|
9c931136d0 | ||
|
|
f01ee72ea5 | ||
|
|
f6b4c39195 | ||
|
|
578f59532d | ||
|
|
dcf9e4b2dd | ||
|
|
2021f25453 | ||
|
|
faee8a092d | ||
|
|
3d65d84193 | ||
|
|
5e05df9f71 | ||
|
|
df2d03fe9b | ||
|
|
80967287ad | ||
|
|
2855f1d237 | ||
|
|
5e25649e77 | ||
|
|
3e90e523eb | ||
|
|
c702aec1f8 | ||
|
|
ede40f260e | ||
|
|
ca53cc8380 | ||
|
|
7ec9b4e252 | ||
|
|
0d7e13d2a6 | ||
|
|
a5c56684b9 | ||
|
|
68d96459c3 | ||
|
|
6cf4757019 | ||
|
|
6e80d6fba5 | ||
|
|
ec07954d5d | ||
|
|
7fd2d39f72 | ||
|
|
64880c75a3 | ||
|
|
cc94689db1 | ||
|
|
806e483049 | ||
|
|
8129e92d2b | ||
|
|
660706ba2c | ||
|
|
2feca9d35a | ||
|
|
3a4118703a | ||
|
|
bd669f72ee | ||
|
|
1f3c3eee83 | ||
|
|
6e84ebee8e | ||
|
|
f22376ef32 | ||
|
|
324b49a5eb | ||
|
|
0e9727e8d5 | ||
|
|
44ee90e5cf | ||
|
|
2b9ef75d82 | ||
|
|
4dc2b4d55f | ||
|
|
69f77d309d | ||
|
|
9cd1744a2b | ||
|
|
c6e411173e | ||
|
|
8b11adc6f2 | ||
|
|
57ec30e459 | ||
|
|
c4a836f95a | ||
|
|
9b2de2583e | ||
|
|
7b2bfb4a0f | ||
|
|
0e31826043 | ||
|
|
7b3c073b3e | ||
|
|
2dc900b95d | ||
|
|
f41423501a | ||
|
|
07d9728b63 | ||
|
|
d4c90e80a5 | ||
|
|
91141028e6 | ||
|
|
9d69d4b9ef | ||
|
|
d57d5c544e | ||
|
|
74d4dab157 | ||
|
|
94246dabe3 | ||
|
|
4fe91e56ac | ||
|
|
0d7440d676 | ||
|
|
60edd2d3b1 | ||
|
|
d4d06660f3 | ||
|
|
8d8f15121b | ||
|
|
1f8cb5144d | ||
|
|
667ee359a1 | ||
|
|
4b009159ba | ||
|
|
89f66bd00c | ||
|
|
2d542c6028 | ||
|
|
68346fec55 | ||
|
|
23f32ca9f3 |
@@ -227,7 +227,25 @@ step-gclient-sync: &step-gclient-sync
|
||||
$GCLIENT_EXTRA_ARGS \
|
||||
"$CIRCLE_REPOSITORY_URL"
|
||||
|
||||
gclient sync --with_branch_heads --with_tags
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 gclient sync --with_branch_heads --with_tags
|
||||
# Re-export all the patches to check if there were changes.
|
||||
python src/electron/script/export_all_patches.py src/electron/patches/config.json
|
||||
cd src/electron
|
||||
git update-index --refresh || true
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
# There are changes to the patches. Make a git commit with the updated patches
|
||||
git add patches
|
||||
GIT_COMMITTER_NAME="Electron Bot" GIT_COMMITTER_EMAIL="anonymous@electronjs.org" git commit -m "update patches" --author="Electron Bot <anonymous@electronjs.org>"
|
||||
# Export it
|
||||
mkdir -p ../../patches
|
||||
git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch
|
||||
echo
|
||||
echo "======================================================================"
|
||||
echo "There were changes to the patches when applying."
|
||||
echo "Check the CI artifacts for a patch you can apply to fix it."
|
||||
echo "======================================================================"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
step-setup-env-for-build: &step-setup-env-for-build
|
||||
@@ -342,7 +360,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
cd src/electron
|
||||
./script/codesign/import-testing-cert-ci.sh
|
||||
./script/codesign/generate-identity.sh
|
||||
fi
|
||||
|
||||
step-install-gnutar-on-mac: &step-install-gnutar-on-mac
|
||||
@@ -681,6 +699,8 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
|
||||
if [ "$GENERATE_SYMBOLS" == "true" ]; then
|
||||
cd src
|
||||
ninja -C out/Default electron:electron_symbols
|
||||
cd out/Default/breakpad_symbols
|
||||
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
|
||||
fi
|
||||
|
||||
step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
@@ -970,6 +990,8 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
- *step-save-git-cache
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
@@ -988,6 +1010,7 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
sudo chown -R $(id -u):$(id -g) /portal
|
||||
mv ./src /portal
|
||||
- *step-save-src-cache
|
||||
- *step-save-brew-cache
|
||||
|
||||
steps-electron-gn-check: &steps-electron-gn-check
|
||||
steps:
|
||||
@@ -1193,7 +1216,7 @@ steps-test-node: &steps-test-node
|
||||
name: Run Node Tests
|
||||
command: |
|
||||
cd src
|
||||
node electron/script/node-spec-runner.js junit
|
||||
node electron/script/node-spec-runner.js --default --jUnitDir=junit
|
||||
- store_test_results:
|
||||
path: src/junit
|
||||
|
||||
@@ -1276,7 +1299,6 @@ commands:
|
||||
at: .
|
||||
- *step-restore-brew-cache
|
||||
- *step-install-gnutar-on-mac
|
||||
- *step-save-brew-cache
|
||||
- when:
|
||||
condition: << parameters.checkout-and-assume-cache >>
|
||||
steps:
|
||||
@@ -1302,6 +1324,8 @@ commands:
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
- store_artifacts:
|
||||
path: patches
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- when:
|
||||
condition: << parameters.preserve-vendor-dirs >>
|
||||
@@ -1672,7 +1696,6 @@ jobs:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
@@ -1685,6 +1708,7 @@ jobs:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
@@ -1734,7 +1758,6 @@ jobs:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
@@ -1749,6 +1772,7 @@ jobs:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
@@ -1805,7 +1829,6 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-32bit-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -1820,6 +1843,7 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-32bit-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -1879,7 +1903,6 @@ jobs:
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -1893,6 +1916,7 @@ jobs:
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -1949,7 +1973,6 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -1962,6 +1985,7 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -2022,7 +2046,6 @@ jobs:
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
@@ -2036,6 +2059,7 @@ jobs:
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
"browser": true
|
||||
},
|
||||
"rules": {
|
||||
"semi": ["error", "always"],
|
||||
"no-var": "error",
|
||||
"no-unused-vars": 0,
|
||||
"no-global-assign": 0,
|
||||
"guard-for-in": 2,
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
"vars": "all",
|
||||
"args": "after-used",
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
||||
# `git apply` and friends don't understand CRLF, even on windows. Force those
|
||||
# files to be checked out with LF endings even if core.autocrlf is true.
|
||||
*.patch text eol=lf
|
||||
patches/**/.patches merge=union
|
||||
|
||||
12
.github/CODEOWNERS
vendored
12
.github/CODEOWNERS
vendored
@@ -3,10 +3,20 @@
|
||||
# https://help.github.com/articles/about-codeowners
|
||||
# https://git-scm.com/docs/gitignore
|
||||
|
||||
# Most stuff in here is owned by the Community & Safety WG...
|
||||
/.github/* @electron/wg-community
|
||||
|
||||
# ...except the Admin WG maintains this file.
|
||||
/.github/CODEOWNERS @electron/wg-admin
|
||||
|
||||
# Upgrades WG
|
||||
/patches/ @electron/wg-upgrades
|
||||
DEPS @electron/wg-upgrades
|
||||
|
||||
# Docs & Tooling WG
|
||||
/default_app/ @electron/wg-docs-tools
|
||||
/docs/ @electron/wg-docs-tools
|
||||
|
||||
# Releases WG
|
||||
/npm/ @electron/wg-releases
|
||||
/script/release @electron/wg-releases
|
||||
/script/release @electron/wg-releases
|
||||
16
BUILD.gn
16
BUILD.gn
@@ -224,7 +224,6 @@ grit("resources") {
|
||||
]
|
||||
|
||||
# Mojo manifest overlays are generated.
|
||||
source_is_generated = true
|
||||
grit_flags = [
|
||||
"-E",
|
||||
"target_gen_dir=" + rebase_path(target_gen_dir, root_build_dir),
|
||||
@@ -500,9 +499,16 @@ source_set("electron_lib") {
|
||||
"//third_party/breakpad:client",
|
||||
"//ui/events/devices/x11",
|
||||
"//ui/events/platform/x11",
|
||||
"//ui/gtk",
|
||||
"//ui/views/controls/webview",
|
||||
"//ui/wm",
|
||||
]
|
||||
if (use_x11) {
|
||||
deps += [
|
||||
"//ui/gfx/x",
|
||||
"//ui/gtk:x",
|
||||
]
|
||||
}
|
||||
configs += [ ":gio_unix" ]
|
||||
include_dirs += [ "//third_party/breakpad" ]
|
||||
configs += [ "//build/config/linux:x11" ]
|
||||
@@ -736,9 +742,14 @@ if (is_mac) {
|
||||
sources = [
|
||||
"$root_out_dir/egl_intermediates/libswiftshader_libEGL.dylib",
|
||||
"$root_out_dir/egl_intermediates/libswiftshader_libGLESv2.dylib",
|
||||
"$root_out_dir/vk_intermediates/libvk_swiftshader.dylib",
|
||||
"$root_out_dir/vk_intermediates/vk_swiftshader_icd.json",
|
||||
]
|
||||
outputs = [ "{{bundle_contents_dir}}/Libraries/{{source_file_part}}" ]
|
||||
public_deps = [ "//ui/gl:swiftshader_library_copy" ]
|
||||
public_deps = [
|
||||
"//ui/gl:swiftshader_egl_library_copy",
|
||||
"//ui/gl:swiftshader_vk_library_copy",
|
||||
]
|
||||
}
|
||||
}
|
||||
group("electron_angle_library") {
|
||||
@@ -791,6 +802,7 @@ if (is_mac) {
|
||||
|
||||
include_dirs = [ "." ]
|
||||
sources = filenames.framework_sources
|
||||
libs = []
|
||||
|
||||
if (enable_osr) {
|
||||
libs += [ "IOSurface.framework" ]
|
||||
|
||||
6
DEPS
6
DEPS
@@ -12,9 +12,9 @@ gclient_gn_args = [
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'ddac4c3cc1528254008f9e7a2aea5d4426906bfd',
|
||||
'83.0.4102.3',
|
||||
'node_version':
|
||||
'v12.15.0',
|
||||
'v12.14.1',
|
||||
'nan_version':
|
||||
'2ee313aaca52e2b478965ac50eb5082520380d1b',
|
||||
|
||||
@@ -155,3 +155,5 @@ hooks = [
|
||||
recursedeps = [
|
||||
'src',
|
||||
]
|
||||
|
||||
# Touch DEPS again to bust cache
|
||||
|
||||
@@ -1 +1 @@
|
||||
10.0.0-nightly.20200221
|
||||
9.0.0-beta.15
|
||||
@@ -2,7 +2,7 @@ is_electron_build = true
|
||||
root_extra_deps = [ "//electron" ]
|
||||
|
||||
# Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json
|
||||
node_module_version = 82
|
||||
node_module_version = 80
|
||||
|
||||
v8_promise_internal_field_count = 1
|
||||
v8_typed_array_max_size_in_heap = 0
|
||||
@@ -21,7 +21,6 @@ dawn_enable_vulkan_validation_layers = false
|
||||
|
||||
is_cfi = false
|
||||
|
||||
# TODO: disabled due to crashes. re-enable.
|
||||
enable_osr = false
|
||||
enable_osr = true
|
||||
|
||||
enable_electron_extensions = true
|
||||
|
||||
@@ -109,11 +109,15 @@ static_library("chrome") {
|
||||
]
|
||||
|
||||
if (use_aura) {
|
||||
sources += [
|
||||
"//chrome/browser/platform_util_aura.cc",
|
||||
"//chrome/browser/ui/views/color_chooser_aura.cc",
|
||||
"//chrome/browser/ui/views/color_chooser_aura.h",
|
||||
]
|
||||
sources += [ "//chrome/browser/platform_util_aura.cc" ]
|
||||
|
||||
if (!is_win) {
|
||||
sources += [
|
||||
"//chrome/browser/ui/views/color_chooser_aura.cc",
|
||||
"//chrome/browser/ui/views/color_chooser_aura.h",
|
||||
]
|
||||
}
|
||||
|
||||
deps += [ "//components/feature_engagement" ]
|
||||
}
|
||||
|
||||
@@ -261,6 +265,7 @@ source_set("plugins") {
|
||||
"//chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h",
|
||||
]
|
||||
deps += [
|
||||
"//components/pdf/browser",
|
||||
"//media:media_buildflags",
|
||||
"//ppapi/buildflags",
|
||||
"//ppapi/proxy:ipc",
|
||||
@@ -298,8 +303,6 @@ source_set("plugins") {
|
||||
sources += [
|
||||
"//chrome/renderer/pepper/pepper_flash_drm_renderer_host.cc",
|
||||
"//chrome/renderer/pepper/pepper_flash_drm_renderer_host.h",
|
||||
"//chrome/renderer/pepper/pepper_flash_font_file_host.cc",
|
||||
"//chrome/renderer/pepper/pepper_flash_font_file_host.h",
|
||||
"//chrome/renderer/pepper/pepper_flash_fullscreen_host.cc",
|
||||
"//chrome/renderer/pepper/pepper_flash_fullscreen_host.h",
|
||||
"//chrome/renderer/pepper/pepper_flash_menu_host.cc",
|
||||
@@ -308,7 +311,14 @@ source_set("plugins") {
|
||||
"//chrome/renderer/pepper/pepper_flash_renderer_host.h",
|
||||
]
|
||||
}
|
||||
if (enable_pepper_flash || enable_pdf_viewer) {
|
||||
sources += [
|
||||
"//chrome/renderer/pepper/pepper_flash_font_file_host.cc",
|
||||
"//chrome/renderer/pepper/pepper_flash_font_file_host.h",
|
||||
]
|
||||
}
|
||||
deps += [
|
||||
"//components/pdf/renderer",
|
||||
"//components/strings",
|
||||
"//media:media_buildflags",
|
||||
"//ppapi/host",
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron'
|
||||
import * as path from 'path'
|
||||
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit()
|
||||
})
|
||||
app.quit();
|
||||
});
|
||||
|
||||
function decorateURL (url: string) {
|
||||
// safely add `?utm_source=default_app
|
||||
const parsedUrl = new URL(url)
|
||||
parsedUrl.searchParams.append('utm_source', 'default_app')
|
||||
return parsedUrl.toString()
|
||||
const parsedUrl = new URL(url);
|
||||
parsedUrl.searchParams.append('utm_source', 'default_app');
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
|
||||
// Find the shortest path to the electron binary
|
||||
const absoluteElectronPath = process.execPath
|
||||
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath)
|
||||
const absoluteElectronPath = process.execPath;
|
||||
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
|
||||
const electronPath = absoluteElectronPath.length < relativeElectronPath.length
|
||||
? absoluteElectronPath
|
||||
: relativeElectronPath
|
||||
: relativeElectronPath;
|
||||
|
||||
const indexPath = path.resolve(app.getAppPath(), 'index.html')
|
||||
const indexPath = path.resolve(app.getAppPath(), 'index.html');
|
||||
|
||||
function isTrustedSender (webContents: Electron.WebContents) {
|
||||
if (webContents !== (mainWindow && mainWindow.webContents)) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedUrl = new URL(webContents.getURL())
|
||||
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
|
||||
: parsedUrl.pathname;
|
||||
return parsedUrl.protocol === 'file:' && urlPath === indexPath;
|
||||
}
|
||||
|
||||
ipcMain.handle('bootstrap', (event) => {
|
||||
return isTrustedSender(event.sender) ? electronPath : null
|
||||
})
|
||||
return isTrustedSender(event.sender) ? electronPath : null;
|
||||
});
|
||||
|
||||
async function createWindow () {
|
||||
await app.whenReady()
|
||||
await app.whenReady();
|
||||
|
||||
const options: Electron.BrowserWindowConstructorOptions = {
|
||||
width: 900,
|
||||
@@ -57,46 +57,46 @@ async function createWindow () {
|
||||
},
|
||||
useContentSize: true,
|
||||
show: false
|
||||
}
|
||||
};
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
options.icon = path.join(__dirname, 'icon.png')
|
||||
options.icon = path.join(__dirname, 'icon.png');
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow(options)
|
||||
mainWindow.on('ready-to-show', () => mainWindow!.show())
|
||||
mainWindow = new BrowserWindow(options);
|
||||
mainWindow.on('ready-to-show', () => mainWindow!.show());
|
||||
|
||||
mainWindow.webContents.on('new-window', (event, url) => {
|
||||
event.preventDefault()
|
||||
shell.openExternal(decorateURL(url))
|
||||
})
|
||||
event.preventDefault();
|
||||
shell.openExternal(decorateURL(url));
|
||||
});
|
||||
|
||||
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => {
|
||||
const parsedUrl = new URL(webContents.getURL())
|
||||
const parsedUrl = new URL(webContents.getURL());
|
||||
|
||||
const options: Electron.MessageBoxOptions = {
|
||||
title: 'Permission Request',
|
||||
message: `Allow '${parsedUrl.origin}' to access '${permission}'?`,
|
||||
buttons: ['OK', 'Cancel'],
|
||||
cancelId: 1
|
||||
}
|
||||
};
|
||||
|
||||
dialog.showMessageBox(mainWindow!, options).then(({ response }) => {
|
||||
done(response === 0)
|
||||
})
|
||||
})
|
||||
done(response === 0);
|
||||
});
|
||||
});
|
||||
|
||||
return mainWindow
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
export const loadURL = async (appUrl: string) => {
|
||||
mainWindow = await createWindow()
|
||||
mainWindow.loadURL(appUrl)
|
||||
mainWindow.focus()
|
||||
}
|
||||
mainWindow = await createWindow();
|
||||
mainWindow.loadURL(appUrl);
|
||||
mainWindow.focus();
|
||||
};
|
||||
|
||||
export const loadFile = async (appPath: string) => {
|
||||
mainWindow = await createWindow()
|
||||
mainWindow.loadFile(appPath)
|
||||
mainWindow.focus()
|
||||
}
|
||||
mainWindow = await createWindow();
|
||||
mainWindow.loadFile(appPath);
|
||||
mainWindow.focus();
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { app, dialog } from 'electron'
|
||||
import { app, dialog } from 'electron';
|
||||
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as url from 'url'
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
type DefaultAppOptions = {
|
||||
file: null | string;
|
||||
@@ -14,10 +14,10 @@ type DefaultAppOptions = {
|
||||
modules: string[];
|
||||
}
|
||||
|
||||
const Module = require('module')
|
||||
const Module = require('module');
|
||||
|
||||
// Parse command line options.
|
||||
const argv = process.argv.slice(1)
|
||||
const argv = process.argv.slice(1);
|
||||
|
||||
const option: DefaultAppOptions = {
|
||||
file: null,
|
||||
@@ -27,50 +27,50 @@ const option: DefaultAppOptions = {
|
||||
interactive: false,
|
||||
abi: false,
|
||||
modules: []
|
||||
}
|
||||
};
|
||||
|
||||
let nextArgIsRequire = false
|
||||
let nextArgIsRequire = false;
|
||||
|
||||
for (const arg of argv) {
|
||||
if (nextArgIsRequire) {
|
||||
option.modules.push(arg)
|
||||
nextArgIsRequire = false
|
||||
continue
|
||||
option.modules.push(arg);
|
||||
nextArgIsRequire = false;
|
||||
continue;
|
||||
} else if (arg === '--version' || arg === '-v') {
|
||||
option.version = true
|
||||
break
|
||||
option.version = true;
|
||||
break;
|
||||
} else if (arg.match(/^--app=/)) {
|
||||
option.file = arg.split('=')[1]
|
||||
break
|
||||
option.file = arg.split('=')[1];
|
||||
break;
|
||||
} else if (arg === '--interactive' || arg === '-i' || arg === '-repl') {
|
||||
option.interactive = true
|
||||
option.interactive = true;
|
||||
} else if (arg === '--test-type=webdriver') {
|
||||
option.webdriver = true
|
||||
option.webdriver = true;
|
||||
} else if (arg === '--require' || arg === '-r') {
|
||||
nextArgIsRequire = true
|
||||
continue
|
||||
nextArgIsRequire = true;
|
||||
continue;
|
||||
} else if (arg === '--abi' || arg === '-a') {
|
||||
option.abi = true
|
||||
continue
|
||||
option.abi = true;
|
||||
continue;
|
||||
} else if (arg === '--no-help') {
|
||||
option.noHelp = true
|
||||
continue
|
||||
option.noHelp = true;
|
||||
continue;
|
||||
} else if (arg[0] === '-') {
|
||||
continue
|
||||
continue;
|
||||
} else {
|
||||
option.file = arg
|
||||
break
|
||||
option.file = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextArgIsRequire) {
|
||||
console.error('Invalid Usage: --require [file]\n\n"file" is required')
|
||||
process.exit(1)
|
||||
console.error('Invalid Usage: --require [file]\n\n"file" is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Set up preload modules
|
||||
if (option.modules.length > 0) {
|
||||
Module._preloadModules(option.modules)
|
||||
Module._preloadModules(option.modules);
|
||||
}
|
||||
|
||||
function loadApplicationPackage (packagePath: string) {
|
||||
@@ -79,102 +79,102 @@ function loadApplicationPackage (packagePath: string) {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
value: true
|
||||
})
|
||||
});
|
||||
|
||||
try {
|
||||
// Override app name and version.
|
||||
packagePath = path.resolve(packagePath)
|
||||
const packageJsonPath = path.join(packagePath, 'package.json')
|
||||
let appPath
|
||||
packagePath = path.resolve(packagePath);
|
||||
const packageJsonPath = path.join(packagePath, 'package.json');
|
||||
let appPath;
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
let packageJson
|
||||
let packageJson;
|
||||
try {
|
||||
packageJson = require(packageJsonPath)
|
||||
packageJson = require(packageJsonPath);
|
||||
} catch (e) {
|
||||
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`)
|
||||
return
|
||||
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packageJson.version) {
|
||||
app.setVersion(packageJson.version)
|
||||
app.setVersion(packageJson.version);
|
||||
}
|
||||
if (packageJson.productName) {
|
||||
app.name = packageJson.productName
|
||||
app.name = packageJson.productName;
|
||||
} else if (packageJson.name) {
|
||||
app.name = packageJson.name
|
||||
app.name = packageJson.name;
|
||||
}
|
||||
appPath = packagePath
|
||||
appPath = packagePath;
|
||||
}
|
||||
|
||||
try {
|
||||
const filePath = Module._resolveFilename(packagePath, module, true)
|
||||
app._setDefaultAppPaths(appPath || path.dirname(filePath))
|
||||
const filePath = Module._resolveFilename(packagePath, module, true);
|
||||
app._setDefaultAppPaths(appPath || path.dirname(filePath));
|
||||
} catch (e) {
|
||||
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`)
|
||||
return
|
||||
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the app.
|
||||
Module._load(packagePath, module, true)
|
||||
Module._load(packagePath, module, true);
|
||||
} catch (e) {
|
||||
console.error('App threw an error during load')
|
||||
console.error(e.stack || e)
|
||||
throw e
|
||||
console.error('App threw an error during load');
|
||||
console.error(e.stack || e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function showErrorMessage (message: string) {
|
||||
app.focus()
|
||||
dialog.showErrorBox('Error launching app', message)
|
||||
process.exit(1)
|
||||
app.focus();
|
||||
dialog.showErrorBox('Error launching app', message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function loadApplicationByURL (appUrl: string) {
|
||||
const { loadURL } = await import('./default_app')
|
||||
loadURL(appUrl)
|
||||
const { loadURL } = await import('./default_app');
|
||||
loadURL(appUrl);
|
||||
}
|
||||
|
||||
async function loadApplicationByFile (appPath: string) {
|
||||
const { loadFile } = await import('./default_app')
|
||||
loadFile(appPath)
|
||||
const { loadFile } = await import('./default_app');
|
||||
loadFile(appPath);
|
||||
}
|
||||
|
||||
function startRepl () {
|
||||
if (process.platform === 'win32') {
|
||||
console.error('Electron REPL not currently supported on Windows')
|
||||
process.exit(1)
|
||||
console.error('Electron REPL not currently supported on Windows');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// prevent quitting
|
||||
app.on('window-all-closed', () => {})
|
||||
app.on('window-all-closed', () => {});
|
||||
|
||||
const repl = require('repl')
|
||||
const repl = require('repl');
|
||||
repl.start('> ').on('exit', () => {
|
||||
process.exit(0)
|
||||
})
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
if (option.file && !option.webdriver) {
|
||||
const file = option.file
|
||||
const protocol = url.parse(file).protocol
|
||||
const extension = path.extname(file)
|
||||
const file = option.file;
|
||||
const protocol = url.parse(file).protocol;
|
||||
const extension = path.extname(file);
|
||||
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
|
||||
loadApplicationByURL(file)
|
||||
loadApplicationByURL(file);
|
||||
} else if (extension === '.html' || extension === '.htm') {
|
||||
loadApplicationByFile(path.resolve(file))
|
||||
loadApplicationByFile(path.resolve(file));
|
||||
} else {
|
||||
loadApplicationPackage(file)
|
||||
loadApplicationPackage(file);
|
||||
}
|
||||
} else if (option.version) {
|
||||
console.log('v' + process.versions.electron)
|
||||
process.exit(0)
|
||||
console.log('v' + process.versions.electron);
|
||||
process.exit(0);
|
||||
} else if (option.abi) {
|
||||
console.log(process.versions.modules)
|
||||
process.exit(0)
|
||||
console.log(process.versions.modules);
|
||||
process.exit(0);
|
||||
} else if (option.interactive) {
|
||||
startRepl()
|
||||
startRepl();
|
||||
} else {
|
||||
if (!option.noHelp) {
|
||||
const welcomeMessage = `
|
||||
@@ -192,10 +192,10 @@ Options:
|
||||
-i, --interactive Open a REPL to the main process.
|
||||
-r, --require Module to preload (option can be repeated).
|
||||
-v, --version Print the version.
|
||||
-a, --abi Print the Node ABI version.`
|
||||
-a, --abi Print the Node ABI version.`;
|
||||
|
||||
console.log(welcomeMessage)
|
||||
console.log(welcomeMessage);
|
||||
}
|
||||
|
||||
loadApplicationByFile('index.html')
|
||||
loadApplicationByFile('index.html');
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
import { ipcRenderer, contextBridge } from 'electron'
|
||||
import { ipcRenderer, contextBridge } from 'electron';
|
||||
|
||||
async function getOcticonSvg (name: string) {
|
||||
try {
|
||||
const response = await fetch(`octicon/${name}.svg`)
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = await response.text()
|
||||
return div
|
||||
const response = await fetch(`octicon/${name}.svg`);
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = await response.text();
|
||||
return div;
|
||||
} catch {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSVG (element: HTMLSpanElement) {
|
||||
for (const cssClass of element.classList) {
|
||||
if (cssClass.startsWith('octicon-')) {
|
||||
const icon = await getOcticonSvg(cssClass.substr(8))
|
||||
const icon = await getOcticonSvg(cssClass.substr(8));
|
||||
if (icon) {
|
||||
for (const elemClass of element.classList) {
|
||||
icon.classList.add(elemClass)
|
||||
icon.classList.add(elemClass);
|
||||
}
|
||||
element.before(icon)
|
||||
element.remove()
|
||||
break
|
||||
element.before(icon);
|
||||
element.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initialize () {
|
||||
const electronPath = await ipcRenderer.invoke('bootstrap')
|
||||
const electronPath = await ipcRenderer.invoke('bootstrap');
|
||||
|
||||
function replaceText (selector: string, text: string) {
|
||||
const element = document.querySelector<HTMLElement>(selector)
|
||||
const element = document.querySelector<HTMLElement>(selector);
|
||||
if (element) {
|
||||
element.innerText = text
|
||||
element.innerText = text;
|
||||
}
|
||||
}
|
||||
|
||||
replaceText('.electron-version', `Electron v${process.versions.electron}`)
|
||||
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`)
|
||||
replaceText('.node-version', `Node v${process.versions.node}`)
|
||||
replaceText('.v8-version', `v8 v${process.versions.v8}`)
|
||||
replaceText('.command-example', `${electronPath} path-to-app`)
|
||||
replaceText('.electron-version', `Electron v${process.versions.electron}`);
|
||||
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`);
|
||||
replaceText('.node-version', `Node v${process.versions.node}`);
|
||||
replaceText('.v8-version', `v8 v${process.versions.v8}`);
|
||||
replaceText('.command-example', `${electronPath} path-to-app`);
|
||||
|
||||
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
|
||||
loadSVG(element)
|
||||
loadSVG(element);
|
||||
}
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('electronDefaultApp', {
|
||||
initialize
|
||||
})
|
||||
});
|
||||
|
||||
@@ -112,7 +112,6 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* [Process Object](api/process.md)
|
||||
* [Supported Command Line Switches](api/command-line-switches.md)
|
||||
* [Environment Variables](api/environment-variables.md)
|
||||
* [Chrome Extensions Support](api/extensions.md)
|
||||
* [Breaking API Changes](breaking-changes.md)
|
||||
|
||||
### Custom DOM Elements:
|
||||
|
||||
@@ -659,8 +659,6 @@ to the npm modules spec. You should usually also specify a `productName`
|
||||
field, which is your application's full capitalized name, and which will be
|
||||
preferred over `name` by Electron.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.setName(name)`
|
||||
|
||||
* `name` String
|
||||
@@ -669,8 +667,6 @@ Overrides the current application's name.
|
||||
|
||||
**Note:** This function overrides the name used internally by Electron; it does not affect the name that the OS uses.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.getLocale()`
|
||||
|
||||
Returns `String` - The current application locale. Possible return values are documented [here](locales.md).
|
||||
@@ -1089,14 +1085,10 @@ On macOS, it shows on the dock icon. On Linux, it only works for Unity launcher.
|
||||
**Note:** Unity launcher requires the existence of a `.desktop` file to work,
|
||||
for more information please read [Desktop Environment Integration][unity-requirement].
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.getBadgeCount()` _Linux_ _macOS_
|
||||
|
||||
Returns `Integer` - The current value displayed in the counter badge.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.isUnityRunning()` _Linux_
|
||||
|
||||
Returns `Boolean` - Whether the current desktop environment is Unity launcher.
|
||||
@@ -1171,8 +1163,6 @@ technologies, such as screen readers, has been detected. See
|
||||
https://www.chromium.org/developers/design-documents/accessibility for more
|
||||
details.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.setAccessibilitySupportEnabled(enabled)` _macOS_ _Windows_
|
||||
|
||||
* `enabled` Boolean - Enable or disable [accessibility tree](https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree) rendering
|
||||
@@ -1184,8 +1174,6 @@ This API must be called after the `ready` event is emitted.
|
||||
|
||||
**Note:** Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `app.showAboutPanel()`
|
||||
|
||||
Show the app's about panel options. These options can be overridden with `app.setAboutPanelOptions(options)`.
|
||||
|
||||
@@ -289,7 +289,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
between the web pages even when you specified different values for them,
|
||||
including but not limited to `preload`, `sandbox` and `nodeIntegration`.
|
||||
So it is suggested to use exact same `webPreferences` for web pages with
|
||||
the same `affinity`. _This property is experimental_
|
||||
the same `affinity`. _Deprecated_
|
||||
* `zoomFactor` Number (optional) - The default zoom factor of the page, `3.0` represents
|
||||
`300%`. Default is `1.0`.
|
||||
* `javascript` Boolean (optional) - Enables JavaScript support. Default is `true`.
|
||||
@@ -369,6 +369,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
consecutive dialog protection is triggered. If not defined the default
|
||||
message would be used, note that currently the default message is in
|
||||
English and not localized.
|
||||
* `disableDialogs` Boolean (optional) - Whether to disable dialogs
|
||||
completely. Overrides `safeDialogs`. Default is `false`.
|
||||
* `navigateOnDragDrop` Boolean (optional) - Whether dragging and dropping a
|
||||
file or link onto the page causes a navigation. Default is `false`.
|
||||
* `autoplayPolicy` String (optional) - Autoplay policy to apply to
|
||||
@@ -1113,15 +1115,11 @@ Returns `Integer[]` - Contains the window's maximum width and height.
|
||||
|
||||
* `resizable` Boolean
|
||||
|
||||
Sets whether the window can be manually resized by user.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the window can be manually resized by the user.
|
||||
|
||||
#### `win.isResizable()`
|
||||
|
||||
Returns `Boolean` - Whether the window can be manually resized by user.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Returns `Boolean` - Whether the window can be manually resized by the user.
|
||||
|
||||
#### `win.setMovable(movable)` _macOS_ _Windows_
|
||||
|
||||
@@ -1129,41 +1127,29 @@ Returns `Boolean` - Whether the window can be manually resized by user.
|
||||
|
||||
Sets whether the window can be moved by user. On Linux does nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.isMovable()` _macOS_ _Windows_
|
||||
|
||||
Returns `Boolean` - Whether the window can be moved by user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setMinimizable(minimizable)` _macOS_ _Windows_
|
||||
|
||||
* `minimizable` Boolean
|
||||
|
||||
Sets whether the window can be manually minimized by user. On Linux does
|
||||
nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the window can be manually minimized by user. On Linux does nothing.
|
||||
|
||||
#### `win.isMinimizable()` _macOS_ _Windows_
|
||||
|
||||
Returns `Boolean` - Whether the window can be manually minimized by user
|
||||
Returns `Boolean` - Whether the window can be manually minimized by the user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setMaximizable(maximizable)` _macOS_ _Windows_
|
||||
|
||||
* `maximizable` Boolean
|
||||
|
||||
Sets whether the window can be manually maximized by user. On Linux does
|
||||
nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the window can be manually maximized by user. On Linux does nothing.
|
||||
|
||||
#### `win.isMaximizable()` _macOS_ _Windows_
|
||||
|
||||
@@ -1171,23 +1157,15 @@ Returns `Boolean` - Whether the window can be manually maximized by user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setFullScreenable(fullscreenable)`
|
||||
|
||||
* `fullscreenable` Boolean
|
||||
|
||||
Sets whether the maximize/zoom window button toggles fullscreen mode or
|
||||
maximizes the window.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Sets whether the maximize/zoom window button toggles fullscreen mode or maximizes the window.
|
||||
|
||||
#### `win.isFullScreenable()`
|
||||
|
||||
Returns `Boolean` - Whether the maximize/zoom window button toggles fullscreen mode or
|
||||
maximizes the window.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
Returns `Boolean` - Whether the maximize/zoom window button toggles fullscreen mode or maximizes the window.
|
||||
|
||||
#### `win.setClosable(closable)` _macOS_ _Windows_
|
||||
|
||||
@@ -1195,16 +1173,12 @@ maximizes the window.
|
||||
|
||||
Sets whether the window can be manually closed by user. On Linux does nothing.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.isClosable()` _macOS_ _Windows_
|
||||
|
||||
Returns `Boolean` - Whether the window can be manually closed by user.
|
||||
|
||||
On Linux always returns `true`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setAlwaysOnTop(flag[, level][, relativeLevel])`
|
||||
|
||||
* `flag` Boolean
|
||||
@@ -1616,23 +1590,17 @@ This cannot be called when `titleBarStyle` is set to `customButtonsOnHover`.
|
||||
Sets whether the window menu bar should hide itself automatically. Once set the
|
||||
menu bar will only show when users press the single `Alt` key.
|
||||
|
||||
If the menu bar is already visible, calling `setAutoHideMenuBar(true)` won't
|
||||
hide it immediately.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
If the menu bar is already visible, calling `setAutoHideMenuBar(true)` won't hide it immediately.
|
||||
|
||||
#### `win.isMenuBarAutoHide()`
|
||||
|
||||
Returns `Boolean` - Whether menu bar automatically hides itself.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `win.setMenuBarVisibility(visible)` _Windows_ _Linux_
|
||||
|
||||
* `visible` Boolean
|
||||
|
||||
Sets whether the menu bar should be visible. If the menu bar is auto-hide, users
|
||||
can still bring up the menu bar by pressing the single `Alt` key.
|
||||
Sets whether the menu bar should be visible. If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key.
|
||||
|
||||
#### `win.isMenuBarVisible()`
|
||||
|
||||
@@ -1748,6 +1716,17 @@ will remove the vibrancy effect on the window.
|
||||
Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been
|
||||
deprecated and will be removed in an upcoming version of macOS.
|
||||
|
||||
#### `win.setTrafficLightPosition(position)` _macOS_
|
||||
|
||||
* `position` [Point](structures/point.md)
|
||||
|
||||
Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden`.
|
||||
|
||||
#### `win.getTrafficLightPosition()` _macOS_
|
||||
|
||||
Returns `Point` - The current position for the traffic light buttons. Can only be used with `titleBarStyle`
|
||||
set to `hidden`.
|
||||
|
||||
#### `win.setTouchBar(touchBar)` _macOS_ _Experimental_
|
||||
|
||||
* `touchBar` TouchBar | null
|
||||
|
||||
@@ -22,6 +22,9 @@ which the request is associated.
|
||||
with which the request is associated. Defaults to the empty string. The
|
||||
`session` option prevails on `partition`. Thus if a `session` is explicitly
|
||||
specified, `partition` is ignored.
|
||||
* `useSessionCookies` Boolean (optional) - Whether to send cookies with this
|
||||
request from the provided session. This will make the `net` request's
|
||||
cookie behavior match a `fetch` request. Default is `false`.
|
||||
* `protocol` String (optional) - The protocol scheme in the form 'scheme:'.
|
||||
Currently supported values are 'http:' or 'https:'. Defaults to 'http:'.
|
||||
* `host` String (optional) - The server host provided as a concatenation of
|
||||
|
||||
@@ -16,66 +16,80 @@ app.whenReady().then(() => {
|
||||
})
|
||||
```
|
||||
|
||||
## Electron CLI Flags
|
||||
## --ignore-connections-limit=`domains`
|
||||
|
||||
### --auth-server-whitelist=`url`
|
||||
Ignore the connections limit for `domains` list separated by `,`.
|
||||
|
||||
A comma-separated list of servers for which integrated authentication is enabled.
|
||||
|
||||
For example:
|
||||
|
||||
```sh
|
||||
--auth-server-whitelist='*example.com, *foobar.com, *baz'
|
||||
```
|
||||
|
||||
then any `url` ending with `example.com`, `foobar.com`, `baz` will be considered
|
||||
for integrated authentication. Without `*` prefix the URL has to match exactly.
|
||||
|
||||
### --auth-negotiate-delegate-whitelist=`url`
|
||||
|
||||
A comma-separated list of servers for which delegation of user credentials is required.
|
||||
Without `*` prefix the URL has to match exactly.
|
||||
|
||||
### --disable-http-cache
|
||||
## --disable-http-cache
|
||||
|
||||
Disables the disk cache for HTTP requests.
|
||||
|
||||
### --disable-http2
|
||||
## --disable-http2
|
||||
|
||||
Disable HTTP/2 and SPDY/3.1 protocols.
|
||||
|
||||
### --disable-renderer-backgrounding
|
||||
## --lang
|
||||
|
||||
Prevents Chromium from lowering the priority of invisible pages' renderer
|
||||
processes.
|
||||
Set a custom locale.
|
||||
|
||||
This flag is global to all renderer processes, if you only want to disable
|
||||
throttling in one window, you can take the hack of
|
||||
[playing silent audio][play-silent-audio].
|
||||
## --inspect=`port` and --inspect-brk=`port`
|
||||
|
||||
### --disk-cache-size=`size`
|
||||
Debug-related flags, see the [Debugging the Main Process][debugging-main-process] guide for details.
|
||||
|
||||
## --remote-debugging-port=`port`
|
||||
|
||||
Enables remote debugging over HTTP on the specified `port`.
|
||||
|
||||
## --disk-cache-size=`size`
|
||||
|
||||
Forces the maximum disk space to be used by the disk cache, in bytes.
|
||||
|
||||
### --enable-api-filtering-logging
|
||||
## --js-flags=`flags`
|
||||
|
||||
Enables caller stack logging for the following APIs (filtering events):
|
||||
- `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
||||
- `remote.require()` / `remote-require`
|
||||
- `remote.getGlobal()` / `remote-get-builtin`
|
||||
- `remote.getBuiltin()` / `remote-get-global`
|
||||
- `remote.getCurrentWindow()` / `remote-get-current-window`
|
||||
- `remote.getCurrentWebContents()` / `remote-get-current-web-contents`
|
||||
Specifies the flags passed to the Node.js engine. It has to be passed when starting
|
||||
Electron if you want to enable the `flags` in the main process.
|
||||
|
||||
### --enable-logging
|
||||
```sh
|
||||
$ electron --js-flags="--harmony_proxies --harmony_collections" your-app
|
||||
```
|
||||
|
||||
Prints Chromium's logging into console.
|
||||
See the [Node.js documentation][node-cli] or run `node --help` in your terminal for a list of available flags. Additionally, run `node --v8-options` to see a list of flags that specifically refer to Node.js's V8 JavaScript engine.
|
||||
|
||||
This switch can not be used in `app.commandLine.appendSwitch` since it is parsed
|
||||
earlier than user's app is loaded, but you can set the `ELECTRON_ENABLE_LOGGING`
|
||||
environment variable to achieve the same effect.
|
||||
## --proxy-server=`address:port`
|
||||
|
||||
### --host-rules=`rules`
|
||||
Use a specified proxy server, which overrides the system setting. This switch
|
||||
only affects requests with HTTP protocol, including HTTPS and WebSocket
|
||||
requests. It is also noteworthy that not all proxy servers support HTTPS and
|
||||
WebSocket requests. The proxy URL does not support username and password
|
||||
authentication [per Chromium issue](https://bugs.chromium.org/p/chromium/issues/detail?id=615947).
|
||||
|
||||
## --proxy-bypass-list=`hosts`
|
||||
|
||||
Instructs Electron to bypass the proxy server for the given semi-colon-separated
|
||||
list of hosts. This flag has an effect only if used in tandem with
|
||||
`--proxy-server`.
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const { app } = require('electron')
|
||||
app.commandLine.appendSwitch('proxy-bypass-list', '<local>;*.google.com;*foo.com;1.2.3.4:5678')
|
||||
```
|
||||
|
||||
Will use the proxy server for all hosts except for local addresses (`localhost`,
|
||||
`127.0.0.1` etc.), `google.com` subdomains, hosts that contain the suffix
|
||||
`foo.com` and anything at `1.2.3.4:5678`.
|
||||
|
||||
## --proxy-pac-url=`url`
|
||||
|
||||
Uses the PAC script at the specified `url`.
|
||||
|
||||
## --no-proxy-server
|
||||
|
||||
Don't use a proxy server and always make direct connections. Overrides any other
|
||||
proxy server flags that are passed.
|
||||
|
||||
## --host-rules=`rules`
|
||||
|
||||
A comma-separated list of `rules` that control how hostnames are mapped.
|
||||
|
||||
@@ -93,96 +107,69 @@ These mappings apply to the endpoint host in a net request (the TCP connect
|
||||
and host resolver in a direct connection, and the `CONNECT` in an HTTP proxy
|
||||
connection, and the endpoint host in a `SOCKS` proxy connection).
|
||||
|
||||
### --host-resolver-rules=`rules`
|
||||
## --host-resolver-rules=`rules`
|
||||
|
||||
Like `--host-rules` but these `rules` only apply to the host resolver.
|
||||
|
||||
### --ignore-certificate-errors
|
||||
## --auth-server-whitelist=`url`
|
||||
|
||||
Ignores certificate related errors.
|
||||
|
||||
### --ignore-connections-limit=`domains`
|
||||
|
||||
Ignore the connections limit for `domains` list separated by `,`.
|
||||
|
||||
### --js-flags=`flags`
|
||||
|
||||
Specifies the flags passed to the Node.js engine. It has to be passed when starting
|
||||
Electron if you want to enable the `flags` in the main process.
|
||||
|
||||
```sh
|
||||
$ electron --js-flags="--harmony_proxies --harmony_collections" your-app
|
||||
```
|
||||
|
||||
See the [Node.js documentation][node-cli] or run `node --help` in your terminal for a list of available flags. Additionally, run `node --v8-options` to see a list of flags that specifically refer to Node.js's V8 JavaScript engine.
|
||||
|
||||
### --lang
|
||||
|
||||
Set a custom locale.
|
||||
|
||||
### --log-net-log=`path`
|
||||
|
||||
Enables net log events to be saved and writes them to `path`.
|
||||
|
||||
### --no-proxy-server
|
||||
|
||||
Don't use a proxy server and always make direct connections. Overrides any other
|
||||
proxy server flags that are passed.
|
||||
|
||||
### --no-sandbox
|
||||
|
||||
Disables Chromium sandbox, which is now enabled by default.
|
||||
Should only be used for testing.
|
||||
|
||||
### --proxy-bypass-list=`hosts`
|
||||
|
||||
Instructs Electron to bypass the proxy server for the given semi-colon-separated
|
||||
list of hosts. This flag has an effect only if used in tandem with
|
||||
`--proxy-server`.
|
||||
A comma-separated list of servers for which integrated authentication is enabled.
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const { app } = require('electron')
|
||||
app.commandLine.appendSwitch('proxy-bypass-list', '<local>;*.google.com;*foo.com;1.2.3.4:5678')
|
||||
```sh
|
||||
--auth-server-whitelist='*example.com, *foobar.com, *baz'
|
||||
```
|
||||
|
||||
Will use the proxy server for all hosts except for local addresses (`localhost`,
|
||||
`127.0.0.1` etc.), `google.com` subdomains, hosts that contain the suffix
|
||||
`foo.com` and anything at `1.2.3.4:5678`.
|
||||
then any `url` ending with `example.com`, `foobar.com`, `baz` will be considered
|
||||
for integrated authentication. Without `*` prefix the URL has to match exactly.
|
||||
|
||||
### --proxy-pac-url=`url`
|
||||
## --auth-negotiate-delegate-whitelist=`url`
|
||||
|
||||
Uses the PAC script at the specified `url`.
|
||||
A comma-separated list of servers for which delegation of user credentials is required.
|
||||
Without `*` prefix the URL has to match exactly.
|
||||
|
||||
### --proxy-server=`address:port`
|
||||
## --ignore-certificate-errors
|
||||
|
||||
Use a specified proxy server, which overrides the system setting. This switch
|
||||
only affects requests with HTTP protocol, including HTTPS and WebSocket
|
||||
requests. It is also noteworthy that not all proxy servers support HTTPS and
|
||||
WebSocket requests. The proxy URL does not support username and password
|
||||
authentication [per Chromium issue](https://bugs.chromium.org/p/chromium/issues/detail?id=615947).
|
||||
Ignores certificate related errors.
|
||||
|
||||
### --remote-debugging-port=`port`
|
||||
|
||||
Enables remote debugging over HTTP on the specified `port`.
|
||||
|
||||
### --ppapi-flash-path=`path`
|
||||
## --ppapi-flash-path=`path`
|
||||
|
||||
Sets the `path` of the pepper flash plugin.
|
||||
|
||||
### --ppapi-flash-version=`version`
|
||||
## --ppapi-flash-version=`version`
|
||||
|
||||
Sets the `version` of the pepper flash plugin.
|
||||
|
||||
### --v=`log_level`
|
||||
## --log-net-log=`path`
|
||||
|
||||
Enables net log events to be saved and writes them to `path`.
|
||||
|
||||
## --disable-renderer-backgrounding
|
||||
|
||||
Prevents Chromium from lowering the priority of invisible pages' renderer
|
||||
processes.
|
||||
|
||||
This flag is global to all renderer processes, if you only want to disable
|
||||
throttling in one window, you can take the hack of
|
||||
[playing silent audio][play-silent-audio].
|
||||
|
||||
## --enable-logging
|
||||
|
||||
Prints Chromium's logging into console.
|
||||
|
||||
This switch can not be used in `app.commandLine.appendSwitch` since it is parsed
|
||||
earlier than user's app is loaded, but you can set the `ELECTRON_ENABLE_LOGGING`
|
||||
environment variable to achieve the same effect.
|
||||
|
||||
## --v=`log_level`
|
||||
|
||||
Gives the default maximal active V-logging level; 0 is the default. Normally
|
||||
positive values are used for V-logging levels.
|
||||
|
||||
This switch only works when `--enable-logging` is also passed.
|
||||
|
||||
### --vmodule=`pattern`
|
||||
## --vmodule=`pattern`
|
||||
|
||||
Gives the per-module maximal V-logging levels to override the value given by
|
||||
`--v`. E.g. `my_module=2,foo*=3` would change the logging level for all code in
|
||||
@@ -194,38 +181,20 @@ logging level for all code in the source files under a `foo/bar` directory.
|
||||
|
||||
This switch only works when `--enable-logging` is also passed.
|
||||
|
||||
## Node.js Flags
|
||||
## --enable-api-filtering-logging
|
||||
|
||||
Electron supports some of the [CLI flags][node-cli] supported by Node.js.
|
||||
Enables caller stack logging for the following APIs (filtering events):
|
||||
- `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
||||
- `remote.require()` / `remote-require`
|
||||
- `remote.getGlobal()` / `remote-get-builtin`
|
||||
- `remote.getBuiltin()` / `remote-get-global`
|
||||
- `remote.getCurrentWindow()` / `remote-get-current-window`
|
||||
- `remote.getCurrentWebContents()` / `remote-get-current-web-contents`
|
||||
|
||||
**Note:** Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect.
|
||||
## --no-sandbox
|
||||
|
||||
### --inspect-brk[=[host:]port]
|
||||
|
||||
Activate inspector on host:port and break at start of user script. Default host:port is 127.0.0.1:9229.
|
||||
|
||||
Aliased to `--debug-brk=[host:]port`.
|
||||
|
||||
### --inspect-port=[host:]port
|
||||
|
||||
Set the `host:port` to be used when the inspector is activated. Useful when activating the inspector by sending the SIGUSR1 signal. Default host is `127.0.0.1`.
|
||||
|
||||
Aliased to `--debug-port=[host:]port`.
|
||||
|
||||
### --inspect[=[host:]port]
|
||||
|
||||
Activate inspector on `host:port`. Default is `127.0.0.1:9229`.
|
||||
|
||||
V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug and profile Electron instances. The tools attach to Electron instances via a tcp port and communicate using the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
|
||||
|
||||
See the [Debugging the Main Process][debugging-main-process] guide for more details.
|
||||
|
||||
Aliased to `--debug[=[host:]port`.
|
||||
|
||||
### --inspect-publish-uid=stderr,http
|
||||
Specify ways of the inspector web socket url exposure.
|
||||
|
||||
By default inspector websocket url is available in stderr and under /json/list endpoint on http://host:port/json/list.
|
||||
Disables Chromium sandbox, which is now enabled by default.
|
||||
Should only be used for testing.
|
||||
|
||||
[app]: app.md
|
||||
[append-switch]: app.md#appcommandlineappendswitchswitch-value
|
||||
|
||||
@@ -82,16 +82,12 @@ The API is only available in session's `will-download` callback function.
|
||||
If user doesn't set the save path via the API, Electron will use the original
|
||||
routine to determine the save path; this usually prompts a save dialog.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md): use the `savePath` property instead.**
|
||||
|
||||
#### `downloadItem.getSavePath()`
|
||||
|
||||
Returns `String` - The save path of the download item. This will be either the path
|
||||
set via `downloadItem.setSavePath(path)` or the path selected from the shown
|
||||
save dialog.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md): use the `savePath` property instead.**
|
||||
|
||||
#### `downloadItem.setSaveDialogOptions(options)`
|
||||
|
||||
* `options` SaveDialogOptions - Set the save file dialog options. This object has the same
|
||||
|
||||
@@ -80,18 +80,6 @@ and spawned child processes that set `ELECTRON_RUN_AS_NODE`.
|
||||
|
||||
Starts the process as a normal Node.js process.
|
||||
|
||||
In this mode, you will be able to pass [cli options](https://nodejs.org/api/cli.html) to Node.js as
|
||||
you would when running the normal Node.js executable, with the exception of the following flags:
|
||||
|
||||
* "--openssl-config"
|
||||
* "--use-bundled-ca"
|
||||
* "--use-openssl-ca",
|
||||
* "--force-fips"
|
||||
* "--enable-fips"
|
||||
|
||||
These flags are disabled owing to the fact that Electron uses BoringSSL instead of OpenSSL when building Node.js'
|
||||
`crypto` module, and so will not work as designed.
|
||||
|
||||
### `ELECTRON_NO_ATTACH_CONSOLE` _Windows_
|
||||
|
||||
Don't attach to the current console session.
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
# Chrome Extension Support
|
||||
|
||||
Electron supports a subset of the [Chrome Extensions
|
||||
API][chrome-extensions-api-index], primarily to support DevTools extensions and
|
||||
Chromium-internal extensions, but it also happens to support some other
|
||||
extension capabilities.
|
||||
|
||||
[chrome-extensions-api-index]: https://developer.chrome.com/extensions/api_index
|
||||
|
||||
> **Note:** Electron does not support arbitrary Chrome extensions from the
|
||||
> store, and it is a **non-goal** of the Electron project to be perfectly
|
||||
> compatible with Chrome's implementation of Extensions.
|
||||
|
||||
## Loading extensions
|
||||
|
||||
Electron only supports loading unpacked extensions (i.e., `.crx` files do not
|
||||
work). Extensions are installed per-`session`. To load an extension, call
|
||||
[`ses.loadExtension`](session.md#sesloadextensionpath):
|
||||
|
||||
```js
|
||||
const { session } = require('electron')
|
||||
|
||||
session.loadExtension('path/to/unpacked/extension').then(({ id }) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
Loaded extensions will not be automatically remembered across exits; if you do
|
||||
not call `loadExtension` when the app runs, the extension will not be loaded.
|
||||
|
||||
Note that loading extensions is only supported in persistent sessions.
|
||||
Attempting to load an extension into an in-memory session will throw an error.
|
||||
|
||||
See the [`session`](session.md) documentation for more information about
|
||||
loading, unloading, and querying active extensions.
|
||||
|
||||
## Supported Extensions APIs
|
||||
|
||||
We support the following extensions APIs, with some caveats. Other APIs may
|
||||
additionally be supported, but support for any APIs not listed here is
|
||||
provisional and may be removed.
|
||||
|
||||
### `chrome.devtools.inspectedWindow`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
### `chrome.devtools.network`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
### `chrome.devtools.panels`
|
||||
|
||||
All features of this API are supported.
|
||||
|
||||
### `chrome.extension`
|
||||
|
||||
The following properties of `chrome.extension` are supported:
|
||||
|
||||
- `chrome.extension.lastError`
|
||||
|
||||
The following methods of `chrome.extension` are supported:
|
||||
|
||||
- `chrome.extension.getURL`
|
||||
- `chrome.extension.getBackgroundPage`
|
||||
|
||||
### `chrome.runtime`
|
||||
|
||||
The following properties of `chrome.runtime` are supported:
|
||||
|
||||
- `chrome.runtime.lastError`
|
||||
- `chrome.runtime.id`
|
||||
|
||||
The following methods of `chrome.runtime` are supported:
|
||||
|
||||
- `chrome.runtime.getBackgroundPage`
|
||||
- `chrome.runtime.getManifest`
|
||||
- `chrome.runtime.getURL`
|
||||
- `chrome.runtime.connect`
|
||||
- `chrome.runtime.sendMessage`
|
||||
|
||||
The following events of `chrome.runtime` are supported:
|
||||
|
||||
- `chrome.runtime.onStartup`
|
||||
- `chrome.runtime.onInstalled`
|
||||
- `chrome.runtime.onSuspend`
|
||||
- `chrome.runtime.onSuspendCanceled`
|
||||
- `chrome.runtime.onConnect`
|
||||
- `chrome.runtime.onMessage`
|
||||
|
||||
### `chrome.storage`
|
||||
|
||||
Only `chrome.storage.local` is supported; `chrome.storage.sync` and
|
||||
`chrome.storage.managed` are not.
|
||||
|
||||
### `chrome.tabs`
|
||||
|
||||
The following methods of `chrome.tabs` are supported:
|
||||
|
||||
- `chrome.tabs.sendMessage`
|
||||
- `chrome.tabs.executeScript`
|
||||
|
||||
> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
> supported and will raise an error.
|
||||
@@ -45,9 +45,3 @@ The Electron team is currently undergoing an initiative to convert separate gett
|
||||
* `isMacTemplateImage`
|
||||
* `SystemPreferences` module
|
||||
* `appLevelAppearance`
|
||||
* `webContents` module
|
||||
* `audioMuted`
|
||||
* `frameRate`
|
||||
* `userAgent`
|
||||
* `zoomFactor`
|
||||
* `zoomLevel`
|
||||
|
||||
@@ -276,14 +276,10 @@ Returns [`Size`](structures/size.md)
|
||||
|
||||
Marks the image as a template image.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `image.isTemplateImage()`
|
||||
|
||||
Returns `Boolean` - Whether the image is a template image.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `image.crop(rect)`
|
||||
|
||||
* `rect` [Rectangle](structures/rectangle.md) - The area of the image to crop.
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
## Class: ServiceWorkers
|
||||
|
||||
> Query and receive events from a sessions active service workers.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)
|
||||
|
||||
Instances of the `ServiceWorkers` class are accessed by using `serviceWorkers` property of
|
||||
a `Session`.
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const { session } = require('electron')
|
||||
|
||||
// Get all service workers.
|
||||
console.log(session.defaultSession.serviceWorkers.getAllRunning())
|
||||
|
||||
// Handle logs and get service worker info
|
||||
session.defaultSession.serviceWorkers.on('console-message', (event, messageDetails) => {
|
||||
console.log(
|
||||
'Got service worker message',
|
||||
messageDetails,
|
||||
'from',
|
||||
session.defaultSession.serviceWorkers.getFromVersionID(messageDetails.versionId)
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Events
|
||||
|
||||
The following events are available on instances of `ServiceWorkers`:
|
||||
|
||||
#### Event: 'console-message'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `messageDetails` Object - Information about the console message
|
||||
* `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`.
|
||||
* `sourceUrl` String - The URL the message came from
|
||||
* `lineNumber` Number - The line number of the source that triggered this console message
|
||||
|
||||
Emitted when a service worker logs something to the console.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
The following methods are available on instances of `ServiceWorkers`:
|
||||
|
||||
#### `serviceWorkers.getAllRunning()`
|
||||
|
||||
Returns `Record<Number, ServiceWorkerInfo>` - A [ServiceWorkerInfo](structures/service-worker-info.md) object where the keys are the service worker version ID and the values are the information about that service worker.
|
||||
|
||||
#### `serviceWorkers.getFromVersionID(versionId)`
|
||||
|
||||
* `versionId` Number
|
||||
|
||||
Returns [`ServiceWorkerInfo`](structures/service-worker-info.md) - Information about this service worker
|
||||
|
||||
If the service worker does not exist or is not running this method will throw an exception.
|
||||
## Class: ServiceWorkers
|
||||
|
||||
> Query and receive events from a sessions active service workers.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)
|
||||
|
||||
Instances of the `ServiceWorkers` class are accessed by using `serviceWorkers` property of
|
||||
a `Session`.
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const { session } = require('electron')
|
||||
|
||||
// Get all service workers.
|
||||
console.log(session.defaultSession.serviceWorkers.getAllRunning())
|
||||
|
||||
// Handle logs and get service worker info
|
||||
session.defaultSession.serviceWorkers.on('console-message', (event, messageDetails) => {
|
||||
console.log(
|
||||
'Got service worker message',
|
||||
messageDetails,
|
||||
'from',
|
||||
session.defaultSession.serviceWorkers.getFromVersionID(messageDetails.versionId)
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Events
|
||||
|
||||
The following events are available on instances of `ServiceWorkers`:
|
||||
|
||||
#### Event: 'console-message'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `messageDetails` Object - Information about the console message
|
||||
* `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`.
|
||||
* `sourceUrl` String - The URL the message came from
|
||||
* `lineNumber` Number - The line number of the source that triggered this console message
|
||||
|
||||
Emitted when a service worker logs something to the console.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
The following methods are available on instances of `ServiceWorkers`:
|
||||
|
||||
#### `serviceWorkers.getAllRunning()`
|
||||
|
||||
Returns `Record<Number, ServiceWorkerInfo>` - A [ServiceWorkerInfo](structures/service-worker-info.md) object where the keys are the service worker version ID and the values are the information about that service worker.
|
||||
|
||||
#### `serviceWorkers.getFromVersionID(versionId)`
|
||||
|
||||
* `versionId` Number
|
||||
|
||||
Returns [`ServiceWorkerInfo`](structures/service-worker-info.md) - Information about this service worker
|
||||
|
||||
If the service worker does not exist or is not running this method will throw an exception.
|
||||
|
||||
@@ -105,6 +105,45 @@ Returns:
|
||||
Emitted when a render process requests preconnection to a URL, generally due to
|
||||
a [resource hint](https://w3c.github.io/resource-hints/).
|
||||
|
||||
#### Event: 'spellcheck-dictionary-initialized'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `languageCode` String - The language code of the dictionary file
|
||||
|
||||
Emitted when a hunspell dictionary file has been successfully initialized. This
|
||||
occurs after the file has been downloaded.
|
||||
|
||||
#### Event: 'spellcheck-dictionary-download-begin'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `languageCode` String - The language code of the dictionary file
|
||||
|
||||
Emitted when a hunspell dictionary file starts downloading
|
||||
|
||||
#### Event: 'spellcheck-dictionary-download-success'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `languageCode` String - The language code of the dictionary file
|
||||
|
||||
Emitted when a hunspell dictionary file has been successfully downloaded
|
||||
|
||||
#### Event: 'spellcheck-dictionary-download-failure'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `languageCode` String - The language code of the dictionary file
|
||||
|
||||
Emitted when a hunspell dictionary file download fails. For details
|
||||
on the failure you should collect a netlog and inspect the download
|
||||
request.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
The following methods are available on instances of `Session`:
|
||||
@@ -483,7 +522,12 @@ setting with the current OS locale. This setting is persisted across restarts.
|
||||
By default Electron will download hunspell dictionaries from the Chromium CDN. If you want to override this
|
||||
behavior you can use this API to point the dictionary downloader at your own hosted version of the hunspell
|
||||
dictionaries. We publish a `hunspell_dictionaries.zip` file with each release which contains the files you need
|
||||
to host here.
|
||||
to host here, the file server must be **case insensitive** you must upload each file twice, once with the case it
|
||||
has in the ZIP file and once with the filename as all lower case.
|
||||
|
||||
If the files present in `hunspell_dictionaries.zip` are available at `https://example.com/dictionaries/language-code.bdic`
|
||||
then you should call this api with `ses.setSpellCheckerDictionaryDownloadURL('https://example.com/dictionaries/')`. Please
|
||||
note the trailing slash. The URL to the dictionaries is formed as `${url}${filename}`.
|
||||
|
||||
**Note:** On macOS the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS.
|
||||
|
||||
@@ -496,7 +540,8 @@ Resolves when the full dictionary is loaded from disk.
|
||||
|
||||
* `word` String - The word you want to add to the dictionary
|
||||
|
||||
Returns `Boolean` - Whether the word was successfully written to the custom dictionary.
|
||||
Returns `Boolean` - Whether the word was successfully written to the custom dictionary. This API
|
||||
will not work on non-persistent (in-memory) sessions.
|
||||
|
||||
**Note:** On macOS and Windows 10 this word will be written to the OS custom dictionary as well
|
||||
|
||||
@@ -504,7 +549,8 @@ Returns `Boolean` - Whether the word was successfully written to the custom dict
|
||||
|
||||
* `word` String - The word you want to remove from the dictionary
|
||||
|
||||
Returns `Boolean` - Whether the word was successfully removed from the custom dictionary.
|
||||
Returns `Boolean` - Whether the word was successfully removed from the custom dictionary. This API
|
||||
will not work on non-persistent (in-memory) sessions.
|
||||
|
||||
**Note:** On macOS and Windows 10 this word will be removed from the OS custom dictionary as well
|
||||
|
||||
@@ -520,8 +566,6 @@ requests an API that Electron does not support) then they will be logged to the
|
||||
console.
|
||||
|
||||
Note that Electron does not support the full range of Chrome extensions APIs.
|
||||
See [Supported Extensions APIs](extensions.md#supported-extensions-apis) for
|
||||
more details on what is supported.
|
||||
|
||||
Note that in previous versions of Electron, extensions that were loaded would
|
||||
be remembered for future runs of the application. This is no longer the case:
|
||||
@@ -544,9 +588,6 @@ This API does not support loading packed (.crx) extensions.
|
||||
**Note:** This API cannot be called before the `ready` event of the `app` module
|
||||
is emitted.
|
||||
|
||||
**Note:** Loading extensions into in-memory (non-persistent) sessions is not
|
||||
supported and will throw an error.
|
||||
|
||||
#### `ses.removeExtension(extensionId)`
|
||||
|
||||
* `extensionId` String - ID of extension to remove
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Extension Object
|
||||
|
||||
* `id` String
|
||||
* `manifest` any - Copy of the [extension's manifest data](https://developer.chrome.com/extensions/manifest).
|
||||
* `name` String
|
||||
* `path` String - The extension's file path.
|
||||
* `version` String
|
||||
* `url` String - The extension's `chrome-extension://` URL.
|
||||
|
||||
@@ -360,7 +360,7 @@ Returns `Boolean` - `true` if an inverted color scheme (a high contrast color sc
|
||||
|
||||
Returns `Boolean` - `true` if a high contrast theme is active, `false` otherwise.
|
||||
|
||||
**Depreacted:** Should use the new [`nativeTheme.shouldUseHighContrastColors`](native-theme.md#nativethemeshouldusehighcontrastcolors-macos-windows-readonly) API.
|
||||
**Deprecated:** Should use the new [`nativeTheme.shouldUseHighContrastColors`](native-theme.md#nativethemeshouldusehighcontrastcolors-macos-windows-readonly) API.
|
||||
|
||||
### `systemPreferences.getEffectiveAppearance()` _macOS_
|
||||
|
||||
@@ -369,16 +369,6 @@ Returns `String` - Can be `dark`, `light` or `unknown`.
|
||||
Gets the macOS appearance setting that is currently applied to your application,
|
||||
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
|
||||
|
||||
Please note that until Electron is built targeting the 10.14 SDK, your application's
|
||||
`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In
|
||||
the interim in order for your application to inherit the OS preference you must set the
|
||||
`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are
|
||||
using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode`
|
||||
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
|
||||
for more details.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_
|
||||
|
||||
Returns `String` | `null` - Can be `dark`, `light` or `unknown`.
|
||||
@@ -387,8 +377,6 @@ Gets the macOS appearance setting that you have declared you want for
|
||||
your application, maps to [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc).
|
||||
You can use the `setAppLevelAppearance` API to set this value.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_ _Deprecated_
|
||||
|
||||
* `appearance` String | null - Can be `dark` or `light`
|
||||
@@ -396,8 +384,6 @@ You can use the `setAppLevelAppearance` API to set this value.
|
||||
Sets the appearance setting for your application, this should override the
|
||||
system default and override the value of `getEffectiveAppearance`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
### `systemPreferences.canPromptTouchID()` _macOS_
|
||||
|
||||
Returns `Boolean` - whether or not this device has the ability to use Touch ID.
|
||||
@@ -478,11 +464,3 @@ A `String` property that can be `dark`, `light` or `unknown`.
|
||||
|
||||
Returns the macOS appearance setting that is currently applied to your application,
|
||||
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
|
||||
|
||||
Please note that until Electron is built targeting the 10.14 SDK, your application's
|
||||
`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In
|
||||
the interim in order for your application to inherit the OS preference you must set the
|
||||
`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are
|
||||
using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode`
|
||||
packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
|
||||
for more details.
|
||||
|
||||
@@ -310,7 +310,7 @@ and allow the page to be unloaded.
|
||||
const { BrowserWindow, dialog } = require('electron')
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.webContents.on('will-prevent-unload', (event) => {
|
||||
const choice = dialog.showMessageBoxSync(win, {
|
||||
const choice = dialog.showMessageBox(win, {
|
||||
type: 'question',
|
||||
buttons: ['Leave', 'Stay'],
|
||||
title: 'Do you want to leave this site?',
|
||||
@@ -967,14 +967,10 @@ Returns `Boolean` - Whether the renderer process has crashed.
|
||||
|
||||
Overrides the user agent for this web page.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.getUserAgent()`
|
||||
|
||||
Returns `String` - The user agent for this web page.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.insertCSS(css[, options])`
|
||||
|
||||
* `css` String
|
||||
@@ -1054,33 +1050,27 @@ Ignore application menu shortcuts while this web contents is focused.
|
||||
|
||||
Mute the audio on the current web page.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.isAudioMuted()`
|
||||
|
||||
Returns `Boolean` - Whether this page has been muted.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.isCurrentlyAudible()`
|
||||
|
||||
Returns `Boolean` - Whether audio is currently playing.
|
||||
|
||||
#### `contents.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Number - Zoom factor.
|
||||
* `factor` Double - Zoom factor; default is 1.0.
|
||||
|
||||
Changes the zoom factor to the specified factor. Zoom factor is
|
||||
zoom percent divided by 100, so 300% = 3.0.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
The factor must be greater than 0.0.
|
||||
|
||||
#### `contents.getZoomFactor()`
|
||||
|
||||
Returns `Number` - the current zoom factor.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.setZoomLevel(level)`
|
||||
|
||||
* `level` Number - Zoom level.
|
||||
@@ -1090,14 +1080,10 @@ increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively. The formula for this is
|
||||
`scale := 1.2 ^ level`.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.getZoomLevel()`
|
||||
|
||||
Returns `Number` - the current zoom level.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.setVisualZoomLevelLimits(minimumLevel, maximumLevel)`
|
||||
|
||||
* `minimumLevel` Number
|
||||
@@ -1286,7 +1272,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`.
|
||||
* `callback` Function (optional)
|
||||
* `success` Boolean - Indicates success of the print call.
|
||||
* `failureReason` String - Called back if the print fails; can be `cancelled` or `failed`.
|
||||
* `failureReason` String - Error description called back if the print fails.
|
||||
|
||||
Prints window's web page. When `silent` is set to `true`, Electron will pick
|
||||
the system's default printer if `deviceName` is empty and the default settings for printing.
|
||||
@@ -1709,14 +1695,10 @@ Returns `Boolean` - If *offscreen rendering* is enabled returns whether it is cu
|
||||
If *offscreen rendering* is enabled sets the frame rate to the specified number.
|
||||
Only values between 1 and 60 are accepted.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.getFrameRate()`
|
||||
|
||||
Returns `Integer` - If *offscreen rendering* is enabled returns the current frame rate.
|
||||
|
||||
**[Deprecated](modernization/property-updates.md)**
|
||||
|
||||
#### `contents.invalidate()`
|
||||
|
||||
Schedules a full repaint of the window this web contents is in.
|
||||
|
||||
@@ -22,11 +22,13 @@ The `WebFrame` class has the following instance methods:
|
||||
|
||||
### `webFrame.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Number - Zoom factor.
|
||||
* `factor` Double - Zoom factor; default is 1.0.
|
||||
|
||||
Changes the zoom factor to the specified factor. Zoom factor is
|
||||
zoom percent divided by 100, so 300% = 3.0.
|
||||
|
||||
The factor must be greater than 0.0.
|
||||
|
||||
### `webFrame.getZoomFactor()`
|
||||
|
||||
Returns `Number` - The current zoom factor.
|
||||
@@ -122,13 +124,20 @@ by its key, which is returned from `webFrame.insertCSS(css)`.
|
||||
|
||||
Inserts `text` to the focused element.
|
||||
|
||||
### `webFrame.executeJavaScript(code[, userGesture])`
|
||||
### `webFrame.executeJavaScript(code[, userGesture, callback])`
|
||||
|
||||
* `code` String
|
||||
* `userGesture` Boolean (optional) - Default is `false`.
|
||||
* `callback` Function (optional) - Called after script has been executed. Unless
|
||||
the frame is suspended (e.g. showing a modal alert), execution will be
|
||||
synchronous and the callback will be invoked before the method returns. For
|
||||
compatibility with an older version of this method, the error parameter is
|
||||
second.
|
||||
* `result` Any
|
||||
* `error` Error
|
||||
|
||||
Returns `Promise<any>` - A promise that resolves with the result of the executed code
|
||||
or is rejected if the result of the code is a rejected promise.
|
||||
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.
|
||||
|
||||
Evaluates `code` in page.
|
||||
|
||||
@@ -136,14 +145,24 @@ In the browser window some HTML APIs like `requestFullScreen` can only be
|
||||
invoked by a gesture from the user. Setting `userGesture` to `true` will remove
|
||||
this limitation.
|
||||
|
||||
### `webFrame.executeJavaScriptInIsolatedWorld(worldId, scripts[, userGesture])`
|
||||
### `webFrame.executeJavaScriptInIsolatedWorld(worldId, scripts[, userGesture, callback])`
|
||||
|
||||
* `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. You can provide any integer here.
|
||||
* `worldId` Integer - The ID of the world to run the javascript
|
||||
in, `0` is the default main world (where content runs), `999` is the
|
||||
world used by Electron's `contextIsolation` feature. Accepts values
|
||||
in the range 1..536870911.
|
||||
* `scripts` [WebSource[]](structures/web-source.md)
|
||||
* `userGesture` Boolean (optional) - Default is `false`.
|
||||
* `callback` Function (optional) - Called after script has been executed. Unless
|
||||
the frame is suspended (e.g. showing a modal alert), execution will be
|
||||
synchronous and the callback will be invoked before the method returns. For
|
||||
compatibility with an older version of this method, the error parameter is
|
||||
second.
|
||||
* `result` Any
|
||||
* `error` Error
|
||||
|
||||
Returns `Promise<any>` - A promise that resolves with the result of the executed code
|
||||
or is rejected if the result of the code is a rejected promise.
|
||||
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.
|
||||
|
||||
Works like `executeJavaScript` but evaluates `scripts` in an isolated context.
|
||||
|
||||
|
||||
@@ -6,26 +6,6 @@ Breaking changes will be documented here, and deprecation warnings added to JS c
|
||||
|
||||
The `FIXME` string is used in code comments to denote things that should be fixed for future releases. See https://github.com/electron/electron/search?q=fixme
|
||||
|
||||
## Planned Breaking API Changes (10.0)
|
||||
|
||||
### `enableRemoteModule` defaults to `false`
|
||||
|
||||
In Electron 9, using the remote module without explicitly enabling it via the
|
||||
`enableRemoteModule` WebPreferences option began emitting a warning. In
|
||||
Electron 10, the remote module is now disabled by default. To use the remote
|
||||
module, `enableRemoteModule: true` must be specified in WebPreferences:
|
||||
|
||||
```js
|
||||
const w = new BrowserWindow({
|
||||
webPreferences: {
|
||||
enableRemoteModule: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
We [recommend moving away from the remote
|
||||
module](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31).
|
||||
|
||||
## Planned Breaking API Changes (9.0)
|
||||
|
||||
### `<webview>.getWebContents()`
|
||||
|
||||
@@ -48,7 +48,7 @@ still set breakpoints - Visual Studio will automatically figure out that the
|
||||
source code matches the code running in the attached process and break
|
||||
accordingly.
|
||||
|
||||
Relevant code files can be found in `./shell/`.
|
||||
Relevant code files can be found in `./atom/`.
|
||||
|
||||
### Attaching
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ this basic introduction, let's assume that you're calling a command from JavaScr
|
||||
that isn't behaving correctly - so you'd like to break on that command's C++
|
||||
counterpart inside the Electron source.
|
||||
|
||||
Relevant code files can be found in `./shell/`.
|
||||
Relevant code files can be found in `./atom/`.
|
||||
|
||||
Let's assume that you want to debug `app.setName()`, which is defined in `browser.cc`
|
||||
as `Browser::SetName()`. Set the breakpoint using the `breakpoint` command, specifying
|
||||
|
||||
@@ -55,7 +55,7 @@ $ git checkout -b my-branch -t upstream/master
|
||||
### Step 4: Code
|
||||
|
||||
Most pull requests opened against the `electron/electron` repository include
|
||||
changes to either the C/C++ code in the `shell/` folder,
|
||||
changes to either the C/C++ code in the `atom/` folder,
|
||||
the JavaScript code in the `lib/` folder, the documentation in `docs/api/`
|
||||
or tests in the `spec/` folder.
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/menu"
|
||||
<a href="http://electron.atom.io/docs/api/menu"
|
||||
>full API documentation<span
|
||||
>(opens in new window)</span
|
||||
></a
|
||||
@@ -111,7 +111,7 @@
|
||||
<p>
|
||||
See the full
|
||||
<a
|
||||
href="https://electronjs.org/docs/api/web-contents/#event-context-menu"
|
||||
href="http://electron.atom.io/docs/api/web-contents/#event-context-menu"
|
||||
>context-menu event documentation</a
|
||||
>
|
||||
for all the available properties.
|
||||
|
||||
@@ -159,7 +159,7 @@ const template = [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: () => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
<p>
|
||||
Open the full documentation for the
|
||||
<a href="https://electronjs.org/docs/api/menu">Menu</a>,
|
||||
<a href="https://electronjs.org/docs/api/accelerator">Accelerator</a>,
|
||||
<a href="http://electron.atom.io/docs/api/menu">Menu</a>,
|
||||
<a href="http://electron.atom.io/docs/api/accelerator">Accelerator</a>,
|
||||
and
|
||||
<a href="https://electronjs.org/docs/api/global-shortcut">globalShortcut</a>
|
||||
<a href="http://electron.atom.io/docs/api/global-shortcut">globalShortcut</a>
|
||||
APIs in your browser.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/dialog/">
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/dialog/">
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/dialog/">
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/dialog/">
|
||||
<a href="http://electron.atom.io/docs/api/dialog/">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
const { shell } = require('electron')
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
})
|
||||
</code></pre>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const { shell } = require('electron')
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
})
|
||||
|
||||
const OpenAllOutboundLinks = () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<p>This module works in both the main and renderer process.</p>
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/shell">
|
||||
<a href="http://electron.atom.io/docs/api/shell">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -9,5 +9,5 @@ fileManagerBtn.addEventListener('click', (event) => {
|
||||
})
|
||||
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/all/#notifications-windows-linux-macos">
|
||||
<a href="https://electron.atom.io/docs/all/#notifications-windows-linux-macos">
|
||||
full API documentation<span>(opens in new window)</span>
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
<a href="http://electron.atom.io/docs/api/tray">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
@@ -108,7 +108,7 @@ ipc.on('tray-removed', function () {
|
||||
On Linux distributions that only have app indicator support, users
|
||||
will need to install <code>libappindicator1</code> to make the
|
||||
tray icon work. See the
|
||||
<a href="https://electronjs.org/docs/api/tray">
|
||||
<a href="http://electron.atom.io/docs/api/tray">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
for more details about using Tray on Linux.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<h3>The <code>app</code> module provides methods for handling protocols.</h3>
|
||||
<p>These methods allow you to set and unset the protocols your app should be the default app for. Similar to when a browser asks to be your default for viewing web pages.</p>
|
||||
|
||||
<p>Open the <a href="https://electronjs.org/docs/api/app">full app API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> in your browser.</p>
|
||||
<p>Open the <a href="http://electron.atom.io/docs/api/app">full app API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> in your browser.</p>
|
||||
</header>
|
||||
|
||||
<div >
|
||||
@@ -89,4 +89,4 @@
|
||||
</html>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<p>The <code>process</code> module is built into Node.js (therefore you can use this in both the main and renderer processes) and in Electron apps this object has a few more useful properties on it.</p>
|
||||
<p>The example below gets the version of Electron in use by the app.</p>
|
||||
<p>See the <a href="https://electronjs.org/docs/api/process">process documentation <span>(opens in new window)</span></a> for more.</p>
|
||||
<p>See the <a href="http://electron.atom.io/docs/api/process">process documentation <span>(opens in new window)</span></a> for more.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
<a href="http://electron.atom.io/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
<p>
|
||||
For more details, see the
|
||||
<a href="https://electronjs.org/docs/api/frameless-window/">
|
||||
<a href="http://electron.atom.io/docs/api/frameless-window/">
|
||||
Frameless Window
|
||||
</a>
|
||||
documentation.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
<a href="http://electron.atom.io/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
@@ -47,7 +47,7 @@
|
||||
There are a lot of methods for controlling the state of the window
|
||||
such as the size, location, and focus status as well as events to
|
||||
listen to for window changes. Visit the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
<a href="http://electron.atom.io/docs/api/browser-window">
|
||||
documentation (opens in new window)
|
||||
</a>
|
||||
for the full list.
|
||||
|
||||
@@ -15,5 +15,5 @@ newWindowBtn.addEventListener('click', (event) => {
|
||||
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal("https://electronjs.org/docs/api/browser-window")
|
||||
shell.openExternal("http://electron.atom.io/docs/api/browser-window")
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<p>
|
||||
Open the
|
||||
<a href="https://electronjs.org/docs/api/browser-window">
|
||||
<a href="http://electron.atom.io/docs/api/browser-window">
|
||||
full API documentation (opens in new window)
|
||||
</a>
|
||||
in your browser.
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
| 6.0.0 | 2019-05-01 | 2019-07-30 | M76 | v12 |
|
||||
| 7.0.0 | 2019-08-01 | 2019-10-22 | M78 | v12 |
|
||||
| 8.0.0 | 2019-10-24 | 2020-02-04 | M80 | v12 |
|
||||
| 9.0.0 | 2020-02-06 | 2020-04-28 | M82 | v12 |
|
||||
| 9.0.0 | 2020-02-06 | 2020-03-12 | M82 | v12 |
|
||||
| 10.0.0 | TBD | TBD | TBD | TBD |
|
||||
|
||||
@@ -1,29 +1,40 @@
|
||||
# Mojave Dark Mode
|
||||
# Supporting macOS Dark Mode
|
||||
|
||||
In macOS 10.14 Mojave, Apple introduced a new [system-wide dark mode](https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/)
|
||||
for all macOS computers. If your app does have a dark mode, you can make your Electron app
|
||||
follow the system-wide dark mode setting using [the nativeTheme api](../api/native-theme.md).
|
||||
for all macOS computers. If your Electron app has a dark mode, you can make it follow the
|
||||
system-wide dark mode setting using [the `nativeTheme` api](../api/native-theme.md).
|
||||
|
||||
In macOS 10.15 Catalina, Apple introduced a new "automatic" dark mode option for all macOS computers. In order
|
||||
for the `nativeTheme.shouldUseDarkColors` and `Tray` APIs to work correctly in this mode on Catalina you need to either have `NSRequiresAquaSystemAppearance` set to `false` in your `Info.plist` file or be on Electron `>=7.0.0`.
|
||||
In macOS 10.15 Catalina, Apple introduced a new "automatic" dark mode option for all macOS computers.
|
||||
In order for the `nativeTheme.shouldUseDarkColors` and `Tray` APIs to work correctly in this mode on
|
||||
Catalina, you need to either have `NSRequiresAquaSystemAppearance` set to `false` in your
|
||||
`Info.plist` file, or be on Electron `>=7.0.0`. Both [Electron Packager][electron-packager] and
|
||||
[Electron Forge][electron-forge] have a [`darwinDarkModeSupport` option][packager-darwindarkmode-api]
|
||||
to automate the `Info.plist` changes during app build time.
|
||||
|
||||
## Automatically updating the native interfaces
|
||||
|
||||
"Native Interfaces" include the file picker, window border, dialogs, context menus and more; basically anything where
|
||||
the UI comes from macOS and not your app. The default behavior as of Electron 7.0.0 is to opt in to this automatic
|
||||
theming from the OS. If you wish to opt out you must set the `NSRequiresAquaSystemAppearance` key in the `Info.plist` file
|
||||
to `true`. Please note that once Electron starts building against the 10.14 SDK it will not be possible for you to opt
|
||||
out of this theming.
|
||||
"Native Interfaces" include the file picker, window border, dialogs, context menus and more; basically,
|
||||
anything where the UI comes from macOS and not your app. As of Electron 7.0.0, the default behavior
|
||||
is to opt in to this automatic theming from the OS. If you wish to opt out and are using Electron
|
||||
> 8.0.0, you must set the `NSRequiresAquaSystemAppearance` key in the `Info.plist` file to `true`.
|
||||
Please note that Electron 8.0.0 and above will not let your opt out of this theming, due to the use
|
||||
of the macOS 10.14 SDK.
|
||||
|
||||
## Automatically updating your own interfaces
|
||||
|
||||
If your app has its own dark mode you should toggle it on and off in sync with the system's dark mode setting. You can do
|
||||
this by listening for the theme updated event on Electron's `nativeTheme` module. E.g.
|
||||
If your app has its own dark mode, you should toggle it on and off in sync with the system's dark
|
||||
mode setting. You can do this by listening for the theme updated event on Electron's `nativeTheme` module.
|
||||
|
||||
```js
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
const { nativeTheme } = require('electron')
|
||||
|
||||
nativeTheme.on('updated', function theThemeHasChanged () {
|
||||
updateMyAppTheme(nativeTheme.shouldUseDarkColors)
|
||||
})
|
||||
```
|
||||
|
||||
[electron-forge]: https://www.electronforge.io/
|
||||
[electron-packager]: https://github.com/electron/electron-packager
|
||||
[packager-darwindarkmode-api]: https://electron.github.io/electron-packager/master/interfaces/electronpackager.options.html#darwindarkmodesupport
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Testing on Headless CI Systems (Travis CI, Jenkins)
|
||||
# Testing on Headless CI Systems (Travis CI, Github Actions, Jenkins)
|
||||
|
||||
Being based on Chromium, Electron requires a display driver to function.
|
||||
If Chromium can't find a display driver, Electron will fail to launch -
|
||||
@@ -45,6 +45,10 @@ install:
|
||||
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
### Github Actions
|
||||
|
||||
For Github Actions, a [Xvfb action is available](https://github.com/marketplace/actions/gabrielbb-xvfb-action).
|
||||
|
||||
### Jenkins
|
||||
|
||||
For Jenkins, a [Xvfb plugin is available](https://wiki.jenkins-ci.org/display/JENKINS/Xvfb+Plugin).
|
||||
|
||||
@@ -21,7 +21,6 @@ auto_filenames = {
|
||||
"docs/api/dock.md",
|
||||
"docs/api/download-item.md",
|
||||
"docs/api/environment-variables.md",
|
||||
"docs/api/extensions.md",
|
||||
"docs/api/file-object.md",
|
||||
"docs/api/frameless-window.md",
|
||||
"docs/api/global-shortcut.md",
|
||||
|
||||
@@ -558,8 +558,11 @@ filenames = {
|
||||
"shell/common/skia_util.h",
|
||||
"shell/common/v8_value_converter.cc",
|
||||
"shell/common/v8_value_converter.h",
|
||||
"shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc",
|
||||
"shell/renderer/api/context_bridge/render_frame_context_bridge_store.h",
|
||||
"shell/common/world_ids.h",
|
||||
"shell/renderer/api/context_bridge/object_cache.cc",
|
||||
"shell/renderer/api/context_bridge/object_cache.h",
|
||||
"shell/renderer/api/context_bridge/render_frame_function_store.cc",
|
||||
"shell/renderer/api/context_bridge/render_frame_function_store.h",
|
||||
"shell/renderer/api/electron_api_context_bridge.cc",
|
||||
"shell/renderer/api/electron_api_context_bridge.h",
|
||||
"shell/renderer/api/electron_api_renderer_ipc.cc",
|
||||
@@ -598,6 +601,8 @@ filenames = {
|
||||
]
|
||||
|
||||
lib_sources_extensions = [
|
||||
"shell/browser/extensions/api/i18n/i18n_api.cc",
|
||||
"shell/browser/extensions/api/i18n/i18n_api.h",
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.cc",
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
@@ -616,6 +621,8 @@ filenames = {
|
||||
"shell/browser/extensions/electron_extension_host_delegate.h",
|
||||
"shell/browser/extensions/electron_extension_loader.cc",
|
||||
"shell/browser/extensions/electron_extension_loader.h",
|
||||
"shell/browser/extensions/electron_extension_message_filter.cc",
|
||||
"shell/browser/extensions/electron_extension_message_filter.h",
|
||||
"shell/browser/extensions/electron_extension_system.cc",
|
||||
"shell/browser/extensions/electron_extension_system.h",
|
||||
"shell/browser/extensions/electron_extension_system_factory.cc",
|
||||
|
||||
@@ -44,7 +44,7 @@ hunspell_dictionaries = [
|
||||
"//third_party/hunspell_dictionaries/ta-IN-3-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/tg-TG-5-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/tr-TR-4-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/uk-UA-3-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/uk-UA-4-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/vi-VN-3-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/xx-XX-3-0.bdic",
|
||||
]
|
||||
|
||||
@@ -1,21 +1,44 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { deprecate, Menu } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import { Menu } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const bindings = process.electronBinding('app')
|
||||
const commandLine = process.electronBinding('command_line')
|
||||
const { app, App } = bindings
|
||||
const bindings = process.electronBinding('app');
|
||||
const commandLine = process.electronBinding('command_line');
|
||||
const { app, App } = bindings;
|
||||
|
||||
// Only one app object permitted.
|
||||
export default app
|
||||
export default app;
|
||||
|
||||
let dockMenu: Electron.Menu | null = null
|
||||
let dockMenu: Electron.Menu | null = null;
|
||||
|
||||
// App is an EventEmitter.
|
||||
Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(app as any)
|
||||
Object.setPrototypeOf(App.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(app as any);
|
||||
|
||||
// Properties.
|
||||
|
||||
const nativeASGetter = app.isAccessibilitySupportEnabled;
|
||||
const nativeASSetter = app.setAccessibilitySupportEnabled;
|
||||
Object.defineProperty(App.prototype, 'accessibilitySupportEnabled', {
|
||||
get: () => nativeASGetter.call(app),
|
||||
set: (enabled) => nativeASSetter.call(app, enabled)
|
||||
});
|
||||
|
||||
const nativeBCGetter = app.getBadgeCount;
|
||||
const nativeBCSetter = app.setBadgeCount;
|
||||
Object.defineProperty(App.prototype, 'badgeCount', {
|
||||
get: () => nativeBCGetter.call(app),
|
||||
set: (count) => nativeBCSetter.call(app, count)
|
||||
});
|
||||
|
||||
const nativeNGetter = app.getName;
|
||||
const nativeNSetter = app.setName;
|
||||
Object.defineProperty(App.prototype, 'name', {
|
||||
get: () => nativeNGetter.call(app),
|
||||
set: (name) => nativeNSetter.call(app, name)
|
||||
});
|
||||
|
||||
Object.assign(app, {
|
||||
commandLine: {
|
||||
@@ -24,99 +47,94 @@ Object.assign(app, {
|
||||
appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)),
|
||||
appendArgument: (arg: string) => commandLine.appendArgument(String(arg))
|
||||
} as Electron.CommandLine
|
||||
})
|
||||
});
|
||||
|
||||
// we define this here because it'd be overly complicated to
|
||||
// do in native land
|
||||
Object.defineProperty(app, 'applicationMenu', {
|
||||
get () {
|
||||
return Menu.getApplicationMenu()
|
||||
return Menu.getApplicationMenu();
|
||||
},
|
||||
set (menu: Electron.Menu | null) {
|
||||
return Menu.setApplicationMenu(menu)
|
||||
return Menu.setApplicationMenu(menu);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
App.prototype.isPackaged = (() => {
|
||||
const execFile = path.basename(process.execPath).toLowerCase()
|
||||
const execFile = path.basename(process.execPath).toLowerCase();
|
||||
if (process.platform === 'win32') {
|
||||
return execFile !== 'electron.exe'
|
||||
return execFile !== 'electron.exe';
|
||||
}
|
||||
return execFile !== 'electron'
|
||||
})()
|
||||
return execFile !== 'electron';
|
||||
})();
|
||||
|
||||
app._setDefaultAppPaths = (packagePath) => {
|
||||
// Set the user path according to application's name.
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.name!))
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.name!))
|
||||
app.setAppPath(packagePath)
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.name!));
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.name!));
|
||||
app.setAppPath(packagePath);
|
||||
|
||||
// Add support for --user-data-dir=
|
||||
if (app.commandLine.hasSwitch('user-data-dir')) {
|
||||
const userDataDir = app.commandLine.getSwitchValue('user-data-dir')
|
||||
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir)
|
||||
const userDataDir = app.commandLine.getSwitchValue('user-data-dir');
|
||||
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const setDockMenu = app.dock!.setMenu
|
||||
const setDockMenu = app.dock!.setMenu;
|
||||
app.dock!.setMenu = (menu) => {
|
||||
dockMenu = menu
|
||||
setDockMenu(menu)
|
||||
}
|
||||
app.dock!.getMenu = () => dockMenu
|
||||
dockMenu = menu;
|
||||
setDockMenu(menu);
|
||||
};
|
||||
app.dock!.getMenu = () => dockMenu;
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m
|
||||
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m
|
||||
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m;
|
||||
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m;
|
||||
|
||||
const getStatus = (pid: number) => {
|
||||
try {
|
||||
return fs.readFileSync(`/proc/${pid}/status`, 'utf8')
|
||||
return fs.readFileSync(`/proc/${pid}/status`, 'utf8');
|
||||
} catch {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getEntry = (file: string, pattern: RegExp) => {
|
||||
const match = file.match(pattern)
|
||||
return match ? parseInt(match[1], 10) : 0
|
||||
}
|
||||
const match = file.match(pattern);
|
||||
return match ? parseInt(match[1], 10) : 0;
|
||||
};
|
||||
|
||||
const getProcessMemoryInfo = (pid: number) => {
|
||||
const file = getStatus(pid)
|
||||
const file = getStatus(pid);
|
||||
|
||||
return {
|
||||
workingSetSize: getEntry(file, patternVmRSS),
|
||||
peakWorkingSetSize: getEntry(file, patternVmHWM)
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const nativeFn = app.getAppMetrics
|
||||
const nativeFn = app.getAppMetrics;
|
||||
app.getAppMetrics = () => {
|
||||
const metrics = nativeFn.call(app)
|
||||
const metrics = nativeFn.call(app);
|
||||
for (const metric of metrics) {
|
||||
metric.memory = getProcessMemoryInfo(metric.pid)
|
||||
metric.memory = getProcessMemoryInfo(metric.pid);
|
||||
}
|
||||
|
||||
return metrics
|
||||
}
|
||||
return metrics;
|
||||
};
|
||||
}
|
||||
|
||||
// Routes the events to webContents.
|
||||
const events = ['certificate-error', 'select-client-certificate']
|
||||
const events = ['certificate-error', 'select-client-certificate'];
|
||||
for (const name of events) {
|
||||
app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => {
|
||||
webContents.emit(name, event, ...args)
|
||||
})
|
||||
webContents.emit(name, event, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
// Property Deprecations
|
||||
deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled')
|
||||
deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount')
|
||||
deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName')
|
||||
|
||||
// Wrappers for native classes.
|
||||
const { DownloadItem } = process.electronBinding('download_item')
|
||||
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype)
|
||||
const { DownloadItem } = process.electronBinding('download_item');
|
||||
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win')
|
||||
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')
|
||||
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater')
|
||||
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)
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(autoUpdater);
|
||||
|
||||
module.exports = autoUpdater
|
||||
module.exports = autoUpdater;
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win')
|
||||
const { app } = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win');
|
||||
|
||||
class AutoUpdater extends EventEmitter {
|
||||
quitAndInstall () {
|
||||
if (!this.updateAvailable) {
|
||||
return this.emitError('No update available, can\'t quit and install')
|
||||
return this.emitError('No update available, can\'t quit and install');
|
||||
}
|
||||
squirrelUpdate.processStart()
|
||||
app.quit()
|
||||
squirrelUpdate.processStart();
|
||||
app.quit();
|
||||
}
|
||||
|
||||
getFeedURL () {
|
||||
return this.updateURL
|
||||
return this.updateURL;
|
||||
}
|
||||
|
||||
setFeedURL (options) {
|
||||
let updateURL
|
||||
let updateURL;
|
||||
if (typeof options === 'object') {
|
||||
if (typeof options.url === 'string') {
|
||||
updateURL = options.url
|
||||
updateURL = options.url;
|
||||
} else {
|
||||
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call')
|
||||
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call');
|
||||
}
|
||||
} else if (typeof options === 'string') {
|
||||
updateURL = options
|
||||
updateURL = options;
|
||||
} else {
|
||||
throw new Error('Expected an options object with a \'url\' property to be provided')
|
||||
throw new Error('Expected an options object with a \'url\' property to be provided');
|
||||
}
|
||||
this.updateURL = updateURL
|
||||
this.updateURL = updateURL;
|
||||
}
|
||||
|
||||
checkForUpdates () {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set')
|
||||
return this.emitError('Update URL is not set');
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel')
|
||||
return this.emitError('Can not find Squirrel');
|
||||
}
|
||||
this.emit('checking-for-update')
|
||||
this.emit('checking-for-update');
|
||||
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
return this.emitError(error);
|
||||
}
|
||||
if (update == null) {
|
||||
return this.emit('update-not-available')
|
||||
return this.emit('update-not-available');
|
||||
}
|
||||
this.updateAvailable = true
|
||||
this.emit('update-available')
|
||||
this.updateAvailable = true;
|
||||
this.emit('update-available');
|
||||
squirrelUpdate.update(this.updateURL, (error) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error)
|
||||
return this.emitError(error);
|
||||
}
|
||||
const { releaseNotes, version } = update
|
||||
const { releaseNotes, version } = update;
|
||||
// Date is not available on Windows, so fake it.
|
||||
const date = new Date()
|
||||
const date = new Date();
|
||||
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
|
||||
this.quitAndInstall()
|
||||
})
|
||||
})
|
||||
})
|
||||
this.quitAndInstall();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
emitError (message) {
|
||||
this.emit('error', new Error(message), message)
|
||||
this.emit('error', new Error(message), message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new AutoUpdater()
|
||||
module.exports = new AutoUpdater();
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const spawn = require('child_process').spawn
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
// i.e. my-app/app-0.1.13/
|
||||
const appFolder = path.dirname(process.execPath)
|
||||
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
|
||||
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
|
||||
const exeName = path.basename(process.execPath);
|
||||
let spawnedArgs = [];
|
||||
let spawnedProcess;
|
||||
|
||||
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i])
|
||||
const isSameArgs = (args) => 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
|
||||
let error, errorEmitted, stderr, stdout;
|
||||
|
||||
try {
|
||||
// Ensure we don't spawn multiple squirrel processes
|
||||
@@ -28,92 +28,92 @@ const spawnUpdate = function (args, detached, callback) {
|
||||
if (spawnedProcess && !isSameArgs(args)) {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`AutoUpdater process with arguments ${args} is already running`)
|
||||
return callback(`AutoUpdater process with arguments ${args} is already running`);
|
||||
} else if (!spawnedProcess) {
|
||||
spawnedProcess = spawn(updateExe, args, {
|
||||
detached: detached,
|
||||
windowsHide: true
|
||||
})
|
||||
spawnedArgs = args || []
|
||||
});
|
||||
spawnedArgs = args || [];
|
||||
}
|
||||
} catch (error1) {
|
||||
error = error1
|
||||
error = error1;
|
||||
|
||||
// Shouldn't happen, but still guard it.
|
||||
process.nextTick(function () {
|
||||
return callback(error)
|
||||
})
|
||||
return
|
||||
return callback(error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
stdout = '';
|
||||
stderr = '';
|
||||
|
||||
spawnedProcess.stdout.on('data', (data) => { stdout += data })
|
||||
spawnedProcess.stderr.on('data', (data) => { stderr += data })
|
||||
spawnedProcess.stdout.on('data', (data) => { stdout += data; });
|
||||
spawnedProcess.stderr.on('data', (data) => { stderr += data; });
|
||||
|
||||
errorEmitted = false
|
||||
errorEmitted = false;
|
||||
spawnedProcess.on('error', (error) => {
|
||||
errorEmitted = true
|
||||
callback(error)
|
||||
})
|
||||
errorEmitted = true;
|
||||
callback(error);
|
||||
});
|
||||
|
||||
return spawnedProcess.on('exit', function (code, signal) {
|
||||
spawnedProcess = undefined
|
||||
spawnedArgs = []
|
||||
spawnedProcess = undefined;
|
||||
spawnedArgs = [];
|
||||
|
||||
// We may have already emitted an error.
|
||||
if (errorEmitted) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Process terminated with error.
|
||||
if (code !== 0) {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`)
|
||||
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`);
|
||||
}
|
||||
|
||||
// Success.
|
||||
callback(null, stdout)
|
||||
})
|
||||
}
|
||||
callback(null, stdout);
|
||||
});
|
||||
};
|
||||
|
||||
// Start an instance of the installed app.
|
||||
exports.processStart = function () {
|
||||
return spawnUpdate(['--processStartAndWait', exeName], true, function () {})
|
||||
}
|
||||
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) {
|
||||
let ref, ref1, update
|
||||
let ref, ref1, update;
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
return callback(error);
|
||||
}
|
||||
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() : void 0 : void 0 : void 0
|
||||
const json = stdout.trim().split('\n').pop();
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0;
|
||||
} catch {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(`Invalid result:\n${stdout}`)
|
||||
return callback(`Invalid result:\n${stdout}`);
|
||||
}
|
||||
return callback(null, update)
|
||||
})
|
||||
}
|
||||
return callback(null, update);
|
||||
});
|
||||
};
|
||||
|
||||
// Update the application to the latest remote version specified by URL.
|
||||
exports.update = function (updateURL, callback) {
|
||||
return spawnUpdate(['--update', updateURL], false, callback)
|
||||
}
|
||||
return spawnUpdate(['--update', updateURL], false, callback);
|
||||
};
|
||||
|
||||
// Is the Update.exe installed with the current application?
|
||||
exports.supported = function () {
|
||||
try {
|
||||
fs.accessSync(updateExe, fs.R_OK)
|
||||
return true
|
||||
fs.accessSync(updateExe, fs.R_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { BrowserView } = process.electronBinding('browser_view')
|
||||
const { EventEmitter } = require('events');
|
||||
const { BrowserView } = process.electronBinding('browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = BrowserView
|
||||
module.exports = BrowserView;
|
||||
|
||||
@@ -1,29 +1,49 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const { WebContentsView, TopLevelWindow, deprecate } = electron
|
||||
const { BrowserWindow } = process.electronBinding('window')
|
||||
const electron = require('electron');
|
||||
const { WebContentsView, TopLevelWindow, deprecate } = electron;
|
||||
const { BrowserWindow } = process.electronBinding('window');
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
|
||||
|
||||
BrowserWindow.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
TopLevelWindow.prototype._init.call(this)
|
||||
TopLevelWindow.prototype._init.call(this);
|
||||
|
||||
// Avoid recursive require.
|
||||
const { app } = electron
|
||||
const { app } = electron;
|
||||
|
||||
// Create WebContentsView.
|
||||
this.setContentView(new WebContentsView(this.webContents))
|
||||
this.setContentView(new WebContentsView(this.webContents));
|
||||
|
||||
const nativeSetBounds = this.setBounds
|
||||
const nativeSetBounds = this.setBounds;
|
||||
this.setBounds = (bounds, ...opts) => {
|
||||
bounds = {
|
||||
...this.getBounds(),
|
||||
...bounds
|
||||
};
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// window.resizeTo(...)
|
||||
// window.moveTo(...)
|
||||
this.webContents.on('move', (event, size) => {
|
||||
this.setBounds(size);
|
||||
});
|
||||
|
||||
// Hide the auto-hide menu when webContents is focused.
|
||||
this.webContents.on('activate', () => {
|
||||
if (process.platform !== 'darwin' && this.autoHideMenuBar && this.isMenuBarVisible()) {
|
||||
this.setMenuBarVisibility(false);
|
||||
}
|
||||
nativeSetBounds.call(this, bounds, ...opts)
|
||||
}
|
||||
});
|
||||
|
||||
// Change window title to page title.
|
||||
this.webContents.on('page-title-updated', (event, title, ...args) => {
|
||||
// Route the event to BrowserWindow.
|
||||
this.emit('page-title-updated', event, title, ...args);
|
||||
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title);
|
||||
});
|
||||
|
||||
// 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
|
||||
@@ -34,144 +54,172 @@ BrowserWindow.prototype._init = function () {
|
||||
// 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()
|
||||
})
|
||||
this.focus();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event) => {
|
||||
app.emit('browser-window-blur', event, this)
|
||||
})
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event) => {
|
||||
app.emit('browser-window-focus', event, this)
|
||||
})
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized()
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized()
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden'
|
||||
this.webContents.emit('-window-visibility-change', visibilityState)
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event, visibilityChanged)
|
||||
this.on(event, visibilityChanged);
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process.electronBinding('event').createEmpty()
|
||||
app.emit('browser-window-created', event, this)
|
||||
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
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// Properties
|
||||
|
||||
Object.defineProperty(this, 'autoHideMenuBar', {
|
||||
get: () => this.isMenuBarAutoHide(),
|
||||
set: (autoHide) => this.setAutoHideMenuBar(autoHide)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'minimizable', {
|
||||
get: () => this.isMinimizable(),
|
||||
set: (min) => this.setMinimizable(min)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'maximizable', {
|
||||
get: () => this.isMaximizable(),
|
||||
set: (max) => this.setMaximizable(max)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'resizable', {
|
||||
get: () => this.isResizable(),
|
||||
set: (res) => this.setResizable(res)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'fullScreenable', {
|
||||
get: () => this.isFullScreenable(),
|
||||
set: (full) => this.setFullScreenable(full)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'closable', {
|
||||
get: () => this.isClosable(),
|
||||
set: (close) => this.setClosable(close)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'movable', {
|
||||
get: () => this.isMovable(),
|
||||
set: (move) => this.setMovable(move)
|
||||
});
|
||||
};
|
||||
|
||||
const isBrowserWindow = (win) => {
|
||||
return win && win.constructor.name === 'BrowserWindow'
|
||||
}
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
|
||||
BrowserWindow.fromId = (id) => {
|
||||
const win = TopLevelWindow.fromId(id)
|
||||
return isBrowserWindow(win) ? win : null
|
||||
}
|
||||
const win = TopLevelWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win : null;
|
||||
};
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow)
|
||||
}
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Helpers.
|
||||
Object.assign(BrowserWindow.prototype, {
|
||||
loadURL (...args) {
|
||||
return this.webContents.loadURL(...args)
|
||||
return this.webContents.loadURL(...args);
|
||||
},
|
||||
getURL (...args) {
|
||||
return this.webContents.getURL()
|
||||
return this.webContents.getURL();
|
||||
},
|
||||
loadFile (...args) {
|
||||
return this.webContents.loadFile(...args)
|
||||
return this.webContents.loadFile(...args);
|
||||
},
|
||||
reload (...args) {
|
||||
return this.webContents.reload(...args)
|
||||
return this.webContents.reload(...args);
|
||||
},
|
||||
send (...args) {
|
||||
return this.webContents.send(...args)
|
||||
return this.webContents.send(...args);
|
||||
},
|
||||
openDevTools (...args) {
|
||||
return this.webContents.openDevTools(...args)
|
||||
return this.webContents.openDevTools(...args);
|
||||
},
|
||||
closeDevTools () {
|
||||
return this.webContents.closeDevTools()
|
||||
return this.webContents.closeDevTools();
|
||||
},
|
||||
isDevToolsOpened () {
|
||||
return this.webContents.isDevToolsOpened()
|
||||
return this.webContents.isDevToolsOpened();
|
||||
},
|
||||
isDevToolsFocused () {
|
||||
return this.webContents.isDevToolsFocused()
|
||||
return this.webContents.isDevToolsFocused();
|
||||
},
|
||||
toggleDevTools () {
|
||||
return this.webContents.toggleDevTools()
|
||||
return this.webContents.toggleDevTools();
|
||||
},
|
||||
inspectElement (...args) {
|
||||
return this.webContents.inspectElement(...args)
|
||||
return this.webContents.inspectElement(...args);
|
||||
},
|
||||
inspectSharedWorker () {
|
||||
return this.webContents.inspectSharedWorker()
|
||||
return this.webContents.inspectSharedWorker();
|
||||
},
|
||||
inspectServiceWorker () {
|
||||
return this.webContents.inspectServiceWorker()
|
||||
return this.webContents.inspectServiceWorker();
|
||||
},
|
||||
showDefinitionForSelection () {
|
||||
return this.webContents.showDefinitionForSelection()
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
},
|
||||
capturePage (...args) {
|
||||
return this.webContents.capturePage(...args)
|
||||
return this.webContents.capturePage(...args);
|
||||
},
|
||||
setTouchBar (touchBar) {
|
||||
electron.TouchBar._setOnWindow(touchBar, this)
|
||||
electron.TouchBar._setOnWindow(touchBar, this);
|
||||
},
|
||||
setBackgroundThrottling (allowed) {
|
||||
this.webContents.setBackgroundThrottling(allowed)
|
||||
this.webContents.setBackgroundThrottling(allowed);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Deprecations
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable')
|
||||
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable')
|
||||
|
||||
module.exports = BrowserWindow
|
||||
module.exports = BrowserWindow;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
'use strict'
|
||||
module.exports = process.electronBinding('content_tracing')
|
||||
'use strict';
|
||||
module.exports = process.electronBinding('content_tracing');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter')
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter');
|
||||
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
|
||||
|
||||
class CrashReporterMain extends CrashReporter {
|
||||
init (options) {
|
||||
return crashReporterInit(options)
|
||||
return crashReporterInit(options);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new CrashReporterMain()
|
||||
module.exports = new CrashReporterMain();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { app, BrowserWindow, deprecate } = require('electron')
|
||||
const binding = process.electronBinding('dialog')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
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,
|
||||
@@ -15,7 +15,7 @@ const saveFileDialogProperties = {
|
||||
treatPackageAsDirectory: 1 << 2,
|
||||
showOverwriteConfirmation: 1 << 3,
|
||||
dontAddToRecent: 1 << 4
|
||||
}
|
||||
};
|
||||
|
||||
const openFileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
@@ -27,15 +27,15 @@ const openFileDialogProperties = {
|
||||
noResolveAliases: 1 << 6, // macOS
|
||||
treatPackageAsDirectory: 1 << 7, // macOS
|
||||
dontAddToRecent: 1 << 8 // Windows
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeAccessKey = (text) => {
|
||||
if (typeof text !== 'string') return 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')
|
||||
return text.replace(/&(&?)/g, '$1');
|
||||
}
|
||||
|
||||
// Linux uses a single underscore as an access key prefix so escape
|
||||
@@ -44,41 +44,41 @@ const normalizeAccessKey = (text) => {
|
||||
// a single underscore
|
||||
if (process.platform === 'linux') {
|
||||
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
|
||||
if (after === '&') return after
|
||||
return `_${after}`
|
||||
})
|
||||
if (after === '&') return after;
|
||||
return `_${after}`;
|
||||
});
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
const checkAppInitialized = function () {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready')
|
||||
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
|
||||
const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties;
|
||||
let dialogProperties = 0;
|
||||
for (const prop in dialogPropertiesTypes) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= dialogPropertiesTypes[prop]
|
||||
dialogProperties |= dialogPropertiesTypes[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return dialogProperties
|
||||
}
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const saveDialog = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window
|
||||
window = null
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { title: 'Save' }
|
||||
if (options == null) options = { title: 'Save' };
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
@@ -90,33 +90,33 @@ const saveDialog = (sync, window, options) => {
|
||||
securityScopedBookmarks = false,
|
||||
nameFieldLabel = '',
|
||||
showsTagField = true
|
||||
} = options
|
||||
} = 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')
|
||||
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)
|
||||
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)
|
||||
}
|
||||
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
|
||||
};
|
||||
|
||||
const openDialog = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window
|
||||
window = null
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -127,33 +127,33 @@ const openDialog = (sync, window, options) => {
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options
|
||||
} = options;
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array')
|
||||
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')
|
||||
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)
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
|
||||
settings.properties = setupDialogProperties(DialogType.OPEN, properties);
|
||||
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings)
|
||||
}
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
|
||||
};
|
||||
|
||||
const messageBox = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window
|
||||
window = null
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { type: 'none' }
|
||||
if (options == null) options = { type: 'none' };
|
||||
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
|
||||
const messageBoxOptions = { noLink: 1 << 0 }
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
const messageBoxOptions = { noLink: 1 << 0 };
|
||||
|
||||
let {
|
||||
buttons = [],
|
||||
@@ -167,32 +167,32 @@ const messageBox = (sync, window, options) => {
|
||||
message = '',
|
||||
title = '',
|
||||
type = 'none'
|
||||
} = options
|
||||
} = 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')
|
||||
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
|
||||
checkboxChecked = !!checkboxChecked;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
throw new Error('checkboxChecked requires that checkboxLabel also be passed')
|
||||
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
|
||||
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = buttons[i].toLowerCase()
|
||||
const text = buttons[i].toLowerCase();
|
||||
if (text === 'cancel' || text === 'no') {
|
||||
cancelId = i
|
||||
break
|
||||
cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,57 +210,57 @@ const messageBox = (sync, window, options) => {
|
||||
checkboxLabel,
|
||||
checkboxChecked,
|
||||
icon
|
||||
}
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
return binding.showMessageBoxSync(settings)
|
||||
return binding.showMessageBoxSync(settings);
|
||||
} else {
|
||||
return binding.showMessageBox(settings)
|
||||
return binding.showMessageBox(settings);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function (window, options) {
|
||||
return openDialog(false, window, options)
|
||||
return openDialog(false, window, options);
|
||||
},
|
||||
|
||||
showOpenDialogSync: function (window, options) {
|
||||
return openDialog(true, window, options)
|
||||
return openDialog(true, window, options);
|
||||
},
|
||||
|
||||
showSaveDialog: function (window, options) {
|
||||
return saveDialog(false, window, options)
|
||||
return saveDialog(false, window, options);
|
||||
},
|
||||
|
||||
showSaveDialogSync: function (window, options) {
|
||||
return saveDialog(true, window, options)
|
||||
return saveDialog(true, window, options);
|
||||
},
|
||||
|
||||
showMessageBox: function (window, options) {
|
||||
return messageBox(false, window, options)
|
||||
return messageBox(false, window, options);
|
||||
},
|
||||
|
||||
showMessageBoxSync: function (window, options) {
|
||||
return messageBox(true, window, options)
|
||||
return messageBox(true, window, options);
|
||||
},
|
||||
|
||||
showErrorBox: function (...args) {
|
||||
return binding.showErrorBox(...args)
|
||||
return binding.showErrorBox(...args);
|
||||
},
|
||||
|
||||
showCertificateTrustDialog: function (window, options) {
|
||||
if (window && window.constructor !== BrowserWindow) options = window
|
||||
if (window && window.constructor !== BrowserWindow) options = window;
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object')
|
||||
throw new TypeError('options must be an object');
|
||||
}
|
||||
|
||||
const { certificate, message = '' } = options
|
||||
const { certificate, message = '' } = options;
|
||||
if (certificate == null || typeof certificate !== 'object') {
|
||||
throw new TypeError('certificate must be an object')
|
||||
throw new TypeError('certificate must be an object');
|
||||
}
|
||||
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string')
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string');
|
||||
|
||||
return binding.showCertificateTrustDialog(window, certificate, message)
|
||||
return binding.showCertificateTrustDialog(window, certificate, message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { defineProperties } from '@electron/internal/common/define-properties'
|
||||
import { commonModuleList } from '@electron/internal/common/api/module-list'
|
||||
import { browserModuleList } from '@electron/internal/browser/api/module-list'
|
||||
import { defineProperties } from '@electron/internal/common/define-properties';
|
||||
import { commonModuleList } from '@electron/internal/common/api/module-list';
|
||||
import { browserModuleList } from '@electron/internal/browser/api/module-list';
|
||||
|
||||
module.exports = {}
|
||||
module.exports = {};
|
||||
|
||||
defineProperties(module.exports, commonModuleList)
|
||||
defineProperties(module.exports, browserModuleList)
|
||||
defineProperties(module.exports, commonModuleList);
|
||||
defineProperties(module.exports, browserModuleList);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('global_shortcut').globalShortcut
|
||||
module.exports = process.electronBinding('global_shortcut').globalShortcut;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { deprecate } = require('electron')
|
||||
const { deprecate } = require('electron');
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const { EventEmitter } = require('events')
|
||||
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase')
|
||||
const { EventEmitter } = require('events');
|
||||
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase');
|
||||
|
||||
// inAppPurchase is an EventEmitter.
|
||||
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(inAppPurchase)
|
||||
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(inAppPurchase);
|
||||
|
||||
module.exports = inAppPurchase
|
||||
module.exports = inAppPurchase;
|
||||
} else {
|
||||
module.exports = {
|
||||
purchaseProduct: (productID, quantity, callback) => {
|
||||
throw new Error('The inAppPurchase module can only be used on macOS')
|
||||
throw new Error('The inAppPurchase module can only be used on macOS');
|
||||
},
|
||||
canMakePayments: () => false,
|
||||
getReceiptURL: () => ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'
|
||||
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl';
|
||||
|
||||
const ipcMain = new IpcMainImpl()
|
||||
const ipcMain = new IpcMainImpl();
|
||||
|
||||
// Do not throw exception when channel name is "error".
|
||||
ipcMain.on('error', () => {})
|
||||
ipcMain.on('error', () => {});
|
||||
|
||||
export default ipcMain
|
||||
export default ipcMain;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron')
|
||||
const { app } = require('electron');
|
||||
|
||||
const isMac = process.platform === 'darwin'
|
||||
const isWindows = process.platform === 'win32'
|
||||
const isLinux = process.platform === 'linux'
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
const roles = {
|
||||
about: {
|
||||
get label () {
|
||||
return isLinux ? 'About' : `About ${app.name}`
|
||||
return isLinux ? 'About' : `About ${app.name}`;
|
||||
}
|
||||
},
|
||||
close: {
|
||||
@@ -38,7 +38,7 @@ const roles = {
|
||||
accelerator: 'Shift+CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: (window) => {
|
||||
window.webContents.reloadIgnoringCache()
|
||||
window.webContents.reloadIgnoringCache();
|
||||
}
|
||||
},
|
||||
front: {
|
||||
@@ -49,7 +49,7 @@ const roles = {
|
||||
},
|
||||
hide: {
|
||||
get label () {
|
||||
return `Hide ${app.name}`
|
||||
return `Hide ${app.name}`;
|
||||
},
|
||||
accelerator: 'Command+H'
|
||||
},
|
||||
@@ -77,9 +77,9 @@ const roles = {
|
||||
quit: {
|
||||
get label () {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return `Quit ${app.name}`
|
||||
case 'win32': return 'Exit'
|
||||
default: return 'Quit'
|
||||
case 'darwin': return `Quit ${app.name}`;
|
||||
case 'win32': return 'Exit';
|
||||
default: return 'Quit';
|
||||
}
|
||||
},
|
||||
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
|
||||
@@ -101,7 +101,7 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+0',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel = 0
|
||||
webContents.zoomLevel = 0;
|
||||
}
|
||||
},
|
||||
selectall: {
|
||||
@@ -134,7 +134,7 @@ const roles = {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: isMac ? 'Control+Command+F' : 'F11',
|
||||
windowMethod: (window) => {
|
||||
window.setFullScreen(!window.isFullScreen())
|
||||
window.setFullScreen(!window.isFullScreen());
|
||||
}
|
||||
},
|
||||
undo: {
|
||||
@@ -156,7 +156,7 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+Plus',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel += 0.5
|
||||
webContents.zoomLevel += 0.5;
|
||||
}
|
||||
},
|
||||
zoomout: {
|
||||
@@ -164,13 +164,13 @@ const roles = {
|
||||
accelerator: 'CommandOrControl+-',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel -= 0.5
|
||||
webContents.zoomLevel -= 0.5;
|
||||
}
|
||||
},
|
||||
// App submenu should be used for Mac only
|
||||
appmenu: {
|
||||
get label () {
|
||||
return app.name
|
||||
return app.name;
|
||||
},
|
||||
submenu: [
|
||||
{ role: 'about' },
|
||||
@@ -249,71 +249,71 @@ const roles = {
|
||||
])
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.roleList = roles
|
||||
exports.roleList = roles;
|
||||
|
||||
const canExecuteRole = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return false
|
||||
if (!isMac) return true
|
||||
if (!roles.hasOwnProperty(role)) return false;
|
||||
if (!isMac) return true;
|
||||
|
||||
// macOS handles all roles natively except for a few
|
||||
return roles[role].nonNativeMacOSRole
|
||||
}
|
||||
return roles[role].nonNativeMacOSRole;
|
||||
};
|
||||
|
||||
exports.getDefaultLabel = (role) => {
|
||||
return roles.hasOwnProperty(role) ? roles[role].label : ''
|
||||
}
|
||||
return roles.hasOwnProperty(role) ? roles[role].label : '';
|
||||
};
|
||||
|
||||
exports.getDefaultAccelerator = (role) => {
|
||||
if (roles.hasOwnProperty(role)) return roles[role].accelerator
|
||||
}
|
||||
if (roles.hasOwnProperty(role)) return roles[role].accelerator;
|
||||
};
|
||||
|
||||
exports.shouldRegisterAccelerator = (role) => {
|
||||
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true
|
||||
}
|
||||
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true;
|
||||
};
|
||||
|
||||
exports.getDefaultSubmenu = (role) => {
|
||||
if (!roles.hasOwnProperty(role)) return
|
||||
if (!roles.hasOwnProperty(role)) return;
|
||||
|
||||
let { submenu } = roles[role]
|
||||
let { submenu } = roles[role];
|
||||
|
||||
// remove null items from within the submenu
|
||||
if (Array.isArray(submenu)) {
|
||||
submenu = submenu.filter((item) => item != null)
|
||||
submenu = submenu.filter((item) => item != null);
|
||||
}
|
||||
|
||||
return submenu
|
||||
}
|
||||
return submenu;
|
||||
};
|
||||
|
||||
exports.execute = (role, focusedWindow, focusedWebContents) => {
|
||||
if (!canExecuteRole(role)) return false
|
||||
if (!canExecuteRole(role)) return false;
|
||||
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role]
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role];
|
||||
|
||||
if (appMethod) {
|
||||
app[appMethod]()
|
||||
return true
|
||||
app[appMethod]();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (windowMethod && focusedWindow != null) {
|
||||
if (typeof windowMethod === 'function') {
|
||||
windowMethod(focusedWindow)
|
||||
windowMethod(focusedWindow);
|
||||
} else {
|
||||
focusedWindow[windowMethod]()
|
||||
focusedWindow[windowMethod]();
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
if (webContentsMethod && focusedWebContents != null) {
|
||||
if (typeof webContentsMethod === 'function') {
|
||||
webContentsMethod(focusedWebContents)
|
||||
webContentsMethod(focusedWebContents);
|
||||
} else {
|
||||
focusedWebContents[webContentsMethod]()
|
||||
focusedWebContents[webContentsMethod]();
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const roles = require('@electron/internal/browser/api/menu-item-roles')
|
||||
const roles = require('@electron/internal/browser/api/menu-item-roles');
|
||||
|
||||
let nextCommandId = 0
|
||||
let nextCommandId = 0;
|
||||
|
||||
const MenuItem = function (options) {
|
||||
const { Menu } = require('electron')
|
||||
const { Menu } = require('electron');
|
||||
|
||||
// Preserve extra fields specified by user
|
||||
for (const key in options) {
|
||||
if (!(key in this)) this[key] = options[key]
|
||||
if (!(key in this)) this[key] = options[key];
|
||||
}
|
||||
if (typeof this.role === 'string' || this.role instanceof String) {
|
||||
this.role = this.role.toLowerCase()
|
||||
this.role = this.role.toLowerCase();
|
||||
}
|
||||
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role)
|
||||
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role);
|
||||
if (this.submenu != null && this.submenu.constructor !== Menu) {
|
||||
this.submenu = Menu.buildFromTemplate(this.submenu)
|
||||
this.submenu = Menu.buildFromTemplate(this.submenu);
|
||||
}
|
||||
if (this.type == null && this.submenu != null) {
|
||||
this.type = 'submenu'
|
||||
this.type = 'submenu';
|
||||
}
|
||||
if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
|
||||
throw new Error('Invalid submenu')
|
||||
throw new Error('Invalid submenu');
|
||||
}
|
||||
|
||||
this.overrideReadOnlyProperty('type', 'normal')
|
||||
this.overrideReadOnlyProperty('role')
|
||||
this.overrideReadOnlyProperty('accelerator')
|
||||
this.overrideReadOnlyProperty('icon')
|
||||
this.overrideReadOnlyProperty('submenu')
|
||||
this.overrideReadOnlyProperty('type', 'normal');
|
||||
this.overrideReadOnlyProperty('role');
|
||||
this.overrideReadOnlyProperty('accelerator');
|
||||
this.overrideReadOnlyProperty('icon');
|
||||
this.overrideReadOnlyProperty('submenu');
|
||||
|
||||
this.overrideProperty('label', roles.getDefaultLabel(this.role))
|
||||
this.overrideProperty('sublabel', '')
|
||||
this.overrideProperty('toolTip', '')
|
||||
this.overrideProperty('enabled', true)
|
||||
this.overrideProperty('visible', true)
|
||||
this.overrideProperty('checked', false)
|
||||
this.overrideProperty('acceleratorWorksWhenHidden', true)
|
||||
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role))
|
||||
this.overrideProperty('label', roles.getDefaultLabel(this.role));
|
||||
this.overrideProperty('sublabel', '');
|
||||
this.overrideProperty('toolTip', '');
|
||||
this.overrideProperty('enabled', true);
|
||||
this.overrideProperty('visible', true);
|
||||
this.overrideProperty('checked', false);
|
||||
this.overrideProperty('acceleratorWorksWhenHidden', true);
|
||||
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role));
|
||||
|
||||
if (!MenuItem.types.includes(this.type)) {
|
||||
throw new Error(`Unknown menu item type: ${this.type}`)
|
||||
throw new Error(`Unknown menu item type: ${this.type}`);
|
||||
}
|
||||
|
||||
this.overrideReadOnlyProperty('commandId', ++nextCommandId)
|
||||
this.overrideReadOnlyProperty('commandId', ++nextCommandId);
|
||||
|
||||
const click = options.click
|
||||
const click = options.click;
|
||||
this.click = (event, focusedWindow, focusedWebContents) => {
|
||||
// Manually flip the checked flags when clicked.
|
||||
if (this.type === 'checkbox' || this.type === 'radio') {
|
||||
this.checked = !this.checked
|
||||
this.checked = !this.checked;
|
||||
}
|
||||
|
||||
if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
|
||||
if (typeof click === 'function') {
|
||||
click(this, focusedWindow, event)
|
||||
click(this, focusedWindow, event);
|
||||
} else if (typeof this.selector === 'string' && process.platform === 'darwin') {
|
||||
Menu.sendActionToFirstResponder(this.selector)
|
||||
Menu.sendActionToFirstResponder(this.selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
|
||||
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
|
||||
|
||||
MenuItem.prototype.getDefaultRoleAccelerator = function () {
|
||||
return roles.getDefaultAccelerator(this.role)
|
||||
}
|
||||
return roles.getDefaultAccelerator(this.role);
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
|
||||
if (this[name] == null) {
|
||||
this[name] = defaultValue
|
||||
this[name] = defaultValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
|
||||
this.overrideProperty(name, defaultValue)
|
||||
this.overrideProperty(name, defaultValue);
|
||||
Object.defineProperty(this, name, {
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
value: this[name]
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = MenuItem
|
||||
module.exports = MenuItem;
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
function splitArray (arr, predicate) {
|
||||
const result = arr.reduce((multi, item) => {
|
||||
const current = multi[multi.length - 1]
|
||||
const current = multi[multi.length - 1];
|
||||
if (predicate(item)) {
|
||||
if (current.length > 0) multi.push([])
|
||||
if (current.length > 0) multi.push([]);
|
||||
} else {
|
||||
current.push(item)
|
||||
current.push(item);
|
||||
}
|
||||
return multi
|
||||
}, [[]])
|
||||
return multi;
|
||||
}, [[]]);
|
||||
|
||||
if (result[result.length - 1].length === 0) {
|
||||
return result.slice(0, result.length - 1)
|
||||
return result.slice(0, result.length - 1);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
function joinArrays (arrays, joinIDs) {
|
||||
return arrays.reduce((joined, arr, i) => {
|
||||
if (i > 0 && arr.length) {
|
||||
if (joinIDs.length > 0) {
|
||||
joined.push(joinIDs[0])
|
||||
joinIDs.splice(0, 1)
|
||||
joined.push(joinIDs[0]);
|
||||
joinIDs.splice(0, 1);
|
||||
} else {
|
||||
joined.push({ type: 'separator' })
|
||||
joined.push({ type: 'separator' });
|
||||
}
|
||||
}
|
||||
return joined.concat(arr)
|
||||
}, [])
|
||||
return joined.concat(arr);
|
||||
}, []);
|
||||
}
|
||||
|
||||
function pushOntoMultiMap (map, key, value) {
|
||||
if (!map.has(key)) {
|
||||
map.set(key, [])
|
||||
map.set(key, []);
|
||||
}
|
||||
map.get(key).push(value)
|
||||
map.get(key).push(value);
|
||||
}
|
||||
|
||||
function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
@@ -45,102 +45,102 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
candidateGroup.some(
|
||||
candidateItem => candidateItem.id === id
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Sort nodes topologically using a depth-first approach. Encountered cycles
|
||||
// are broken.
|
||||
function sortTopologically (originalOrder, edgesById) {
|
||||
const sorted = []
|
||||
const marked = new Set()
|
||||
const sorted = [];
|
||||
const marked = new Set();
|
||||
|
||||
const visit = (mark) => {
|
||||
if (marked.has(mark)) return
|
||||
marked.add(mark)
|
||||
const edges = edgesById.get(mark)
|
||||
if (marked.has(mark)) return;
|
||||
marked.add(mark);
|
||||
const edges = edgesById.get(mark);
|
||||
if (edges != null) {
|
||||
edges.forEach(visit)
|
||||
edges.forEach(visit);
|
||||
}
|
||||
sorted.push(mark)
|
||||
}
|
||||
sorted.push(mark);
|
||||
};
|
||||
|
||||
originalOrder.forEach(visit)
|
||||
return sorted
|
||||
originalOrder.forEach(visit);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
function attemptToMergeAGroup (groups) {
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i]
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
const toIDs = [...(item.before || []), ...(item.after || [])]
|
||||
const toIDs = [...(item.before || []), ...(item.after || [])];
|
||||
for (const id of toIDs) {
|
||||
const index = indexOfGroupContainingID(groups, id, group)
|
||||
if (index === -1) continue
|
||||
const mergeTarget = groups[index]
|
||||
const index = indexOfGroupContainingID(groups, id, group);
|
||||
if (index === -1) continue;
|
||||
const mergeTarget = groups[index];
|
||||
|
||||
mergeTarget.push(...group)
|
||||
groups.splice(i, 1)
|
||||
return true
|
||||
mergeTarget.push(...group);
|
||||
groups.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
function mergeGroups (groups) {
|
||||
let merged = true
|
||||
let merged = true;
|
||||
while (merged) {
|
||||
merged = attemptToMergeAGroup(groups)
|
||||
merged = attemptToMergeAGroup(groups);
|
||||
}
|
||||
return groups
|
||||
return groups;
|
||||
}
|
||||
|
||||
function sortItemsInGroup (group) {
|
||||
const originalOrder = group.map((node, i) => i)
|
||||
const edges = new Map()
|
||||
const idToIndex = new Map(group.map((item, i) => [item.id, i]))
|
||||
const originalOrder = group.map((node, i) => i);
|
||||
const edges = new Map();
|
||||
const idToIndex = new Map(group.map((item, i) => [item.id, i]));
|
||||
|
||||
group.forEach((item, i) => {
|
||||
if (item.before) {
|
||||
item.before.forEach(toID => {
|
||||
const to = idToIndex.get(toID)
|
||||
const to = idToIndex.get(toID);
|
||||
if (to != null) {
|
||||
pushOntoMultiMap(edges, to, i)
|
||||
pushOntoMultiMap(edges, to, i);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
if (item.after) {
|
||||
item.after.forEach(toID => {
|
||||
const to = idToIndex.get(toID)
|
||||
const to = idToIndex.get(toID);
|
||||
if (to != null) {
|
||||
pushOntoMultiMap(edges, i, to)
|
||||
pushOntoMultiMap(edges, i, to);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const sortedNodes = sortTopologically(originalOrder, edges)
|
||||
return sortedNodes.map(i => group[i])
|
||||
const sortedNodes = sortTopologically(originalOrder, edges);
|
||||
return sortedNodes.map(i => group[i]);
|
||||
}
|
||||
|
||||
function findEdgesInGroup (groups, i, edges) {
|
||||
const group = groups[i]
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
if (item.beforeGroupContaining) {
|
||||
for (const id of item.beforeGroupContaining) {
|
||||
const to = indexOfGroupContainingID(groups, id, group)
|
||||
const to = indexOfGroupContainingID(groups, id, group);
|
||||
if (to !== -1) {
|
||||
pushOntoMultiMap(edges, to, i)
|
||||
return
|
||||
pushOntoMultiMap(edges, to, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.afterGroupContaining) {
|
||||
for (const id of item.afterGroupContaining) {
|
||||
const to = indexOfGroupContainingID(groups, id, group)
|
||||
const to = indexOfGroupContainingID(groups, id, group);
|
||||
if (to !== -1) {
|
||||
pushOntoMultiMap(edges, i, to)
|
||||
return
|
||||
pushOntoMultiMap(edges, i, to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,29 +148,29 @@ function findEdgesInGroup (groups, i, edges) {
|
||||
}
|
||||
|
||||
function sortGroups (groups) {
|
||||
const originalOrder = groups.map((item, i) => i)
|
||||
const edges = new Map()
|
||||
const originalOrder = groups.map((item, i) => i);
|
||||
const edges = new Map();
|
||||
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
findEdgesInGroup(groups, i, edges)
|
||||
findEdgesInGroup(groups, i, edges);
|
||||
}
|
||||
|
||||
const sortedGroupIndexes = sortTopologically(originalOrder, edges)
|
||||
return sortedGroupIndexes.map(i => groups[i])
|
||||
const sortedGroupIndexes = sortTopologically(originalOrder, edges);
|
||||
return sortedGroupIndexes.map(i => groups[i]);
|
||||
}
|
||||
|
||||
function sortMenuItems (menuItems) {
|
||||
const isSeparator = (item) => item.type === 'separator'
|
||||
const separators = menuItems.filter(i => i.type === 'separator')
|
||||
const isSeparator = (item) => item.type === 'separator';
|
||||
const separators = menuItems.filter(i => i.type === 'separator');
|
||||
|
||||
// Split the items into their implicit groups based upon separators.
|
||||
const groups = splitArray(menuItems, isSeparator)
|
||||
const mergedGroups = mergeGroups(groups)
|
||||
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup)
|
||||
const sortedGroups = sortGroups(mergedGroupsWithSortedItems)
|
||||
const groups = splitArray(menuItems, isSeparator);
|
||||
const mergedGroups = mergeGroups(groups);
|
||||
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup);
|
||||
const sortedGroups = sortGroups(mergedGroupsWithSortedItems);
|
||||
|
||||
const joined = joinArrays(sortedGroups, separators)
|
||||
return joined
|
||||
const joined = joinArrays(sortedGroups, separators);
|
||||
return joined;
|
||||
}
|
||||
|
||||
module.exports = { sortMenuItems }
|
||||
module.exports = { sortMenuItems };
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
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 { 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 { Menu } = bindings
|
||||
let applicationMenu = null
|
||||
let groupIdIndex = 0
|
||||
const { Menu } = bindings;
|
||||
let applicationMenu = null;
|
||||
let groupIdIndex = 0;
|
||||
|
||||
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype);
|
||||
|
||||
// Menu Delegate.
|
||||
// This object should hold no reference to |Menu| to avoid cyclic reference.
|
||||
@@ -20,172 +20,172 @@ const delegate = {
|
||||
shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined,
|
||||
isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
|
||||
getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
|
||||
const command = menu.commandsMap[id]
|
||||
if (!command) return
|
||||
if (command.accelerator != null) return command.accelerator
|
||||
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
|
||||
const command = menu.commandsMap[id];
|
||||
if (!command) return;
|
||||
if (command.accelerator != null) return command.accelerator;
|
||||
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator();
|
||||
},
|
||||
shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
|
||||
executeCommand: (menu, event, id) => {
|
||||
const command = menu.commandsMap[id]
|
||||
if (!command) return
|
||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
|
||||
const command = menu.commandsMap[id];
|
||||
if (!command) return;
|
||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
|
||||
},
|
||||
menuWillShow: (menu) => {
|
||||
// Ensure radio groups have at least one menu item selected
|
||||
for (const id in menu.groupsMap) {
|
||||
const found = menu.groupsMap[id].find(item => item.checked) || null
|
||||
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
|
||||
for (const id of Object.keys(menu.groupsMap)) {
|
||||
const found = menu.groupsMap[id].find(item => item.checked) || null;
|
||||
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Instance Methods */
|
||||
|
||||
Menu.prototype._init = function () {
|
||||
this.commandsMap = {}
|
||||
this.groupsMap = {}
|
||||
this.items = []
|
||||
this.delegate = delegate
|
||||
}
|
||||
this.commandsMap = {};
|
||||
this.groupsMap = {};
|
||||
this.items = [];
|
||||
this.delegate = delegate;
|
||||
};
|
||||
|
||||
Menu.prototype.popup = function (options = {}) {
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('Options must be an object')
|
||||
throw new TypeError('Options must be an object');
|
||||
}
|
||||
let { window, x, y, positioningItem, callback } = options
|
||||
let { window, x, y, positioningItem, callback } = options;
|
||||
|
||||
// no callback passed
|
||||
if (!callback || typeof callback !== 'function') callback = () => {}
|
||||
if (!callback || typeof callback !== 'function') callback = () => {};
|
||||
|
||||
// set defaults
|
||||
if (typeof x !== 'number') x = -1
|
||||
if (typeof y !== 'number') y = -1
|
||||
if (typeof positioningItem !== 'number') positioningItem = -1
|
||||
if (typeof x !== 'number') x = -1;
|
||||
if (typeof y !== 'number') y = -1;
|
||||
if (typeof positioningItem !== 'number') positioningItem = -1;
|
||||
|
||||
// find which window to use
|
||||
const wins = TopLevelWindow.getAllWindows()
|
||||
const wins = TopLevelWindow.getAllWindows();
|
||||
if (!wins || wins.indexOf(window) === -1) {
|
||||
window = TopLevelWindow.getFocusedWindow()
|
||||
window = TopLevelWindow.getFocusedWindow();
|
||||
if (!window && wins && wins.length > 0) {
|
||||
window = wins[0]
|
||||
window = wins[0];
|
||||
}
|
||||
if (!window) {
|
||||
throw new Error(`Cannot open Menu without a TopLevelWindow present`)
|
||||
throw new Error(`Cannot open Menu without a TopLevelWindow present`);
|
||||
}
|
||||
}
|
||||
|
||||
this.popupAt(window, x, y, positioningItem, callback)
|
||||
return { browserWindow: window, x, y, position: positioningItem }
|
||||
}
|
||||
this.popupAt(window, x, y, positioningItem, callback);
|
||||
return { browserWindow: window, x, y, position: positioningItem };
|
||||
};
|
||||
|
||||
Menu.prototype.closePopup = function (window) {
|
||||
if (window instanceof TopLevelWindow) {
|
||||
this.closePopupAt(window.id)
|
||||
this.closePopupAt(window.id);
|
||||
} else {
|
||||
// Passing -1 (invalid) would make closePopupAt close the all menu runners
|
||||
// belong to this menu.
|
||||
this.closePopupAt(-1)
|
||||
this.closePopupAt(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Menu.prototype.getMenuItemById = function (id) {
|
||||
const items = this.items
|
||||
const items = this.items;
|
||||
|
||||
let found = items.find(item => item.id === id) || null
|
||||
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)
|
||||
found = items[i].submenu.getMenuItemById(id);
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
return found;
|
||||
};
|
||||
|
||||
Menu.prototype.append = function (item) {
|
||||
return this.insert(this.getItemCount(), item)
|
||||
}
|
||||
return this.insert(this.getItemCount(), item);
|
||||
};
|
||||
|
||||
Menu.prototype.insert = function (pos, item) {
|
||||
if ((item ? item.constructor : void 0) !== MenuItem) {
|
||||
throw new TypeError('Invalid item')
|
||||
throw new TypeError('Invalid item');
|
||||
}
|
||||
|
||||
if (pos < 0) {
|
||||
throw new RangeError(`Position ${pos} cannot be less than 0`)
|
||||
throw new RangeError(`Position ${pos} cannot be less than 0`);
|
||||
} else if (pos > this.getItemCount()) {
|
||||
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`)
|
||||
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`);
|
||||
}
|
||||
|
||||
// insert item depending on its type
|
||||
insertItemByType.call(this, item, pos)
|
||||
insertItemByType.call(this, item, pos);
|
||||
|
||||
// set item properties
|
||||
if (item.sublabel) this.setSublabel(pos, item.sublabel)
|
||||
if (item.toolTip) this.setToolTip(pos, item.toolTip)
|
||||
if (item.icon) this.setIcon(pos, item.icon)
|
||||
if (item.role) this.setRole(pos, item.role)
|
||||
if (item.sublabel) this.setSublabel(pos, item.sublabel);
|
||||
if (item.toolTip) this.setToolTip(pos, item.toolTip);
|
||||
if (item.icon) this.setIcon(pos, item.icon);
|
||||
if (item.role) this.setRole(pos, item.role);
|
||||
|
||||
// Make menu accessable to items.
|
||||
item.overrideReadOnlyProperty('menu', this)
|
||||
item.overrideReadOnlyProperty('menu', this);
|
||||
|
||||
// Remember the items.
|
||||
this.items.splice(pos, 0, item)
|
||||
this.commandsMap[item.commandId] = item
|
||||
}
|
||||
this.items.splice(pos, 0, item);
|
||||
this.commandsMap[item.commandId] = item;
|
||||
};
|
||||
|
||||
Menu.prototype._callMenuWillShow = function () {
|
||||
if (this.delegate) this.delegate.menuWillShow(this)
|
||||
if (this.delegate) this.delegate.menuWillShow(this);
|
||||
this.items.forEach(item => {
|
||||
if (item.submenu) item.submenu._callMenuWillShow()
|
||||
})
|
||||
}
|
||||
if (item.submenu) item.submenu._callMenuWillShow();
|
||||
});
|
||||
};
|
||||
|
||||
/* Static Methods */
|
||||
|
||||
Menu.getApplicationMenu = () => applicationMenu
|
||||
Menu.getApplicationMenu = () => applicationMenu;
|
||||
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
|
||||
|
||||
// set application menu with a preexisting menu
|
||||
Menu.setApplicationMenu = function (menu) {
|
||||
if (menu && menu.constructor !== Menu) {
|
||||
throw new TypeError('Invalid menu')
|
||||
throw new TypeError('Invalid menu');
|
||||
}
|
||||
|
||||
applicationMenu = menu
|
||||
v8Util.setHiddenValue(global, 'applicationMenuSet', true)
|
||||
applicationMenu = menu;
|
||||
v8Util.setHiddenValue(global, 'applicationMenuSet', true);
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (!menu) return
|
||||
menu._callMenuWillShow()
|
||||
bindings.setApplicationMenu(menu)
|
||||
if (!menu) return;
|
||||
menu._callMenuWillShow();
|
||||
bindings.setApplicationMenu(menu);
|
||||
} else {
|
||||
const windows = TopLevelWindow.getAllWindows()
|
||||
return windows.map(w => w.setMenu(menu))
|
||||
const windows = TopLevelWindow.getAllWindows();
|
||||
return windows.map(w => w.setMenu(menu));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Menu.buildFromTemplate = function (template) {
|
||||
if (!Array.isArray(template)) {
|
||||
throw new TypeError('Invalid template for Menu: Menu template must be an array')
|
||||
throw new TypeError('Invalid template for Menu: Menu template must be an array');
|
||||
}
|
||||
if (!areValidTemplateItems(template)) {
|
||||
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type')
|
||||
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type');
|
||||
}
|
||||
const filtered = removeExtraSeparators(template)
|
||||
const sorted = sortTemplate(filtered)
|
||||
const filtered = removeExtraSeparators(template);
|
||||
const sorted = sortTemplate(filtered);
|
||||
|
||||
const menu = new Menu()
|
||||
const menu = new Menu();
|
||||
sorted.forEach(item => {
|
||||
if (item instanceof MenuItem) {
|
||||
menu.append(item)
|
||||
menu.append(item);
|
||||
} else {
|
||||
menu.append(new MenuItem(item))
|
||||
menu.append(new MenuItem(item));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return menu
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
|
||||
/* Helper Functions */
|
||||
|
||||
@@ -196,51 +196,50 @@ function areValidTemplateItems (template) {
|
||||
typeof item === 'object' &&
|
||||
(item.hasOwnProperty('label') ||
|
||||
item.hasOwnProperty('role') ||
|
||||
item.type === 'separator'))
|
||||
item.type === 'separator'));
|
||||
}
|
||||
|
||||
function sortTemplate (template) {
|
||||
const sorted = sortMenuItems(template)
|
||||
for (const id in sorted) {
|
||||
const item = sorted[id]
|
||||
const sorted = sortMenuItems(template);
|
||||
for (const item of sorted) {
|
||||
if (Array.isArray(item.submenu)) {
|
||||
item.submenu = sortTemplate(item.submenu)
|
||||
item.submenu = sortTemplate(item.submenu);
|
||||
}
|
||||
}
|
||||
return sorted
|
||||
return sorted;
|
||||
}
|
||||
|
||||
// Search between separators to find a radio menu item and return its group id
|
||||
function generateGroupId (items, pos) {
|
||||
if (pos > 0) {
|
||||
for (let idx = pos - 1; idx >= 0; idx--) {
|
||||
if (items[idx].type === 'radio') return items[idx].groupId
|
||||
if (items[idx].type === 'separator') break
|
||||
if (items[idx].type === 'radio') return items[idx].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 === 'separator') break
|
||||
if (items[idx].type === 'radio') return items[idx].groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
}
|
||||
groupIdIndex += 1
|
||||
return groupIdIndex
|
||||
groupIdIndex += 1;
|
||||
return groupIdIndex;
|
||||
}
|
||||
|
||||
function removeExtraSeparators (items) {
|
||||
// fold adjacent separators together
|
||||
let ret = items.filter((e, idx, arr) => {
|
||||
if (e.visible === false) return true
|
||||
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'
|
||||
})
|
||||
if (e.visible === false) return true;
|
||||
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator';
|
||||
});
|
||||
|
||||
// remove edge separators
|
||||
ret = ret.filter((e, idx, arr) => {
|
||||
if (e.visible === false) return true
|
||||
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1)
|
||||
})
|
||||
if (e.visible === false) return true;
|
||||
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1);
|
||||
});
|
||||
|
||||
return ret
|
||||
return ret;
|
||||
}
|
||||
|
||||
function insertItemByType (item, pos) {
|
||||
@@ -251,28 +250,28 @@ function insertItemByType (item, pos) {
|
||||
submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
|
||||
radio: () => {
|
||||
// Grouping radio menu items
|
||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
|
||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
|
||||
if (this.groupsMap[item.groupId] == null) {
|
||||
this.groupsMap[item.groupId] = []
|
||||
this.groupsMap[item.groupId] = [];
|
||||
}
|
||||
this.groupsMap[item.groupId].push(item)
|
||||
this.groupsMap[item.groupId].push(item);
|
||||
|
||||
// Setting a radio menu item should flip other items in the group.
|
||||
v8Util.setHiddenValue(item, 'checked', item.checked)
|
||||
v8Util.setHiddenValue(item, 'checked', item.checked);
|
||||
Object.defineProperty(item, 'checked', {
|
||||
enumerable: true,
|
||||
get: () => v8Util.getHiddenValue(item, 'checked'),
|
||||
set: () => {
|
||||
this.groupsMap[item.groupId].forEach(other => {
|
||||
if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
|
||||
})
|
||||
v8Util.setHiddenValue(item, 'checked', true)
|
||||
if (other !== item) v8Util.setHiddenValue(other, 'checked', false);
|
||||
});
|
||||
v8Util.setHiddenValue(item, 'checked', true);
|
||||
}
|
||||
})
|
||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
|
||||
});
|
||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
|
||||
}
|
||||
}
|
||||
types[item.type]()
|
||||
};
|
||||
types[item.type]();
|
||||
}
|
||||
|
||||
module.exports = Menu
|
||||
module.exports = Menu;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
// TODO: Figure out a way to not duplicate this information between here and module-list
|
||||
// It is currently duplicated as module-list "require"s all the browser API file and the
|
||||
// remote module in the renderer process depends on that file. As a result webpack
|
||||
// includes all the browser API files in the renderer process as well and we want to avoid that
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
const features = process.electronBinding('features');
|
||||
|
||||
// Browser side modules, please sort alphabetically.
|
||||
module.exports = [
|
||||
@@ -37,7 +37,7 @@ module.exports = [
|
||||
{ name: 'View' },
|
||||
{ name: 'webContents' },
|
||||
{ name: 'WebContentsView' }
|
||||
]
|
||||
];
|
||||
|
||||
if (features.isViewApiEnabled()) {
|
||||
module.exports.push(
|
||||
@@ -48,5 +48,5 @@ if (features.isViewApiEnabled()) {
|
||||
{ name: 'MdTextButton' },
|
||||
{ name: 'ResizeArea' },
|
||||
{ name: 'TextField' }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// TODO: Updating this file also required updating the module-keys file
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
const features = process.electronBinding('features');
|
||||
|
||||
// Browser side modules, please sort alphabetically.
|
||||
export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
@@ -32,7 +32,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'View', loader: () => require('./view') },
|
||||
{ name: 'webContents', loader: () => require('./web-contents') },
|
||||
{ name: 'WebContentsView', loader: () => require('./web-contents-view') }
|
||||
]
|
||||
];
|
||||
|
||||
if (features.isViewApiEnabled()) {
|
||||
browserModuleList.push(
|
||||
@@ -43,5 +43,5 @@ if (features.isViewApiEnabled()) {
|
||||
{ name: 'MdTextButton', loader: () => require('./views/md-text-button') },
|
||||
{ name: 'ResizeArea', loader: () => require('./views/resize-area') },
|
||||
{ name: 'TextField', loader: () => require('./views/text-field') }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme')
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
|
||||
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(nativeTheme as any)
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(nativeTheme as any);
|
||||
|
||||
module.exports = nativeTheme
|
||||
module.exports = nativeTheme;
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
const { app, session } = require('electron')
|
||||
const { app, session } = require('electron');
|
||||
|
||||
// Fallback to default session.
|
||||
Object.setPrototypeOf(module.exports, new Proxy({}, {
|
||||
get (target, property) {
|
||||
if (!app.isReady()) return
|
||||
if (!app.isReady()) return;
|
||||
|
||||
const netLog = session.defaultSession.netLog
|
||||
const netLog = session.defaultSession.netLog;
|
||||
|
||||
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
|
||||
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return;
|
||||
|
||||
// check for properties on the prototype chain that aren't functions
|
||||
if (typeof netLog[property] !== 'function') return netLog[property]
|
||||
if (typeof netLog[property] !== 'function') return netLog[property];
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args) => netLog[property](...args)
|
||||
return (...args) => netLog[property](...args);
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return []
|
||||
if (!app.isReady()) return [];
|
||||
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog))
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor (target) {
|
||||
return { configurable: true, enumerable: true }
|
||||
return { configurable: true, enumerable: true };
|
||||
}
|
||||
}))
|
||||
}));
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const url = require('url')
|
||||
const { EventEmitter } = require('events')
|
||||
const { Readable, Writable } = require('stream')
|
||||
const { app } = require('electron')
|
||||
const { Session } = process.electronBinding('session')
|
||||
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net')
|
||||
const { URLLoader } = net
|
||||
const url = require('url');
|
||||
const { EventEmitter } = require('events');
|
||||
const { Readable, Writable } = require('stream');
|
||||
const { app } = require('electron');
|
||||
const { Session } = process.electronBinding('session');
|
||||
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net');
|
||||
const { URLLoader } = net;
|
||||
|
||||
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype);
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:'])
|
||||
const kSupportedProtocols = new Set(['http:', 'https:']);
|
||||
|
||||
// set of headers that Node.js discards duplicates for
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
@@ -33,27 +33,27 @@ const discardableDuplicateHeaders = new Set([
|
||||
'server',
|
||||
'age',
|
||||
'expires'
|
||||
])
|
||||
]);
|
||||
|
||||
class IncomingMessage extends Readable {
|
||||
constructor (responseHead) {
|
||||
super()
|
||||
this._shouldPush = false
|
||||
this._data = []
|
||||
this._responseHead = responseHead
|
||||
super();
|
||||
this._shouldPush = false;
|
||||
this._data = [];
|
||||
this._responseHead = responseHead;
|
||||
}
|
||||
|
||||
get statusCode () {
|
||||
return this._responseHead.statusCode
|
||||
return this._responseHead.statusCode;
|
||||
}
|
||||
|
||||
get statusMessage () {
|
||||
return this._responseHead.statusMessage
|
||||
return this._responseHead.statusMessage;
|
||||
}
|
||||
|
||||
get headers () {
|
||||
const filteredHeaders = {}
|
||||
const { rawHeaders } = this._responseHead
|
||||
const filteredHeaders = {};
|
||||
const { rawHeaders } = this._responseHead;
|
||||
rawHeaders.forEach(header => {
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
|
||||
discardableDuplicateHeaders.has(header.key)) {
|
||||
@@ -63,145 +63,145 @@ class IncomingMessage extends Readable {
|
||||
// keep set-cookie as an array per Node.js rules
|
||||
// see https://nodejs.org/api/http.html#http_message_headers
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key].push(header.value)
|
||||
filteredHeaders[header.key].push(header.value);
|
||||
} else {
|
||||
filteredHeaders[header.key] = [header.value]
|
||||
filteredHeaders[header.key] = [header.value];
|
||||
}
|
||||
} else {
|
||||
// for non-cookie headers, the values are joined together with ', '
|
||||
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||
filteredHeaders[header.key] += `, ${header.value}`
|
||||
filteredHeaders[header.key] += `, ${header.value}`;
|
||||
} else {
|
||||
filteredHeaders[header.key] = header.value
|
||||
filteredHeaders[header.key] = header.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return filteredHeaders
|
||||
});
|
||||
return filteredHeaders;
|
||||
}
|
||||
|
||||
get httpVersion () {
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`
|
||||
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
|
||||
}
|
||||
|
||||
get httpVersionMajor () {
|
||||
return this._responseHead.httpVersion.major
|
||||
return this._responseHead.httpVersion.major;
|
||||
}
|
||||
|
||||
get httpVersionMinor () {
|
||||
return this._responseHead.httpVersion.minor
|
||||
return this._responseHead.httpVersion.minor;
|
||||
}
|
||||
|
||||
get rawTrailers () {
|
||||
throw new Error('HTTP trailers are not supported')
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
get trailers () {
|
||||
throw new Error('HTTP trailers are not supported')
|
||||
throw new Error('HTTP trailers are not supported');
|
||||
}
|
||||
|
||||
_storeInternalData (chunk) {
|
||||
this._data.push(chunk)
|
||||
this._pushInternalData()
|
||||
this._data.push(chunk);
|
||||
this._pushInternalData();
|
||||
}
|
||||
|
||||
_pushInternalData () {
|
||||
while (this._shouldPush && this._data.length > 0) {
|
||||
const chunk = this._data.shift()
|
||||
this._shouldPush = this.push(chunk)
|
||||
const chunk = this._data.shift();
|
||||
this._shouldPush = this.push(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
_read () {
|
||||
this._shouldPush = true
|
||||
this._pushInternalData()
|
||||
this._shouldPush = true;
|
||||
this._pushInternalData();
|
||||
}
|
||||
}
|
||||
|
||||
/** Writable stream that buffers up everything written to it. */
|
||||
class SlurpStream extends Writable {
|
||||
constructor () {
|
||||
super()
|
||||
this._data = Buffer.alloc(0)
|
||||
super();
|
||||
this._data = Buffer.alloc(0);
|
||||
}
|
||||
_write (chunk, encoding, callback) {
|
||||
this._data = Buffer.concat([this._data, chunk])
|
||||
callback()
|
||||
this._data = Buffer.concat([this._data, chunk]);
|
||||
callback();
|
||||
}
|
||||
data () { return this._data }
|
||||
data () { return this._data; }
|
||||
}
|
||||
|
||||
class ChunkedBodyStream extends Writable {
|
||||
constructor (clientRequest) {
|
||||
super()
|
||||
this._clientRequest = clientRequest
|
||||
super();
|
||||
this._clientRequest = clientRequest;
|
||||
}
|
||||
|
||||
_write (chunk, encoding, callback) {
|
||||
if (this._downstream) {
|
||||
this._downstream.write(chunk).then(callback, callback)
|
||||
this._downstream.write(chunk).then(callback, callback);
|
||||
} else {
|
||||
// the contract of _write is that we won't be called again until we call
|
||||
// the callback, so we're good to just save a single chunk.
|
||||
this._pendingChunk = chunk
|
||||
this._pendingCallback = callback
|
||||
this._pendingChunk = chunk;
|
||||
this._pendingCallback = callback;
|
||||
|
||||
// The first write to a chunked body stream begins the request.
|
||||
this._clientRequest._startRequest()
|
||||
this._clientRequest._startRequest();
|
||||
}
|
||||
}
|
||||
|
||||
_final (callback) {
|
||||
this._downstream.done()
|
||||
callback()
|
||||
this._downstream.done();
|
||||
callback();
|
||||
}
|
||||
|
||||
startReading (pipe) {
|
||||
if (this._downstream) {
|
||||
throw new Error('two startReading calls???')
|
||||
throw new Error('two startReading calls???');
|
||||
}
|
||||
this._downstream = pipe
|
||||
this._downstream = pipe;
|
||||
if (this._pendingChunk) {
|
||||
const doneWriting = (maybeError) => {
|
||||
const cb = this._pendingCallback
|
||||
delete this._pendingCallback
|
||||
delete this._pendingChunk
|
||||
cb(maybeError)
|
||||
}
|
||||
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting)
|
||||
const cb = this._pendingCallback;
|
||||
delete this._pendingCallback;
|
||||
delete this._pendingChunk;
|
||||
cb(maybeError);
|
||||
};
|
||||
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseOptions (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = url.parse(options)
|
||||
options = url.parse(options);
|
||||
} else {
|
||||
options = { ...options }
|
||||
options = { ...options };
|
||||
}
|
||||
|
||||
const method = (options.method || 'GET').toUpperCase()
|
||||
let urlStr = options.url
|
||||
const method = (options.method || 'GET').toUpperCase();
|
||||
let urlStr = options.url;
|
||||
|
||||
if (!urlStr) {
|
||||
const urlObj = {}
|
||||
const protocol = options.protocol || 'http:'
|
||||
const urlObj = {};
|
||||
const protocol = options.protocol || 'http:';
|
||||
if (!kSupportedProtocols.has(protocol)) {
|
||||
throw new Error('Protocol "' + protocol + '" not supported')
|
||||
throw new Error('Protocol "' + protocol + '" not supported');
|
||||
}
|
||||
urlObj.protocol = protocol
|
||||
urlObj.protocol = protocol;
|
||||
|
||||
if (options.host) {
|
||||
urlObj.host = options.host
|
||||
urlObj.host = options.host;
|
||||
} else {
|
||||
if (options.hostname) {
|
||||
urlObj.hostname = options.hostname
|
||||
urlObj.hostname = options.hostname;
|
||||
} else {
|
||||
urlObj.hostname = 'localhost'
|
||||
urlObj.hostname = 'localhost';
|
||||
}
|
||||
|
||||
if (options.port) {
|
||||
urlObj.port = options.port
|
||||
urlObj.port = options.port;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,203 +212,204 @@ function parseOptions (options) {
|
||||
// well, and b) possibly too restrictive for real-world usage. That's
|
||||
// why it only scans for spaces because those are guaranteed to create
|
||||
// an invalid request.
|
||||
throw new TypeError('Request path contains unescaped characters')
|
||||
throw new TypeError('Request path contains unescaped characters');
|
||||
}
|
||||
const pathObj = url.parse(options.path || '/')
|
||||
urlObj.pathname = pathObj.pathname
|
||||
urlObj.search = pathObj.search
|
||||
urlObj.hash = pathObj.hash
|
||||
urlStr = url.format(urlObj)
|
||||
const pathObj = url.parse(options.path || '/');
|
||||
urlObj.pathname = pathObj.pathname;
|
||||
urlObj.search = pathObj.search;
|
||||
urlObj.hash = pathObj.hash;
|
||||
urlStr = url.format(urlObj);
|
||||
}
|
||||
|
||||
const redirectPolicy = options.redirect || 'follow'
|
||||
const redirectPolicy = options.redirect || 'follow';
|
||||
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
|
||||
throw new Error('redirect mode should be one of follow, error or manual')
|
||||
throw new Error('redirect mode should be one of follow, error or manual');
|
||||
}
|
||||
|
||||
if (options.headers != null && typeof options.headers !== 'object') {
|
||||
throw new TypeError('headers must be an object')
|
||||
throw new TypeError('headers must be an object');
|
||||
}
|
||||
|
||||
const urlLoaderOptions = {
|
||||
method: method,
|
||||
url: urlStr,
|
||||
redirectPolicy,
|
||||
extraHeaders: options.headers || {}
|
||||
}
|
||||
extraHeaders: options.headers || {},
|
||||
useSessionCookies: options.useSessionCookies || false
|
||||
};
|
||||
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders)) {
|
||||
if (!_isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`)
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!_isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`)
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
}
|
||||
if (options.session) {
|
||||
if (options.session instanceof Session) {
|
||||
urlLoaderOptions.session = options.session
|
||||
urlLoaderOptions.session = options.session;
|
||||
} else {
|
||||
throw new TypeError('`session` should be an instance of the Session class')
|
||||
throw new TypeError('`session` should be an instance of the Session class');
|
||||
}
|
||||
} else if (options.partition) {
|
||||
if (typeof options.partition === 'string') {
|
||||
urlLoaderOptions.partition = options.partition
|
||||
urlLoaderOptions.partition = options.partition;
|
||||
} else {
|
||||
throw new TypeError('`partition` should be a string')
|
||||
throw new TypeError('`partition` should be a string');
|
||||
}
|
||||
}
|
||||
return urlLoaderOptions
|
||||
return urlLoaderOptions;
|
||||
}
|
||||
|
||||
class ClientRequest extends Writable {
|
||||
constructor (options, callback) {
|
||||
super({ autoDestroy: true })
|
||||
super({ autoDestroy: true });
|
||||
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready')
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
this.once('response', callback)
|
||||
this.once('response', callback);
|
||||
}
|
||||
|
||||
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options)
|
||||
this._urlLoaderOptions = urlLoaderOptions
|
||||
this._redirectPolicy = redirectPolicy
|
||||
this._started = false
|
||||
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
|
||||
this._urlLoaderOptions = urlLoaderOptions;
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
this._started = false;
|
||||
}
|
||||
|
||||
set chunkedEncoding (value) {
|
||||
if (this._started) {
|
||||
throw new Error('chunkedEncoding can only be set before the request is started')
|
||||
throw new Error('chunkedEncoding can only be set before the request is started');
|
||||
}
|
||||
if (typeof this._chunkedEncoding !== 'undefined') {
|
||||
throw new Error('chunkedEncoding can only be set once')
|
||||
throw new Error('chunkedEncoding can only be set once');
|
||||
}
|
||||
this._chunkedEncoding = !!value
|
||||
this._chunkedEncoding = !!value;
|
||||
if (this._chunkedEncoding) {
|
||||
this._body = new ChunkedBodyStream(this)
|
||||
this._body = new ChunkedBodyStream(this);
|
||||
this._urlLoaderOptions.body = (pipe) => {
|
||||
this._body.startReading(pipe)
|
||||
}
|
||||
this._body.startReading(pipe);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
setHeader (name, value) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)')
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)')
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t set headers after they are sent')
|
||||
throw new Error('Can\'t set headers after they are sent');
|
||||
}
|
||||
if (!_isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`)
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!_isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`)
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
this._urlLoaderOptions.extraHeaders[key] = value
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.extraHeaders[key] = value;
|
||||
}
|
||||
|
||||
getHeader (name) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for getHeader(name)')
|
||||
throw new Error('`name` is required for getHeader(name)');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
return this._urlLoaderOptions.extraHeaders[key]
|
||||
const key = name.toLowerCase();
|
||||
return this._urlLoaderOptions.extraHeaders[key];
|
||||
}
|
||||
|
||||
removeHeader (name) {
|
||||
if (name == null) {
|
||||
throw new Error('`name` is required for removeHeader(name)')
|
||||
throw new Error('`name` is required for removeHeader(name)');
|
||||
}
|
||||
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t remove headers after they are sent')
|
||||
throw new Error('Can\'t remove headers after they are sent');
|
||||
}
|
||||
|
||||
const key = name.toLowerCase()
|
||||
delete this._urlLoaderOptions.extraHeaders[key]
|
||||
const key = name.toLowerCase();
|
||||
delete this._urlLoaderOptions.extraHeaders[key];
|
||||
}
|
||||
|
||||
_write (chunk, encoding, callback) {
|
||||
this._firstWrite = true
|
||||
this._firstWrite = true;
|
||||
if (!this._body) {
|
||||
this._body = new SlurpStream()
|
||||
this._body = new SlurpStream();
|
||||
this._body.on('finish', () => {
|
||||
this._urlLoaderOptions.body = this._body.data()
|
||||
this._startRequest()
|
||||
})
|
||||
this._urlLoaderOptions.body = this._body.data();
|
||||
this._startRequest();
|
||||
});
|
||||
}
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.write(chunk, encoding, callback)
|
||||
this._body.write(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
_final (callback) {
|
||||
if (this._body) {
|
||||
// TODO: is this the right way to forward to another stream?
|
||||
this._body.end(callback)
|
||||
this._body.end(callback);
|
||||
} else {
|
||||
// end() called without a body, go ahead and start the request
|
||||
this._startRequest()
|
||||
callback()
|
||||
this._startRequest();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
_startRequest () {
|
||||
this._started = true
|
||||
this._started = true;
|
||||
const stringifyValues = (obj) => {
|
||||
const ret = {}
|
||||
for (const k in obj) {
|
||||
ret[k] = obj[k].toString()
|
||||
const ret = {};
|
||||
for (const k of Object.keys(obj)) {
|
||||
ret[k] = obj[k].toString();
|
||||
}
|
||||
return ret
|
||||
}
|
||||
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) }
|
||||
this._urlLoader = new URLLoader(opts)
|
||||
return ret;
|
||||
};
|
||||
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
|
||||
this._urlLoader = new URLLoader(opts);
|
||||
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
|
||||
const response = this._response = new IncomingMessage(responseHead)
|
||||
this.emit('response', response)
|
||||
})
|
||||
const response = this._response = new IncomingMessage(responseHead);
|
||||
this.emit('response', response);
|
||||
});
|
||||
this._urlLoader.on('data', (event, data) => {
|
||||
this._response._storeInternalData(Buffer.from(data))
|
||||
})
|
||||
this._response._storeInternalData(Buffer.from(data));
|
||||
});
|
||||
this._urlLoader.on('complete', () => {
|
||||
if (this._response) { this._response._storeInternalData(null) }
|
||||
})
|
||||
if (this._response) { this._response._storeInternalData(null); }
|
||||
});
|
||||
this._urlLoader.on('error', (event, netErrorString) => {
|
||||
const error = new Error(netErrorString)
|
||||
if (this._response) this._response.destroy(error)
|
||||
this._die(error)
|
||||
})
|
||||
const error = new Error(netErrorString);
|
||||
if (this._response) this._response.destroy(error);
|
||||
this._die(error);
|
||||
});
|
||||
|
||||
this._urlLoader.on('login', (event, authInfo, callback) => {
|
||||
const handled = this.emit('login', authInfo, callback)
|
||||
const handled = this.emit('login', authInfo, callback);
|
||||
if (!handled) {
|
||||
// If there were no listeners, cancel the authentication request.
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
|
||||
const { statusCode, newMethod, newUrl } = redirectInfo
|
||||
const { statusCode, newMethod, newUrl } = redirectInfo;
|
||||
if (this._redirectPolicy === 'error') {
|
||||
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`))
|
||||
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`));
|
||||
} else if (this._redirectPolicy === 'manual') {
|
||||
let _followRedirect = false
|
||||
this._followRedirectCb = () => { _followRedirect = true }
|
||||
let _followRedirect = false;
|
||||
this._followRedirectCb = () => { _followRedirect = true; };
|
||||
try {
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers)
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = null
|
||||
this._followRedirectCb = null;
|
||||
if (!_followRedirect && !this._aborted) {
|
||||
this._die(new Error('Redirect was cancelled'))
|
||||
this._die(new Error('Redirect was cancelled'));
|
||||
}
|
||||
}
|
||||
} else if (this._redirectPolicy === 'follow') {
|
||||
@@ -416,61 +417,61 @@ class ClientRequest extends Writable {
|
||||
// allowed but does nothing. (Perhaps it should throw an error
|
||||
// though...? Since the redirect will happen regardless.)
|
||||
try {
|
||||
this._followRedirectCb = () => {}
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers)
|
||||
this._followRedirectCb = () => {};
|
||||
this.emit('redirect', statusCode, newMethod, newUrl, headers);
|
||||
} finally {
|
||||
this._followRedirectCb = null
|
||||
this._followRedirectCb = null;
|
||||
}
|
||||
} else {
|
||||
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`))
|
||||
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this._urlLoader.on('upload-progress', (event, position, total) => {
|
||||
this._uploadProgress = { active: true, started: true, current: position, total }
|
||||
this.emit('upload-progress', position, total) // Undocumented, for now
|
||||
})
|
||||
this._uploadProgress = { active: true, started: true, current: position, total };
|
||||
this.emit('upload-progress', position, total); // Undocumented, for now
|
||||
});
|
||||
|
||||
this._urlLoader.on('download-progress', (event, current) => {
|
||||
if (this._response) {
|
||||
this._response.emit('download-progress', current) // Undocumented, for now
|
||||
this._response.emit('download-progress', current); // Undocumented, for now
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
followRedirect () {
|
||||
if (this._followRedirectCb) {
|
||||
this._followRedirectCb()
|
||||
this._followRedirectCb();
|
||||
} else {
|
||||
throw new Error('followRedirect() called, but was not waiting for a redirect')
|
||||
throw new Error('followRedirect() called, but was not waiting for a redirect');
|
||||
}
|
||||
}
|
||||
|
||||
abort () {
|
||||
if (!this._aborted) {
|
||||
process.nextTick(() => { this.emit('abort') })
|
||||
process.nextTick(() => { this.emit('abort'); });
|
||||
}
|
||||
this._aborted = true
|
||||
this._die()
|
||||
this._aborted = true;
|
||||
this._die();
|
||||
}
|
||||
|
||||
_die (err) {
|
||||
this.destroy(err)
|
||||
this.destroy(err);
|
||||
if (this._urlLoader) {
|
||||
this._urlLoader.cancel()
|
||||
if (this._response) this._response.destroy(err)
|
||||
this._urlLoader.cancel();
|
||||
if (this._response) this._response.destroy(err);
|
||||
}
|
||||
}
|
||||
|
||||
getUploadProgress () {
|
||||
return this._uploadProgress ? { ...this._uploadProgress } : { active: false }
|
||||
return this._uploadProgress ? { ...this._uploadProgress } : { active: false };
|
||||
}
|
||||
}
|
||||
|
||||
Net.prototype.request = function (options, callback) {
|
||||
return new ClientRequest(options, callback)
|
||||
}
|
||||
return new ClientRequest(options, callback);
|
||||
};
|
||||
|
||||
net.ClientRequest = ClientRequest
|
||||
net.ClientRequest = ClientRequest;
|
||||
|
||||
module.exports = net
|
||||
module.exports = net;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { Notification, isSupported } = process.electronBinding('notification')
|
||||
const { EventEmitter } = require('events');
|
||||
const { Notification, isSupported } = process.electronBinding('notification');
|
||||
|
||||
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype);
|
||||
|
||||
Notification.isSupported = isSupported
|
||||
Notification.isSupported = isSupported;
|
||||
|
||||
module.exports = Notification
|
||||
module.exports = Notification;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
import { createLazyInstance } from '../utils'
|
||||
import { createLazyInstance } from '../utils';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor')
|
||||
const { EventEmitter } = require('events');
|
||||
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor');
|
||||
|
||||
// PowerMonitor is an EventEmitter.
|
||||
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype);
|
||||
|
||||
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true)
|
||||
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true);
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
// In order to delay system shutdown when e.preventDefault() is invoked
|
||||
@@ -18,21 +18,21 @@ if (process.platform === 'linux') {
|
||||
//
|
||||
// So here we watch for 'shutdown' listeners to be added or removed and
|
||||
// set or unset our shutdown delay lock accordingly.
|
||||
const { app } = require('electron')
|
||||
const { app } = require('electron');
|
||||
app.whenReady().then(() => {
|
||||
powerMonitor.on('newListener', (event: string) => {
|
||||
// whenever the listener count is incremented to one...
|
||||
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
|
||||
powerMonitor.blockShutdown()
|
||||
powerMonitor.blockShutdown();
|
||||
}
|
||||
})
|
||||
});
|
||||
powerMonitor.on('removeListener', (event: string) => {
|
||||
// whenever the listener count is decremented to zero...
|
||||
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
|
||||
powerMonitor.unblockShutdown()
|
||||
powerMonitor.unblockShutdown();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = powerMonitor
|
||||
module.exports = powerMonitor;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker
|
||||
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { app, session } from 'electron'
|
||||
import { app, session } from 'electron';
|
||||
|
||||
// Global protocol APIs.
|
||||
const protocol = process.electronBinding('protocol')
|
||||
const protocol = process.electronBinding('protocol');
|
||||
|
||||
// Fallback protocol APIs of default session.
|
||||
Object.setPrototypeOf(protocol, new Proxy({}, {
|
||||
get (_target, property) {
|
||||
if (!app.isReady()) return
|
||||
if (!app.isReady()) return;
|
||||
|
||||
const protocol = session.defaultSession!.protocol
|
||||
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return
|
||||
const protocol = session.defaultSession!.protocol;
|
||||
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return;
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args)
|
||||
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return []
|
||||
if (!app.isReady()) return [];
|
||||
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol))
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol));
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor () {
|
||||
return { configurable: true, enumerable: true }
|
||||
return { configurable: true, enumerable: true };
|
||||
}
|
||||
}))
|
||||
}));
|
||||
|
||||
export default protocol
|
||||
export default protocol;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
import { createLazyInstance } from '../utils'
|
||||
const { EventEmitter } = require('events')
|
||||
const { Screen, createScreen } = process.electronBinding('screen')
|
||||
import { createLazyInstance } from '../utils';
|
||||
const { EventEmitter } = require('events');
|
||||
const { Screen, createScreen } = process.electronBinding('screen');
|
||||
|
||||
// Screen is an EventEmitter.
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
|
||||
|
||||
module.exports = createLazyInstance(createScreen, Screen, true)
|
||||
module.exports = createLazyInstance(createScreen, Screen, true);
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { app, deprecate } = require('electron')
|
||||
const { fromPartition, Session, Cookies, NetLog, Protocol, ServiceWorkerContext } = process.electronBinding('session')
|
||||
const { EventEmitter } = require('events');
|
||||
const { app, deprecate } = require('electron');
|
||||
const { fromPartition, Session, Cookies, NetLog, Protocol, ServiceWorkerContext } = process.electronBinding('session');
|
||||
|
||||
// Public API.
|
||||
Object.defineProperties(exports, {
|
||||
defaultSession: {
|
||||
enumerable: true,
|
||||
get () { return fromPartition('') }
|
||||
get () { return fromPartition(''); }
|
||||
},
|
||||
fromPartition: {
|
||||
enumerable: true,
|
||||
value: fromPartition
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(ServiceWorkerContext.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype);
|
||||
Object.setPrototypeOf(ServiceWorkerContext.prototype, EventEmitter.prototype);
|
||||
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
|
||||
|
||||
Session.prototype._init = function () {
|
||||
app.emit('session-created', this)
|
||||
}
|
||||
app.emit('session-created', this);
|
||||
};
|
||||
|
||||
const _originalStartLogging = NetLog.prototype.startLogging
|
||||
const _originalStartLogging = NetLog.prototype.startLogging;
|
||||
NetLog.prototype.startLogging = function (path, ...args) {
|
||||
this._currentlyLoggingPath = path
|
||||
this._currentlyLoggingPath = path;
|
||||
try {
|
||||
return _originalStartLogging.call(this, path, ...args)
|
||||
return _originalStartLogging.call(this, path, ...args);
|
||||
} catch (e) {
|
||||
this._currentlyLoggingPath = null
|
||||
throw e
|
||||
this._currentlyLoggingPath = null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const _originalStopLogging = NetLog.prototype.stopLogging
|
||||
const _originalStopLogging = NetLog.prototype.stopLogging;
|
||||
NetLog.prototype.stopLogging = function () {
|
||||
const logPath = this._currentlyLoggingPath
|
||||
this._currentlyLoggingPath = null
|
||||
return _originalStopLogging.call(this).then(() => logPath)
|
||||
}
|
||||
const logPath = this._currentlyLoggingPath;
|
||||
this._currentlyLoggingPath = null;
|
||||
return _originalStopLogging.call(this).then(() => logPath);
|
||||
};
|
||||
|
||||
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath')
|
||||
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath');
|
||||
Object.defineProperties(NetLog.prototype, {
|
||||
currentlyLoggingPath: {
|
||||
enumerable: true,
|
||||
get () {
|
||||
currentlyLoggingPathDeprecated()
|
||||
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath
|
||||
currentlyLoggingPathDeprecated();
|
||||
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { deprecate } from 'electron'
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences')
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate } from 'electron';
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
|
||||
EventEmitter.call(systemPreferences)
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(systemPreferences);
|
||||
|
||||
if ('appLevelAppearance' in systemPreferences) {
|
||||
deprecate.fnToProperty(
|
||||
SystemPreferences.prototype,
|
||||
'appLevelAppearance',
|
||||
'_getAppLevelAppearance',
|
||||
'_setAppLevelAppearance'
|
||||
)
|
||||
if ('getAppLevelAppearance' in systemPreferences) {
|
||||
const nativeALAGetter = systemPreferences.getAppLevelAppearance;
|
||||
const nativeALASetter = systemPreferences.setAppLevelAppearance;
|
||||
Object.defineProperty(SystemPreferences.prototype, 'appLevelAppearance', {
|
||||
get: () => nativeALAGetter.call(systemPreferences),
|
||||
set: (appearance) => nativeALASetter.call(systemPreferences, appearance)
|
||||
});
|
||||
}
|
||||
|
||||
if ('effectiveAppearance' in systemPreferences) {
|
||||
deprecate.fnToProperty(
|
||||
SystemPreferences.prototype,
|
||||
'effectiveAppearance',
|
||||
'_getEffectiveAppearance'
|
||||
)
|
||||
if ('getEffectiveAppearance' in systemPreferences) {
|
||||
const nativeEAGetter = systemPreferences.getAppLevelAppearance;
|
||||
Object.defineProperty(SystemPreferences.prototype, 'effectiveAppearance', {
|
||||
get: () => nativeEAGetter.call(systemPreferences)
|
||||
});
|
||||
}
|
||||
|
||||
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isDarkMode,
|
||||
'systemPreferences.isDarkMode()',
|
||||
'nativeTheme.shouldUseDarkColors'
|
||||
)
|
||||
);
|
||||
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isInvertedColorScheme,
|
||||
'systemPreferences.isInvertedColorScheme()',
|
||||
'nativeTheme.shouldUseInvertedColorScheme'
|
||||
)
|
||||
);
|
||||
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isHighContrastColorScheme,
|
||||
'systemPreferences.isHighContrastColorScheme()',
|
||||
'nativeTheme.shouldUseHighContrastColors'
|
||||
)
|
||||
);
|
||||
|
||||
module.exports = systemPreferences
|
||||
module.exports = systemPreferences;
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const { EventEmitter } = require('events')
|
||||
const { TopLevelWindow } = process.electronBinding('top_level_window')
|
||||
const electron = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const { TopLevelWindow } = process.electronBinding('top_level_window');
|
||||
|
||||
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
TopLevelWindow.prototype._init = function () {
|
||||
// Avoid recursive require.
|
||||
const { app } = electron
|
||||
const { app } = electron;
|
||||
|
||||
// Simulate the application menu on platforms other than macOS.
|
||||
if (process.platform !== 'darwin') {
|
||||
const menu = app.applicationMenu
|
||||
if (menu) this.setMenu(menu)
|
||||
const menu = app.applicationMenu;
|
||||
if (menu) this.setMenu(menu);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TopLevelWindow.getFocusedWindow = () => {
|
||||
return TopLevelWindow.getAllWindows().find((win) => win.isFocused())
|
||||
}
|
||||
return TopLevelWindow.getAllWindows().find((win) => win.isFocused());
|
||||
};
|
||||
|
||||
module.exports = TopLevelWindow
|
||||
module.exports = TopLevelWindow;
|
||||
|
||||
@@ -1,337 +1,349 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { EventEmitter } = require('events');
|
||||
|
||||
let nextItemID = 1
|
||||
let nextItemID = 1;
|
||||
|
||||
class TouchBar extends EventEmitter {
|
||||
// Bind a touch bar to a window
|
||||
static _setOnWindow (touchBar, window) {
|
||||
if (window._touchBar != null) {
|
||||
window._touchBar._removeFromWindow(window)
|
||||
window._touchBar._removeFromWindow(window);
|
||||
}
|
||||
|
||||
if (touchBar == null) {
|
||||
window._setTouchBarItems([])
|
||||
return
|
||||
window._setTouchBarItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(touchBar)) {
|
||||
touchBar = new TouchBar(touchBar)
|
||||
touchBar = new TouchBar(touchBar);
|
||||
}
|
||||
touchBar._addToWindow(window)
|
||||
touchBar._addToWindow(window);
|
||||
}
|
||||
|
||||
constructor (options) {
|
||||
super()
|
||||
super();
|
||||
|
||||
if (options == null) {
|
||||
throw new Error('Must specify options object as first argument')
|
||||
throw new Error('Must specify options object as first argument');
|
||||
}
|
||||
|
||||
let { items, escapeItem } = options
|
||||
let { items, escapeItem } = options;
|
||||
|
||||
if (!Array.isArray(items)) {
|
||||
items = []
|
||||
items = [];
|
||||
}
|
||||
|
||||
this.changeListener = (item) => {
|
||||
this.emit('change', item.id, item.type)
|
||||
}
|
||||
this.emit('change', item.id, item.type);
|
||||
};
|
||||
|
||||
this.windowListeners = {}
|
||||
this.items = {}
|
||||
this.ordereredItems = []
|
||||
this.escapeItem = escapeItem
|
||||
this.windowListeners = {};
|
||||
this.items = {};
|
||||
this.ordereredItems = [];
|
||||
this.escapeItem = escapeItem;
|
||||
|
||||
const registerItem = (item) => {
|
||||
this.items[item.id] = item
|
||||
item.on('change', this.changeListener)
|
||||
this.items[item.id] = item;
|
||||
item.on('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
item.child.ordereredItems.forEach(registerItem)
|
||||
item.child.ordereredItems.forEach(registerItem);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const idSet = new Set();
|
||||
items.forEach((item) => {
|
||||
if (!(item instanceof TouchBarItem)) {
|
||||
throw new Error('Each item must be an instance of TouchBarItem')
|
||||
throw new Error('Each item must be an instance of TouchBarItem');
|
||||
}
|
||||
this.ordereredItems.push(item)
|
||||
registerItem(item)
|
||||
})
|
||||
|
||||
if (!idSet.has(item.id)) {
|
||||
idSet.add(item.id);
|
||||
} else {
|
||||
throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar');
|
||||
}
|
||||
});
|
||||
|
||||
// register in separate loop after all items are validated
|
||||
for (const item of items) {
|
||||
this.ordereredItems.push(item);
|
||||
registerItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
set escapeItem (item) {
|
||||
if (item != null && !(item instanceof TouchBarItem)) {
|
||||
throw new Error('Escape item must be an instance of TouchBarItem')
|
||||
throw new Error('Escape item must be an instance of TouchBarItem');
|
||||
}
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.removeListener('change', this.changeListener)
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
this._escapeItem = item
|
||||
this._escapeItem = item;
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.on('change', this.changeListener)
|
||||
this.escapeItem.on('change', this.changeListener);
|
||||
}
|
||||
this.emit('escape-item-change', item)
|
||||
this.emit('escape-item-change', item);
|
||||
}
|
||||
|
||||
get escapeItem () {
|
||||
return this._escapeItem
|
||||
return this._escapeItem;
|
||||
}
|
||||
|
||||
_addToWindow (window) {
|
||||
const { id } = window
|
||||
const { id } = window;
|
||||
|
||||
// Already added to window
|
||||
if (this.windowListeners.hasOwnProperty(id)) return
|
||||
if (this.windowListeners.hasOwnProperty(id)) return;
|
||||
|
||||
window._touchBar = this
|
||||
window._touchBar = this;
|
||||
|
||||
const changeListener = (itemID) => {
|
||||
window._refreshTouchBarItem(itemID)
|
||||
}
|
||||
this.on('change', changeListener)
|
||||
window._refreshTouchBarItem(itemID);
|
||||
};
|
||||
this.on('change', changeListener);
|
||||
|
||||
const escapeItemListener = (item) => {
|
||||
window._setEscapeTouchBarItem(item != null ? item : {})
|
||||
}
|
||||
this.on('escape-item-change', escapeItemListener)
|
||||
window._setEscapeTouchBarItem(item != null ? item : {});
|
||||
};
|
||||
this.on('escape-item-change', escapeItemListener);
|
||||
|
||||
const interactionListener = (event, itemID, details) => {
|
||||
let item = this.items[itemID]
|
||||
let item = this.items[itemID];
|
||||
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
|
||||
item = this.escapeItem
|
||||
item = this.escapeItem;
|
||||
}
|
||||
if (item != null && item.onInteraction != null) {
|
||||
item.onInteraction(details)
|
||||
item.onInteraction(details);
|
||||
}
|
||||
}
|
||||
window.on('-touch-bar-interaction', interactionListener)
|
||||
};
|
||||
window.on('-touch-bar-interaction', interactionListener);
|
||||
|
||||
const removeListeners = () => {
|
||||
this.removeListener('change', changeListener)
|
||||
this.removeListener('escape-item-change', escapeItemListener)
|
||||
window.removeListener('-touch-bar-interaction', interactionListener)
|
||||
window.removeListener('closed', removeListeners)
|
||||
window._touchBar = null
|
||||
delete this.windowListeners[id]
|
||||
this.removeListener('change', changeListener);
|
||||
this.removeListener('escape-item-change', escapeItemListener);
|
||||
window.removeListener('-touch-bar-interaction', interactionListener);
|
||||
window.removeListener('closed', removeListeners);
|
||||
window._touchBar = null;
|
||||
delete this.windowListeners[id];
|
||||
const unregisterItems = (items) => {
|
||||
for (const item of items) {
|
||||
item.removeListener('change', this.changeListener)
|
||||
item.removeListener('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
unregisterItems(item.child.ordereredItems)
|
||||
unregisterItems(item.child.ordereredItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
unregisterItems(this.ordereredItems)
|
||||
};
|
||||
unregisterItems(this.ordereredItems);
|
||||
if (this.escapeItem) {
|
||||
this.escapeItem.removeListener('change', this.changeListener)
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
}
|
||||
window.once('closed', removeListeners)
|
||||
this.windowListeners[id] = removeListeners
|
||||
};
|
||||
window.once('closed', removeListeners);
|
||||
this.windowListeners[id] = removeListeners;
|
||||
|
||||
window._setTouchBarItems(this.ordereredItems)
|
||||
escapeItemListener(this.escapeItem)
|
||||
window._setTouchBarItems(this.ordereredItems);
|
||||
escapeItemListener(this.escapeItem);
|
||||
}
|
||||
|
||||
_removeFromWindow (window) {
|
||||
const removeListeners = this.windowListeners[window.id]
|
||||
if (removeListeners != null) removeListeners()
|
||||
const removeListeners = this.windowListeners[window.id];
|
||||
if (removeListeners != null) removeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class TouchBarItem extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
this._addImmutableProperty('id', `${nextItemID++}`)
|
||||
this._parents = []
|
||||
super();
|
||||
this._addImmutableProperty('id', `${nextItemID++}`);
|
||||
this._parents = [];
|
||||
}
|
||||
|
||||
_addImmutableProperty (name, value) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function () {
|
||||
return value
|
||||
return value;
|
||||
},
|
||||
set: function () {
|
||||
throw new Error(`Cannot override property ${name}`)
|
||||
throw new Error(`Cannot override property ${name}`);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_addLiveProperty (name, initialValue) {
|
||||
const privateName = `_${name}`
|
||||
this[privateName] = initialValue
|
||||
const privateName = `_${name}`;
|
||||
this[privateName] = initialValue;
|
||||
Object.defineProperty(this, name, {
|
||||
get: function () {
|
||||
return this[privateName]
|
||||
return this[privateName];
|
||||
},
|
||||
set: function (value) {
|
||||
this[privateName] = value
|
||||
this.emit('change', this)
|
||||
this[privateName] = value;
|
||||
this.emit('change', this);
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_addParent (item) {
|
||||
const existing = this._parents.some(test => test.id === item.id)
|
||||
const existing = this._parents.some(test => test.id === item.id);
|
||||
if (!existing) {
|
||||
this._parents.push({
|
||||
id: item.id,
|
||||
type: item.type
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'button')
|
||||
this._addLiveProperty('label', config.label)
|
||||
this._addLiveProperty('accessibilityLabel', config.accessibilityLabel)
|
||||
this._addLiveProperty('backgroundColor', config.backgroundColor)
|
||||
this._addLiveProperty('icon', config.icon)
|
||||
this._addLiveProperty('iconPosition', config.iconPosition)
|
||||
this._addLiveProperty('enabled', typeof config.enabled !== 'boolean' ? true : config.enabled)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'button');
|
||||
this._addLiveProperty('label', config.label);
|
||||
this._addLiveProperty('accessibilityLabel', config.accessibilityLabel);
|
||||
this._addLiveProperty('backgroundColor', config.backgroundColor);
|
||||
this._addLiveProperty('icon', config.icon);
|
||||
this._addLiveProperty('iconPosition', config.iconPosition);
|
||||
this._addLiveProperty('enabled', typeof config.enabled !== 'boolean' ? true : config.enabled);
|
||||
if (typeof config.click === 'function') {
|
||||
this._addImmutableProperty('onInteraction', () => {
|
||||
config.click()
|
||||
})
|
||||
config.click();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'colorpicker')
|
||||
this._addLiveProperty('availableColors', config.availableColors)
|
||||
this._addLiveProperty('selectedColor', config.selectedColor)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'colorpicker');
|
||||
this._addLiveProperty('availableColors', config.availableColors);
|
||||
this._addLiveProperty('selectedColor', config.selectedColor);
|
||||
|
||||
if (typeof config.change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
this._selectedColor = details.color
|
||||
config.change(details.color)
|
||||
})
|
||||
this._selectedColor = details.color;
|
||||
config.change(details.color);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'group')
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
|
||||
this._addLiveProperty('child', defaultChild)
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this))
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'group');
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
|
||||
this._addLiveProperty('child', defaultChild);
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'label')
|
||||
this._addLiveProperty('label', config.label)
|
||||
this._addLiveProperty('accessibilityLabel', config.accessibilityLabel)
|
||||
this._addLiveProperty('textColor', config.textColor)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'label');
|
||||
this._addLiveProperty('label', config.label);
|
||||
this._addLiveProperty('accessibilityLabel', config.accessibilityLabel);
|
||||
this._addLiveProperty('textColor', config.textColor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'popover')
|
||||
this._addLiveProperty('label', config.label)
|
||||
this._addLiveProperty('icon', config.icon)
|
||||
this._addLiveProperty('showCloseButton', config.showCloseButton)
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
|
||||
this._addLiveProperty('child', defaultChild)
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this))
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'popover');
|
||||
this._addLiveProperty('label', config.label);
|
||||
this._addLiveProperty('icon', config.icon);
|
||||
this._addLiveProperty('showCloseButton', config.showCloseButton);
|
||||
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
|
||||
this._addLiveProperty('child', defaultChild);
|
||||
this.child.ordereredItems.forEach((item) => item._addParent(this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'slider')
|
||||
this._addLiveProperty('label', config.label)
|
||||
this._addLiveProperty('minValue', config.minValue)
|
||||
this._addLiveProperty('maxValue', config.maxValue)
|
||||
this._addLiveProperty('value', config.value)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'slider');
|
||||
this._addLiveProperty('label', config.label);
|
||||
this._addLiveProperty('minValue', config.minValue);
|
||||
this._addLiveProperty('maxValue', config.maxValue);
|
||||
this._addLiveProperty('value', config.value);
|
||||
|
||||
if (typeof config.change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
this._value = details.value
|
||||
config.change(details.value)
|
||||
})
|
||||
this._value = details.value;
|
||||
config.change(details.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'spacer')
|
||||
this._addImmutableProperty('size', config.size)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'spacer');
|
||||
this._addImmutableProperty('size', config.size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
this._addImmutableProperty('type', 'segmented_control')
|
||||
this._addLiveProperty('segmentStyle', config.segmentStyle)
|
||||
this._addLiveProperty('segments', config.segments || [])
|
||||
this._addLiveProperty('selectedIndex', config.selectedIndex)
|
||||
this._addLiveProperty('mode', config.mode)
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
this._addImmutableProperty('type', 'segmented_control');
|
||||
this._addLiveProperty('segmentStyle', config.segmentStyle);
|
||||
this._addLiveProperty('segments', config.segments || []);
|
||||
this._addLiveProperty('selectedIndex', config.selectedIndex);
|
||||
this._addLiveProperty('mode', config.mode);
|
||||
|
||||
if (typeof config.change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
this._selectedIndex = details.selectedIndex
|
||||
config.change(details.selectedIndex, details.isSelected)
|
||||
})
|
||||
this._selectedIndex = details.selectedIndex;
|
||||
config.change(details.selectedIndex, details.isSelected);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super()
|
||||
if (config == null) config = {}
|
||||
let { select, highlight } = config
|
||||
this._addImmutableProperty('type', 'scrubber')
|
||||
this._addLiveProperty('items', config.items)
|
||||
this._addLiveProperty('selectedStyle', config.selectedStyle || null)
|
||||
this._addLiveProperty('overlayStyle', config.overlayStyle || null)
|
||||
this._addLiveProperty('showArrowButtons', config.showArrowButtons || false)
|
||||
this._addLiveProperty('mode', config.mode || 'free')
|
||||
super();
|
||||
if (config == null) config = {};
|
||||
let { select, highlight } = config;
|
||||
this._addImmutableProperty('type', 'scrubber');
|
||||
this._addLiveProperty('items', config.items);
|
||||
this._addLiveProperty('selectedStyle', config.selectedStyle || null);
|
||||
this._addLiveProperty('overlayStyle', config.overlayStyle || null);
|
||||
this._addLiveProperty('showArrowButtons', config.showArrowButtons || false);
|
||||
this._addLiveProperty('mode', config.mode || 'free');
|
||||
|
||||
const cont = typeof config.continuous === 'undefined' ? true : config.continuous
|
||||
this._addLiveProperty('continuous', cont)
|
||||
const cont = typeof config.continuous === 'undefined' ? true : config.continuous;
|
||||
this._addLiveProperty('continuous', cont);
|
||||
|
||||
if (typeof select === 'function' || typeof highlight === 'function') {
|
||||
if (select == null) select = () => {}
|
||||
if (highlight == null) highlight = () => {}
|
||||
if (select == null) select = () => {};
|
||||
if (highlight == null) highlight = () => {};
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
if (details.type === 'select' && typeof select === 'function') {
|
||||
select(details.selectedIndex)
|
||||
select(details.selectedIndex);
|
||||
} else if (details.type === 'highlight' && typeof highlight === 'function') {
|
||||
highlight(details.highlightedIndex)
|
||||
highlight(details.highlightedIndex);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TouchBar
|
||||
module.exports = TouchBar;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { deprecate } = require('electron')
|
||||
const { Tray } = process.electronBinding('tray')
|
||||
const { EventEmitter } = require('events');
|
||||
const { deprecate } = require('electron');
|
||||
const { Tray } = process.electronBinding('tray');
|
||||
|
||||
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype);
|
||||
|
||||
module.exports = Tray
|
||||
module.exports = Tray;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
const { View } = process.electronBinding('view')
|
||||
const { EventEmitter } = require('events');
|
||||
const { View } = process.electronBinding('view');
|
||||
|
||||
Object.setPrototypeOf(View.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(View.prototype, EventEmitter.prototype);
|
||||
|
||||
View.prototype._init = function () {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = View
|
||||
module.exports = View;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { LayoutManager } = electron
|
||||
const { BoxLayout } = process.electronBinding('box_layout')
|
||||
const { LayoutManager } = electron;
|
||||
const { BoxLayout } = process.electronBinding('box_layout');
|
||||
|
||||
Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype)
|
||||
Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype);
|
||||
|
||||
BoxLayout.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
LayoutManager.prototype._init.call(this)
|
||||
}
|
||||
LayoutManager.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = BoxLayout
|
||||
module.exports = BoxLayout;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron
|
||||
const { Button } = process.electronBinding('button')
|
||||
const { View } = electron;
|
||||
const { Button } = process.electronBinding('button');
|
||||
|
||||
Object.setPrototypeOf(Button.prototype, View.prototype)
|
||||
Object.setPrototypeOf(Button.prototype, View.prototype);
|
||||
|
||||
Button.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
View.prototype._init.call(this)
|
||||
}
|
||||
View.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = Button
|
||||
module.exports = Button;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { Button } = electron
|
||||
const { LabelButton } = process.electronBinding('label_button')
|
||||
const { Button } = electron;
|
||||
const { LabelButton } = process.electronBinding('label_button');
|
||||
|
||||
Object.setPrototypeOf(LabelButton.prototype, Button.prototype)
|
||||
Object.setPrototypeOf(LabelButton.prototype, Button.prototype);
|
||||
|
||||
LabelButton.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
Button.prototype._init.call(this)
|
||||
}
|
||||
Button.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = LabelButton
|
||||
module.exports = LabelButton;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const { LayoutManager } = process.electronBinding('layout_manager')
|
||||
const { LayoutManager } = process.electronBinding('layout_manager');
|
||||
|
||||
LayoutManager.prototype._init = function () {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LayoutManager
|
||||
module.exports = LayoutManager;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { LabelButton } = electron
|
||||
const { MdTextButton } = process.electronBinding('md_text_button')
|
||||
const { LabelButton } = electron;
|
||||
const { MdTextButton } = process.electronBinding('md_text_button');
|
||||
|
||||
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype)
|
||||
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype);
|
||||
|
||||
MdTextButton.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
LabelButton.prototype._init.call(this)
|
||||
}
|
||||
LabelButton.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = MdTextButton
|
||||
module.exports = MdTextButton;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron
|
||||
const { ResizeArea } = process.electronBinding('resize_area')
|
||||
const { View } = electron;
|
||||
const { ResizeArea } = process.electronBinding('resize_area');
|
||||
|
||||
Object.setPrototypeOf(ResizeArea.prototype, View.prototype)
|
||||
Object.setPrototypeOf(ResizeArea.prototype, View.prototype);
|
||||
|
||||
ResizeArea.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
View.prototype._init.call(this)
|
||||
}
|
||||
View.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = ResizeArea
|
||||
module.exports = ResizeArea;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron
|
||||
const { TextField } = process.electronBinding('text_field')
|
||||
const { View } = electron;
|
||||
const { TextField } = process.electronBinding('text_field');
|
||||
|
||||
Object.setPrototypeOf(TextField.prototype, View.prototype)
|
||||
Object.setPrototypeOf(TextField.prototype, View.prototype);
|
||||
|
||||
TextField.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
View.prototype._init.call(this)
|
||||
}
|
||||
View.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = TextField
|
||||
module.exports = TextField;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron')
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron
|
||||
const { WebContentsView } = process.electronBinding('web_contents_view')
|
||||
const { View } = electron;
|
||||
const { WebContentsView } = process.electronBinding('web_contents_view');
|
||||
|
||||
Object.setPrototypeOf(WebContentsView.prototype, View.prototype)
|
||||
Object.setPrototypeOf(WebContentsView.prototype, View.prototype);
|
||||
|
||||
WebContentsView.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
View.prototype._init.call(this)
|
||||
}
|
||||
View.prototype._init.call(this);
|
||||
};
|
||||
|
||||
module.exports = WebContentsView
|
||||
module.exports = WebContentsView;
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const features = process.electronBinding('features')
|
||||
const { EventEmitter } = require('events')
|
||||
const electron = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
const { app, ipcMain, session, deprecate } = electron
|
||||
const features = process.electronBinding('features');
|
||||
const { EventEmitter } = require('events');
|
||||
const electron = require('electron');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
const { app, ipcMain, session, deprecate } = electron;
|
||||
|
||||
const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager')
|
||||
const NavigationController = require('@electron/internal/browser/navigation-controller')
|
||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
|
||||
const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager');
|
||||
const NavigationController = require('@electron/internal/browser/navigation-controller');
|
||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
|
||||
|
||||
// session is not used here, the purpose is to make sure session is initalized
|
||||
// before the webContents module.
|
||||
// eslint-disable-next-line
|
||||
session
|
||||
|
||||
let nextId = 0
|
||||
let nextId = 0;
|
||||
const getNextId = function () {
|
||||
return ++nextId
|
||||
}
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
// Stock page sizes
|
||||
const PDFPageSizes = {
|
||||
@@ -61,7 +61,7 @@ const PDFPageSizes = {
|
||||
width_microns: 279400,
|
||||
custom_display_name: 'Tabloid'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Default printing setting
|
||||
const defaultPrintingSetting = {
|
||||
@@ -93,83 +93,83 @@ const defaultPrintingSetting = {
|
||||
// 2 = color - see ColorModel in //printing/print_job_constants.h
|
||||
color: 2,
|
||||
collate: true
|
||||
}
|
||||
};
|
||||
|
||||
// JavaScript implementations of WebContents.
|
||||
const binding = process.electronBinding('web_contents')
|
||||
const { WebContents } = binding
|
||||
const binding = process.electronBinding('web_contents');
|
||||
const { WebContents } = binding;
|
||||
|
||||
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype)
|
||||
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype)
|
||||
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype);
|
||||
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype);
|
||||
|
||||
// WebContents::send(channel, args..)
|
||||
// WebContents::sendToAll(channel, args..)
|
||||
WebContents.prototype.send = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument')
|
||||
throw new Error('Missing required channel argument');
|
||||
}
|
||||
|
||||
const internal = false
|
||||
const sendToAll = false
|
||||
const internal = false;
|
||||
const sendToAll = false;
|
||||
|
||||
return this._send(internal, sendToAll, channel, args)
|
||||
}
|
||||
return this._send(internal, sendToAll, channel, args);
|
||||
};
|
||||
|
||||
WebContents.prototype.sendToAll = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument')
|
||||
throw new Error('Missing required channel argument');
|
||||
}
|
||||
|
||||
const internal = false
|
||||
const sendToAll = true
|
||||
const internal = false;
|
||||
const sendToAll = true;
|
||||
|
||||
return this._send(internal, sendToAll, channel, args)
|
||||
}
|
||||
return this._send(internal, sendToAll, channel, args);
|
||||
};
|
||||
|
||||
WebContents.prototype._sendInternal = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument')
|
||||
throw new Error('Missing required channel argument');
|
||||
}
|
||||
|
||||
const internal = true
|
||||
const sendToAll = false
|
||||
const internal = true;
|
||||
const sendToAll = false;
|
||||
|
||||
return this._send(internal, sendToAll, channel, args)
|
||||
}
|
||||
return this._send(internal, sendToAll, channel, args);
|
||||
};
|
||||
WebContents.prototype._sendInternalToAll = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument')
|
||||
throw new Error('Missing required channel argument');
|
||||
}
|
||||
|
||||
const internal = true
|
||||
const sendToAll = true
|
||||
const internal = true;
|
||||
const sendToAll = true;
|
||||
|
||||
return this._send(internal, sendToAll, channel, args)
|
||||
}
|
||||
return this._send(internal, sendToAll, channel, args);
|
||||
};
|
||||
WebContents.prototype.sendToFrame = function (frameId, channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument')
|
||||
throw new Error('Missing required channel argument');
|
||||
} else if (typeof frameId !== 'number') {
|
||||
throw new Error('Missing required frameId argument')
|
||||
throw new Error('Missing required frameId argument');
|
||||
}
|
||||
|
||||
const internal = false
|
||||
const sendToAll = false
|
||||
const internal = false;
|
||||
const sendToAll = false;
|
||||
|
||||
return this._sendToFrame(internal, sendToAll, frameId, channel, args)
|
||||
}
|
||||
return this._sendToFrame(internal, sendToAll, frameId, channel, args);
|
||||
};
|
||||
WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument')
|
||||
throw new Error('Missing required channel argument');
|
||||
} else if (typeof frameId !== 'number') {
|
||||
throw new Error('Missing required frameId argument')
|
||||
throw new Error('Missing required frameId argument');
|
||||
}
|
||||
|
||||
const internal = true
|
||||
const sendToAll = false
|
||||
const internal = true;
|
||||
const sendToAll = false;
|
||||
|
||||
return this._sendToFrame(internal, sendToAll, frameId, channel, args)
|
||||
}
|
||||
return this._sendToFrame(internal, sendToAll, frameId, channel, args);
|
||||
};
|
||||
|
||||
// Following methods are mapped to webFrame.
|
||||
const webFrameMethods = [
|
||||
@@ -177,138 +177,138 @@ const webFrameMethods = [
|
||||
'insertText',
|
||||
'removeInsertedCSS',
|
||||
'setVisualZoomLevelLimits'
|
||||
]
|
||||
];
|
||||
|
||||
for (const method of webFrameMethods) {
|
||||
WebContents.prototype[method] = function (...args) {
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args)
|
||||
}
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
const waitTillCanExecuteJavaScript = async (webContents) => {
|
||||
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return
|
||||
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
webContents.once('did-stop-loading', () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Make sure WebContents::executeJavaScript would run the code only when the
|
||||
// WebContents has been loaded.
|
||||
WebContents.prototype.executeJavaScript = async function (code, hasUserGesture) {
|
||||
await waitTillCanExecuteJavaScript(this)
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture)
|
||||
}
|
||||
await waitTillCanExecuteJavaScript(this);
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture);
|
||||
};
|
||||
WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (code, hasUserGesture) {
|
||||
await waitTillCanExecuteJavaScript(this)
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture)
|
||||
}
|
||||
await waitTillCanExecuteJavaScript(this);
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture);
|
||||
};
|
||||
|
||||
// Translate the options of printToPDF.
|
||||
WebContents.prototype.printToPDF = function (options) {
|
||||
const printSettings = {
|
||||
...defaultPrintingSetting,
|
||||
requestID: getNextId()
|
||||
}
|
||||
};
|
||||
|
||||
if (options.landscape !== undefined) {
|
||||
if (typeof options.landscape !== 'boolean') {
|
||||
const error = new Error('landscape must be a Boolean')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('landscape must be a Boolean');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.landscape = options.landscape
|
||||
printSettings.landscape = options.landscape;
|
||||
}
|
||||
|
||||
if (options.scaleFactor !== undefined) {
|
||||
if (typeof options.scaleFactor !== 'number') {
|
||||
const error = new Error('scaleFactor must be a Number')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('scaleFactor must be a Number');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.scaleFactor = options.scaleFactor
|
||||
printSettings.scaleFactor = options.scaleFactor;
|
||||
}
|
||||
|
||||
if (options.marginsType !== undefined) {
|
||||
if (typeof options.marginsType !== 'number') {
|
||||
const error = new Error('marginsType must be a Number')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('marginsType must be a Number');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.marginsType = options.marginsType
|
||||
printSettings.marginsType = options.marginsType;
|
||||
}
|
||||
|
||||
if (options.printSelectionOnly !== undefined) {
|
||||
if (typeof options.printSelectionOnly !== 'boolean') {
|
||||
const error = new Error('printSelectionOnly must be a Boolean')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('printSelectionOnly must be a Boolean');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.shouldPrintSelectionOnly = options.printSelectionOnly
|
||||
printSettings.shouldPrintSelectionOnly = options.printSelectionOnly;
|
||||
}
|
||||
|
||||
if (options.printBackground !== undefined) {
|
||||
if (typeof options.printBackground !== 'boolean') {
|
||||
const error = new Error('printBackground must be a Boolean')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('printBackground must be a Boolean');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.shouldPrintBackgrounds = options.printBackground
|
||||
printSettings.shouldPrintBackgrounds = options.printBackground;
|
||||
}
|
||||
|
||||
if (options.pageRanges !== undefined) {
|
||||
const pageRanges = options.pageRanges
|
||||
const pageRanges = options.pageRanges;
|
||||
if (!pageRanges.hasOwnProperty('from') || !pageRanges.hasOwnProperty('to')) {
|
||||
const error = new Error(`pageRanges must be an Object with 'from' and 'to' properties`)
|
||||
return Promise.reject(error)
|
||||
const error = new Error(`pageRanges must be an Object with 'from' and 'to' properties`);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (typeof pageRanges.from !== 'number') {
|
||||
const error = new Error('pageRanges.from must be a Number')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('pageRanges.from must be a Number');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (typeof pageRanges.to !== 'number') {
|
||||
const error = new Error('pageRanges.to must be a Number')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('pageRanges.to must be a Number');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// Chromium uses 1-based page ranges, so increment each by 1.
|
||||
printSettings.pageRange = [{
|
||||
from: pageRanges.from + 1,
|
||||
to: pageRanges.to + 1
|
||||
}]
|
||||
}];
|
||||
}
|
||||
|
||||
if (options.headerFooter !== undefined) {
|
||||
const headerFooter = options.headerFooter
|
||||
printSettings.headerFooterEnabled = true
|
||||
const headerFooter = options.headerFooter;
|
||||
printSettings.headerFooterEnabled = true;
|
||||
if (typeof headerFooter === 'object') {
|
||||
if (!headerFooter.url || !headerFooter.title) {
|
||||
const error = new Error('url and title properties are required for headerFooter')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('url and title properties are required for headerFooter');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (typeof headerFooter.title !== 'string') {
|
||||
const error = new Error('headerFooter.title must be a String')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('headerFooter.title must be a String');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.title = headerFooter.title
|
||||
printSettings.title = headerFooter.title;
|
||||
|
||||
if (typeof headerFooter.url !== 'string') {
|
||||
const error = new Error('headerFooter.url must be a String')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('headerFooter.url must be a String');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
printSettings.url = headerFooter.url
|
||||
printSettings.url = headerFooter.url;
|
||||
} else {
|
||||
const error = new Error('headerFooter must be an Object')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('headerFooter must be an Object');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally set size for PDF.
|
||||
if (options.pageSize !== undefined) {
|
||||
const pageSize = options.pageSize
|
||||
const pageSize = options.pageSize;
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
const error = new Error('height and width properties are required for pageSize')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('height and width properties are required for pageSize');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
// Dimensions in Microns
|
||||
// 1 meter = 10^6 microns
|
||||
@@ -317,28 +317,28 @@ WebContents.prototype.printToPDF = function (options) {
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: Math.ceil(pageSize.height),
|
||||
width_microns: Math.ceil(pageSize.width)
|
||||
}
|
||||
};
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
printSettings.mediaSize = PDFPageSizes[pageSize]
|
||||
printSettings.mediaSize = PDFPageSizes[pageSize];
|
||||
} else {
|
||||
const error = new Error(`Unsupported pageSize: ${pageSize}`)
|
||||
return Promise.reject(error)
|
||||
const error = new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
} else {
|
||||
printSettings.mediaSize = PDFPageSizes['A4']
|
||||
printSettings.mediaSize = PDFPageSizes['A4'];
|
||||
}
|
||||
|
||||
// Chromium expects this in a 0-100 range number, not as float
|
||||
printSettings.scaleFactor = Math.ceil(printSettings.scaleFactor) % 100
|
||||
printSettings.scaleFactor = Math.ceil(printSettings.scaleFactor) % 100;
|
||||
// PrinterType enum from //printing/print_job_constants.h
|
||||
printSettings.printerType = 2
|
||||
printSettings.printerType = 2;
|
||||
if (features.isPrintingEnabled()) {
|
||||
return this._printToPDF(printSettings)
|
||||
return this._printToPDF(printSettings);
|
||||
} else {
|
||||
const error = new Error('Printing feature is disabled')
|
||||
return Promise.reject(error)
|
||||
const error = new Error('Printing feature is disabled');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WebContents.prototype.print = function (options = {}, callback) {
|
||||
// TODO(codebytere): deduplicate argument sanitization by moving rest of
|
||||
@@ -346,10 +346,10 @@ WebContents.prototype.print = function (options = {}, callback) {
|
||||
if (typeof options === 'object') {
|
||||
// Optionally set size for PDF.
|
||||
if (options.pageSize !== undefined) {
|
||||
const pageSize = options.pageSize
|
||||
const pageSize = options.pageSize;
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize')
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
options.mediaSize = {
|
||||
@@ -357,40 +357,40 @@ WebContents.prototype.print = function (options = {}, callback) {
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: Math.ceil(pageSize.height),
|
||||
width_microns: Math.ceil(pageSize.width)
|
||||
}
|
||||
};
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
options.mediaSize = PDFPageSizes[pageSize]
|
||||
options.mediaSize = PDFPageSizes[pageSize];
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`)
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (features.isPrintingEnabled()) {
|
||||
if (callback) {
|
||||
this._print(options, callback)
|
||||
this._print(options, callback);
|
||||
} else {
|
||||
this._print(options)
|
||||
this._print(options);
|
||||
}
|
||||
} else {
|
||||
console.error('Error: Printing feature is disabled.')
|
||||
console.error('Error: Printing feature is disabled.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WebContents.prototype.getPrinters = function () {
|
||||
if (features.isPrintingEnabled()) {
|
||||
return this._getPrinters()
|
||||
return this._getPrinters();
|
||||
} else {
|
||||
console.error('Error: Printing feature is disabled.')
|
||||
return []
|
||||
console.error('Error: Printing feature is disabled.');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WebContents.prototype.loadFile = function (filePath, options = {}) {
|
||||
if (typeof filePath !== 'string') {
|
||||
throw new Error('Must pass filePath as a string')
|
||||
throw new Error('Must pass filePath as a string');
|
||||
}
|
||||
const { query, search, hash } = options
|
||||
const { query, search, hash } = options;
|
||||
|
||||
return this.loadURL(url.format({
|
||||
protocol: 'file',
|
||||
@@ -399,99 +399,99 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
|
||||
query,
|
||||
search,
|
||||
hash
|
||||
}))
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const addReplyToEvent = (event) => {
|
||||
event.reply = (...args) => {
|
||||
event.sender.sendToFrame(event.frameId, ...args)
|
||||
}
|
||||
}
|
||||
event.sender.sendToFrame(event.frameId, ...args);
|
||||
};
|
||||
};
|
||||
|
||||
const addReplyInternalToEvent = (event) => {
|
||||
Object.defineProperty(event, '_replyInternal', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: (...args) => {
|
||||
event.sender._sendToFrameInternal(event.frameId, ...args)
|
||||
event.sender._sendToFrameInternal(event.frameId, ...args);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const addReturnValueToEvent = (event) => {
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: (value) => event.sendReply([value]),
|
||||
get: () => {}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Add JavaScript wrappers for WebContents class.
|
||||
WebContents.prototype._init = function () {
|
||||
// The navigation controller.
|
||||
NavigationController.call(this, this)
|
||||
NavigationController.call(this, this);
|
||||
|
||||
// Every remote callback from renderer process would add a listener to the
|
||||
// render-view-deleted event, so ignore the listeners warning.
|
||||
this.setMaxListeners(0)
|
||||
this.setMaxListeners(0);
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message', function (event, internal, channel, args) {
|
||||
if (internal) {
|
||||
addReplyInternalToEvent(event)
|
||||
ipcMainInternal.emit(channel, event, ...args)
|
||||
addReplyInternalToEvent(event);
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
} else {
|
||||
addReplyToEvent(event)
|
||||
this.emit('ipc-message', event, channel, ...args)
|
||||
ipcMain.emit(channel, event, ...args)
|
||||
addReplyToEvent(event);
|
||||
this.emit('ipc-message', event, channel, ...args);
|
||||
ipcMain.emit(channel, event, ...args);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.on('-ipc-invoke', function (event, internal, channel, args) {
|
||||
event._reply = (result) => event.sendReply({ result })
|
||||
event._reply = (result) => event.sendReply({ result });
|
||||
event._throw = (error) => {
|
||||
console.error(`Error occurred in handler for '${channel}':`, error)
|
||||
event.sendReply({ error: error.toString() })
|
||||
}
|
||||
const target = internal ? ipcMainInternal : ipcMain
|
||||
console.error(`Error occurred in handler for '${channel}':`, error);
|
||||
event.sendReply({ error: error.toString() });
|
||||
};
|
||||
const target = internal ? ipcMainInternal : ipcMain;
|
||||
if (target._invokeHandlers.has(channel)) {
|
||||
target._invokeHandlers.get(channel)(event, ...args)
|
||||
target._invokeHandlers.get(channel)(event, ...args);
|
||||
} else {
|
||||
event._throw(`No handler registered for '${channel}'`)
|
||||
event._throw(`No handler registered for '${channel}'`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.on('-ipc-message-sync', function (event, internal, channel, args) {
|
||||
addReturnValueToEvent(event)
|
||||
addReturnValueToEvent(event);
|
||||
if (internal) {
|
||||
addReplyInternalToEvent(event)
|
||||
ipcMainInternal.emit(channel, event, ...args)
|
||||
addReplyInternalToEvent(event);
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
} else {
|
||||
addReplyToEvent(event)
|
||||
this.emit('ipc-message-sync', event, channel, ...args)
|
||||
ipcMain.emit(channel, event, ...args)
|
||||
addReplyToEvent(event);
|
||||
this.emit('ipc-message-sync', event, channel, ...args);
|
||||
ipcMain.emit(channel, event, ...args);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Handle context menu action request from pepper plugin.
|
||||
this.on('pepper-context-menu', function (event, params, callback) {
|
||||
// Access Menu via electron.Menu to prevent circular require.
|
||||
const menu = electron.Menu.buildFromTemplate(params.menu)
|
||||
const menu = electron.Menu.buildFromTemplate(params.menu);
|
||||
menu.popup({
|
||||
window: event.sender.getOwnerBrowserWindow(),
|
||||
x: params.x,
|
||||
y: params.y,
|
||||
callback
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
this.on('crashed', (event, ...args) => {
|
||||
app.emit('renderer-process-crashed', event, this, ...args)
|
||||
})
|
||||
app.emit('renderer-process-crashed', event, this, ...args);
|
||||
});
|
||||
|
||||
// The devtools requests the webContents to reload.
|
||||
this.on('devtools-reload-page', function () {
|
||||
this.reload()
|
||||
})
|
||||
this.reload();
|
||||
});
|
||||
|
||||
// Handle window.open for BrowserWindow and BrowserView.
|
||||
if (['browserView', 'window'].includes(this.getType())) {
|
||||
@@ -503,9 +503,9 @@ WebContents.prototype._init = function () {
|
||||
show: true,
|
||||
width: 800,
|
||||
height: 600
|
||||
}
|
||||
internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData)
|
||||
})
|
||||
};
|
||||
internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData);
|
||||
});
|
||||
|
||||
// Create a new browser window for the native implementation of
|
||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
||||
@@ -513,8 +513,8 @@ WebContents.prototype._init = function () {
|
||||
userGesture, left, top, width, height, url, frameName) => {
|
||||
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
|
||||
disposition !== 'background-tab')) {
|
||||
event.preventDefault()
|
||||
return
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
@@ -524,54 +524,74 @@ WebContents.prototype._init = function () {
|
||||
width: width || 800,
|
||||
height: height || 600,
|
||||
webContents
|
||||
}
|
||||
const referrer = { url: '', policy: 'default' }
|
||||
internalWindowOpen(event, url, referrer, frameName, disposition, options)
|
||||
})
|
||||
};
|
||||
const referrer = { url: '', policy: 'default' };
|
||||
internalWindowOpen(event, url, referrer, frameName, disposition, options);
|
||||
});
|
||||
}
|
||||
|
||||
this.on('login', (event, ...args) => {
|
||||
app.emit('login', event, this, ...args)
|
||||
})
|
||||
app.emit('login', event, this, ...args);
|
||||
});
|
||||
|
||||
const event = process.electronBinding('event').createEmpty()
|
||||
app.emit('web-contents-created', event, this)
|
||||
}
|
||||
const event = process.electronBinding('event').createEmpty();
|
||||
app.emit('web-contents-created', event, this);
|
||||
|
||||
// Deprecations
|
||||
deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted')
|
||||
deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent')
|
||||
deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel')
|
||||
deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor')
|
||||
deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate')
|
||||
// Properties
|
||||
|
||||
Object.defineProperty(this, 'audioMuted', {
|
||||
get: () => this.isAudioMuted(),
|
||||
set: (muted) => this.setAudioMuted(muted)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'userAgent', {
|
||||
get: () => this.getUserAgent(),
|
||||
set: (agent) => this.setUserAgent(agent)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'zoomLevel', {
|
||||
get: () => this.getZoomLevel(),
|
||||
set: (level) => this.setZoomLevel(level)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'zoomFactor', {
|
||||
get: () => this.getZoomFactor(),
|
||||
set: (factor) => this.setZoomFactor(factor)
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'frameRate', {
|
||||
get: () => this.getFrameRate(),
|
||||
set: (rate) => this.setFrameRate(rate)
|
||||
});
|
||||
};
|
||||
|
||||
// JavaScript wrapper of Debugger.
|
||||
const { Debugger } = process.electronBinding('debugger')
|
||||
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)
|
||||
const { Debugger } = process.electronBinding('debugger');
|
||||
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype);
|
||||
|
||||
// Public APIs.
|
||||
module.exports = {
|
||||
create (options = {}) {
|
||||
return binding.create(options)
|
||||
return binding.create(options);
|
||||
},
|
||||
|
||||
fromId (id) {
|
||||
return binding.fromId(id)
|
||||
return binding.fromId(id);
|
||||
},
|
||||
|
||||
getFocusedWebContents () {
|
||||
let focused = null
|
||||
let focused = null;
|
||||
for (const contents of binding.getAllWebContents()) {
|
||||
if (!contents.isFocused()) continue
|
||||
if (focused == null) focused = contents
|
||||
if (!contents.isFocused()) continue;
|
||||
if (focused == null) focused = contents;
|
||||
// Return webview web contents which may be embedded inside another
|
||||
// web contents that is also reporting as focused
|
||||
if (contents.getType() === 'webview') return contents
|
||||
if (contents.getType() === 'webview') return contents;
|
||||
}
|
||||
return focused
|
||||
return focused;
|
||||
},
|
||||
|
||||
getAllWebContents () {
|
||||
return binding.getAllWebContents()
|
||||
return binding.getAllWebContents();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user