mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
98 Commits
v11.0.0-ni
...
v11.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a342ab7ce7 | ||
|
|
c3258d6c4e | ||
|
|
b5cd9ce0b3 | ||
|
|
e7fc19c98e | ||
|
|
948cc08265 | ||
|
|
d93bb34ac4 | ||
|
|
01023435c0 | ||
|
|
06cb550c75 | ||
|
|
1b175a0609 | ||
|
|
75372e933f | ||
|
|
f859d4ce44 | ||
|
|
9c37e12e2d | ||
|
|
834408a50f | ||
|
|
b6321cc22d | ||
|
|
38fafe4986 | ||
|
|
e6cf5906f6 | ||
|
|
362da77c0a | ||
|
|
3f45fc24bb | ||
|
|
75c3a426ea | ||
|
|
4e15273b9f | ||
|
|
c6c986648f | ||
|
|
bac232689d | ||
|
|
76e3ee6fe6 | ||
|
|
4829b0f816 | ||
|
|
a612fb00c8 | ||
|
|
2f02a469f4 | ||
|
|
b39a5b71fe | ||
|
|
91f5837344 | ||
|
|
5fc354df73 | ||
|
|
ee31611e84 | ||
|
|
071c5930b9 | ||
|
|
5cfe956fe1 | ||
|
|
b500294c1d | ||
|
|
3b250b649b | ||
|
|
2162eeb609 | ||
|
|
00fc8066de | ||
|
|
6b1de271a7 | ||
|
|
ec838cfc11 | ||
|
|
6fa3406912 | ||
|
|
b57f5086ef | ||
|
|
734753dd7a | ||
|
|
10bf50e1fd | ||
|
|
5795e59352 | ||
|
|
2fb14f53fe | ||
|
|
d4a4269989 | ||
|
|
cd805544d1 | ||
|
|
95dd81bd68 | ||
|
|
a76e74ac3f | ||
|
|
74e7f2773f | ||
|
|
b43e601b83 | ||
|
|
589968f9fc | ||
|
|
a12602dbc3 | ||
|
|
77eea08a54 | ||
|
|
86b441d599 | ||
|
|
509740c357 | ||
|
|
bcba4baa85 | ||
|
|
f0953902db | ||
|
|
e5cb22b7f9 | ||
|
|
682f78b9a8 | ||
|
|
8f5280a821 | ||
|
|
fa1323d6cd | ||
|
|
f146a164af | ||
|
|
6f53457a17 | ||
|
|
45551f6bf2 | ||
|
|
14bbc07f1e | ||
|
|
3806f4cb64 | ||
|
|
eb7c04c7c4 | ||
|
|
f649e604be | ||
|
|
4c3da359fc | ||
|
|
3f37ff87d2 | ||
|
|
24fb498fd3 | ||
|
|
d8be385d4b | ||
|
|
e4180b3a3a | ||
|
|
eb550c57d3 | ||
|
|
cdd5bb1eba | ||
|
|
9c8cdd63fd | ||
|
|
3f54f240bd | ||
|
|
36900df7d9 | ||
|
|
ce87a7e69f | ||
|
|
990a6f8b6c | ||
|
|
e002f7315d | ||
|
|
8bbdc224ac | ||
|
|
36bd940bc3 | ||
|
|
f0a0e10bd1 | ||
|
|
b02748e607 | ||
|
|
1719f073c1 | ||
|
|
91cdedfea9 | ||
|
|
cbb47570bd | ||
|
|
e18f508e66 | ||
|
|
6c4017ff45 | ||
|
|
5737fda154 | ||
|
|
8847517798 | ||
|
|
c9aa68e32c | ||
|
|
42f716bbc6 | ||
|
|
ef9addcb92 | ||
|
|
dcb56923cb | ||
|
|
eb6616e4e9 | ||
|
|
9bd0fc5348 |
@@ -37,10 +37,18 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-osx-publish-arm64:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-mas-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-mas-publish-arm64:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -77,12 +85,17 @@ machine-linux-2xlarge: &machine-linux-2xlarge
|
||||
|
||||
machine-mac: &machine-mac
|
||||
macos:
|
||||
xcode: "11.1.0"
|
||||
xcode: "11.5.0"
|
||||
|
||||
machine-mac-large: &machine-mac-large
|
||||
resource_class: large
|
||||
macos:
|
||||
xcode: "11.1.0"
|
||||
xcode: "11.5.0"
|
||||
|
||||
machine-mac-large-arm: &machine-mac-large-arm
|
||||
resource_class: large
|
||||
macos:
|
||||
xcode: "12.0.0-UA"
|
||||
|
||||
# Build configurations options.
|
||||
env-testing-build: &env-testing-build
|
||||
@@ -123,6 +136,10 @@ env-arm: &env-arm
|
||||
BUILD_NATIVE_MKSNAPSHOT: 1
|
||||
TARGET_ARCH: arm
|
||||
|
||||
env-apple-silicon: &env-apple-silicon
|
||||
GN_EXTRA_ARGS: 'target_cpu = "arm64"'
|
||||
TARGET_ARCH: arm64
|
||||
|
||||
env-arm64: &env-arm64
|
||||
GN_EXTRA_ARGS: 'target_cpu = "arm64" fatal_linker_warnings = false enable_linux_installer = false'
|
||||
MKSNAPSHOT_TOOLCHAIN: //build/toolchain/linux:clang_arm64
|
||||
@@ -133,6 +150,11 @@ env-mas: &env-mas
|
||||
GN_EXTRA_ARGS: 'is_mas_build = true'
|
||||
MAS_BUILD: 'true'
|
||||
|
||||
env-mas-apple-silicon: &env-mas-apple-silicon
|
||||
GN_EXTRA_ARGS: 'target_cpu = "arm64" is_mas_build = true'
|
||||
MAS_BUILD: 'true'
|
||||
TARGET_ARCH: arm64
|
||||
|
||||
# Misc build configuration options.
|
||||
env-enable-sccache: &env-enable-sccache
|
||||
USE_SCCACHE: true
|
||||
@@ -312,14 +334,25 @@ step-get-more-space-on-mac: &step-get-more-space-on-mac
|
||||
name: Free up space on MacOS
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
sudo rm -rf /Library/Developer/CoreSimulator
|
||||
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform
|
||||
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
|
||||
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform
|
||||
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform
|
||||
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform
|
||||
sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform
|
||||
sudo mkdir -p $TMPDIR/del-target
|
||||
tmpify() {
|
||||
if [ -d "$1" ]; then
|
||||
sudo mv $1 $TMPDIR/del-target/$(echo $1|shasum -a 256|head -n1|cut -d " " -f1)
|
||||
fi
|
||||
}
|
||||
tmpify /Library/Developer/CoreSimulator
|
||||
tmpify $(xcode-select -p)/Platforms/AppleTVOS.platform
|
||||
tmpify $(xcode-select -p)/Platforms/iPhoneOS.platform
|
||||
tmpify $(xcode-select -p)/Platforms/WatchOS.platform
|
||||
tmpify $(xcode-select -p)/Platforms/WatchSimulator.platform
|
||||
tmpify $(xcode-select -p)/Platforms/AppleTVSimulator.platform
|
||||
tmpify $(xcode-select -p)/Platforms/iPhoneSimulator.platform
|
||||
tmpify ~/.rubies
|
||||
tmpify ~/Library/Caches/Homebrew
|
||||
tmpify /usr/local/Homebrew
|
||||
sudo rm -rf $TMPDIR/del-target
|
||||
fi
|
||||
background: true
|
||||
|
||||
# On macOS delete all .git directories under src/ expect for
|
||||
# third_party/angle/ because of build time generation of file
|
||||
@@ -712,8 +745,6 @@ 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
|
||||
@@ -742,9 +773,13 @@ step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
|
||||
elif [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
export MKSNAPSHOT_PATH="clang_x64_v8_arm64"
|
||||
fi
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/mksnapshot" out/Default
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.so" out/Default
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/mksnapshot" out/Default
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/v8_context_snapshot_generator" out/Default
|
||||
if [ "`uname`" == "Linux" ]; then
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.so" out/Default
|
||||
elif [ "`uname`" == "Darwin" ]; then
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.dylib" out/Default
|
||||
fi
|
||||
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --create-snapshot-only
|
||||
mkdir cross-arch-snapshots
|
||||
cp out/Default-mksnapshot-test/*.bin cross-arch-snapshots
|
||||
@@ -763,8 +798,13 @@ step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test
|
||||
# Only run for non-fork prs
|
||||
if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then
|
||||
#Trigger VSTS job, passing along CircleCI job number and branch to build
|
||||
echo "Triggering electron-$TARGET_ARCH-testing build on VSTS"
|
||||
node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
echo "Triggering electron-arm2-testing build on Azure DevOps"
|
||||
node electron/script/release/ci-release-build.js --job=electron-arm2-testing --ci=DevOps --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
|
||||
else
|
||||
echo "Triggering electron-$TARGET_ARCH-testing build on VSTS"
|
||||
node electron/script/release/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH
|
||||
fi
|
||||
fi
|
||||
|
||||
step-maybe-generate-typescript-defs: &step-maybe-generate-typescript-defs
|
||||
@@ -812,8 +852,13 @@ step-touch-sync-done: &step-touch-sync-done
|
||||
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- v7-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
- v8-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache
|
||||
step-maybe-restore-src-cache-marker: &step-maybe-restore-src-cache-marker
|
||||
restore_cache:
|
||||
keys:
|
||||
- v1-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache marker
|
||||
|
||||
# Restore exact or closest git cache based on the hash of DEPS and .circle-sync-done
|
||||
# If the src cache was restored above then this will match an empty cache
|
||||
@@ -890,9 +935,18 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c
|
||||
step-save-src-cache: &step-save-src-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- /portal
|
||||
key: v7-src-cache-{{ checksum "/portal/src/electron/.depshash" }}
|
||||
- /var/portal
|
||||
key: v8-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
step-make-src-cache-marker: &step-make-src-cache-marker
|
||||
run:
|
||||
name: Making src cache marker
|
||||
command: touch .src-cache-marker
|
||||
step-save-src-cache-marker: &step-save-src-cache-marker
|
||||
save_cache:
|
||||
paths:
|
||||
- .src-cache-marker
|
||||
key: v1-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
|
||||
# Check for doc only change
|
||||
step-check-for-doc-only-change: &step-check-for-doc-only-change
|
||||
@@ -998,7 +1052,8 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
|
||||
- *step-generate-deps-hash
|
||||
- *step-touch-sync-done
|
||||
- maybe-restore-portaled-src-cache
|
||||
- maybe-restore-portaled-src-cache:
|
||||
halt-if-successful: true
|
||||
- *step-maybe-restore-git-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
@@ -1019,10 +1074,12 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache
|
||||
- run:
|
||||
name: Move src folder to the cross-OS portal
|
||||
command: |
|
||||
sudo mkdir -p /portal
|
||||
sudo chown -R $(id -u):$(id -g) /portal
|
||||
mv ./src /portal
|
||||
sudo mkdir -p /var/portal
|
||||
sudo chown -R $(id -u):$(id -g) /var/portal
|
||||
mv ./src /var/portal
|
||||
- *step-save-src-cache
|
||||
- *step-make-src-cache-marker
|
||||
- *step-save-src-cache-marker
|
||||
|
||||
steps-electron-gn-check: &steps-electron-gn-check
|
||||
steps:
|
||||
@@ -1043,8 +1100,8 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-install-gnutar-on-mac
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-generate-deps-hash
|
||||
- *step-touch-sync-done
|
||||
- maybe-restore-portaled-src-cache
|
||||
@@ -1226,20 +1283,34 @@ chromium-upgrade-branches: &chromium-upgrade-branches
|
||||
# Command Aliases
|
||||
commands:
|
||||
maybe-restore-portaled-src-cache:
|
||||
parameters:
|
||||
halt-if-successful:
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- run:
|
||||
name: Prepare for cross-OS sync restore
|
||||
command: |
|
||||
sudo mkdir -p /portal
|
||||
sudo chown -R $(id -u):$(id -g) /portal
|
||||
sudo mkdir -p /var/portal
|
||||
sudo chown -R $(id -u):$(id -g) /var/portal
|
||||
- when:
|
||||
condition: << parameters.halt-if-successful >>
|
||||
steps:
|
||||
- *step-maybe-restore-src-cache-marker
|
||||
- run:
|
||||
name: Halt the job early if the src cache exists
|
||||
command: |
|
||||
if [ -f ".src-cache-marker" ]; then
|
||||
circleci-agent step halt
|
||||
fi
|
||||
- *step-maybe-restore-src-cache
|
||||
- run:
|
||||
name: Fix the src cache restore point on macOS
|
||||
command: |
|
||||
if [ -d "/portal/src" ]; then
|
||||
if [ -d "/var/portal/src" ]; then
|
||||
echo Relocating Cache
|
||||
rm -rf src
|
||||
mv /portal/src ./
|
||||
mv /var/portal/src ./
|
||||
fi
|
||||
checkout-from-cache:
|
||||
steps:
|
||||
@@ -1702,7 +1773,7 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
@@ -1714,7 +1785,7 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
@@ -1766,7 +1837,7 @@ jobs:
|
||||
<<: *env-32bit-release
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
@@ -1780,7 +1851,7 @@ jobs:
|
||||
<<: *env-32bit-release
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
@@ -1833,7 +1904,7 @@ jobs:
|
||||
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:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
@@ -1847,7 +1918,7 @@ jobs:
|
||||
<<: *env-32bit-release
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
@@ -1907,7 +1978,7 @@ jobs:
|
||||
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:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
@@ -1920,11 +1991,11 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
osx-testing:
|
||||
osx-testing-x64:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -1939,22 +2010,14 @@ jobs:
|
||||
checkout-and-assume-cache: true
|
||||
attach: false
|
||||
|
||||
osx-testing-gn-check:
|
||||
osx-testing-x64-gn-check:
|
||||
<<: *machine-mac
|
||||
environment:
|
||||
<<: *env-machine-mac
|
||||
<<: *env-testing-build
|
||||
<<: *steps-electron-gn-check
|
||||
|
||||
osx-chromedriver:
|
||||
<<: *machine-mac
|
||||
environment:
|
||||
<<: *env-machine-mac
|
||||
<<: *env-release-build
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *steps-chromedriver-build
|
||||
|
||||
osx-release:
|
||||
osx-release-x64:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -1968,7 +2031,7 @@ jobs:
|
||||
checkout-and-assume-cache: true
|
||||
attach: false
|
||||
|
||||
osx-publish:
|
||||
osx-publish-x64:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
@@ -1977,11 +2040,25 @@ jobs:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
osx-publish-skip-checkout:
|
||||
osx-publish-arm64:
|
||||
<<: *machine-mac-large-arm
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-apple-silicon
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
osx-publish-x64-skip-checkout:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
@@ -1989,11 +2066,40 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
mas-testing:
|
||||
osx-publish-arm64-skip-checkout:
|
||||
<<: *machine-mac-large-arm
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-apple-silicon
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
osx-testing-arm64:
|
||||
<<: *machine-mac-large-arm
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-testing-build
|
||||
<<: *env-ninja-status
|
||||
<<: *env-macos-build
|
||||
<<: *env-apple-silicon
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
attach: false
|
||||
|
||||
mas-testing-x64:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2009,7 +2115,7 @@ jobs:
|
||||
checkout-and-assume-cache: true
|
||||
attach: false
|
||||
|
||||
mas-testing-gn-check:
|
||||
mas-testing-x64-gn-check:
|
||||
<<: *machine-mac
|
||||
environment:
|
||||
<<: *env-machine-mac
|
||||
@@ -2042,11 +2148,25 @@ jobs:
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
mas-publish-skip-checkout:
|
||||
mas-publish-arm64:
|
||||
<<: *machine-mac-large-arm
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas-apple-silicon
|
||||
<<: *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:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
mas-publish-x64-skip-checkout:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
@@ -2055,10 +2175,39 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
mas-publish-arm64-skip-checkout:
|
||||
<<: *machine-mac-large-arm
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas-apple-silicon
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
mas-testing-arm64:
|
||||
<<: *machine-mac-large-arm
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
<<: *env-testing-build
|
||||
<<: *env-ninja-status
|
||||
<<: *env-macos-build
|
||||
<<: *env-mas-apple-silicon
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac'
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
attach: false
|
||||
|
||||
# Layer 3: Tests.
|
||||
linux-x64-unittests:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -2182,7 +2331,7 @@ jobs:
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *steps-verify-ffmpeg
|
||||
|
||||
osx-testing-tests:
|
||||
osx-testing-x64-tests:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2190,7 +2339,7 @@ jobs:
|
||||
parallelism: 2
|
||||
<<: *steps-tests
|
||||
|
||||
osx-release-tests:
|
||||
osx-release-x64-tests:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2205,7 +2354,7 @@ jobs:
|
||||
<<: *env-send-slack-notifications
|
||||
<<: *steps-verify-ffmpeg
|
||||
|
||||
mas-testing-tests:
|
||||
mas-testing-x64-tests:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2269,7 +2418,7 @@ jobs:
|
||||
steps:
|
||||
- *step-maybe-notify-slack-success
|
||||
|
||||
osx-release-summary:
|
||||
osx-release-x64-summary:
|
||||
<<: *machine-mac
|
||||
environment:
|
||||
<<: *env-machine-mac
|
||||
@@ -2334,7 +2483,7 @@ workflows:
|
||||
publish-osx:
|
||||
when: << pipeline.parameters.run-osx-publish >>
|
||||
jobs:
|
||||
- osx-publish:
|
||||
- osx-publish-x64:
|
||||
context: release-env
|
||||
|
||||
publish-mas:
|
||||
@@ -2343,14 +2492,32 @@ workflows:
|
||||
- mas-publish:
|
||||
context: release-env
|
||||
|
||||
publish-osx-arm64:
|
||||
when: << pipeline.parameters.run-osx-publish-arm64 >>
|
||||
jobs:
|
||||
- osx-publish-arm64:
|
||||
context: release-env
|
||||
|
||||
publish-mas-arm64:
|
||||
when: << pipeline.parameters.run-mas-publish-arm64 >>
|
||||
jobs:
|
||||
- mas-publish-arm64:
|
||||
context: release-env
|
||||
|
||||
publish-macos:
|
||||
when: << pipeline.parameters.run-macos-publish >>
|
||||
jobs:
|
||||
- mac-checkout
|
||||
- osx-publish-skip-checkout:
|
||||
- osx-publish-x64-skip-checkout:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mas-publish-skip-checkout:
|
||||
- mas-publish-x64-skip-checkout:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- osx-publish-arm64-skip-checkout:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mas-publish-arm64-skip-checkout:
|
||||
requires:
|
||||
- mac-checkout
|
||||
|
||||
@@ -2405,29 +2572,37 @@ workflows:
|
||||
- mac-checkout-fast
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
- osx-testing:
|
||||
- osx-testing-x64:
|
||||
requires:
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
- osx-testing-gn-check:
|
||||
- osx-testing-x64-gn-check:
|
||||
requires:
|
||||
- mac-checkout-fast
|
||||
|
||||
- osx-testing-tests:
|
||||
- osx-testing-x64-tests:
|
||||
requires:
|
||||
- osx-testing
|
||||
- osx-testing-x64
|
||||
|
||||
- mas-testing:
|
||||
- osx-testing-arm64:
|
||||
requires:
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
- mas-testing-gn-check:
|
||||
- mas-testing-x64:
|
||||
requires:
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
- mas-testing-x64-gn-check:
|
||||
requires:
|
||||
- mac-checkout-fast
|
||||
|
||||
- mas-testing-tests:
|
||||
- mas-testing-x64-tests:
|
||||
requires:
|
||||
- mas-testing
|
||||
- mas-testing-x64
|
||||
|
||||
- mas-testing-arm64:
|
||||
requires:
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
nightly-linux-release-test:
|
||||
triggers:
|
||||
@@ -2491,19 +2666,19 @@ workflows:
|
||||
- mac-checkout-fast
|
||||
- mac-checkout-and-save-cache
|
||||
|
||||
- osx-release:
|
||||
- osx-release-x64:
|
||||
requires:
|
||||
- mac-checkout-and-save-cache
|
||||
- osx-release-tests:
|
||||
- osx-release-x64-tests:
|
||||
requires:
|
||||
- osx-release
|
||||
- osx-release-x64
|
||||
- osx-verify-ffmpeg:
|
||||
requires:
|
||||
- osx-release
|
||||
- osx-release-summary:
|
||||
- osx-release-x64
|
||||
- osx-release-x64-summary:
|
||||
requires:
|
||||
- osx-release
|
||||
- osx-release-tests
|
||||
- osx-release-x64
|
||||
- osx-release-x64-tests
|
||||
- osx-verify-ffmpeg
|
||||
|
||||
- mas-release:
|
||||
|
||||
@@ -28,10 +28,12 @@
|
||||
},
|
||||
"globals": {
|
||||
"standardScheme": "readonly",
|
||||
"globalThis": "readonly",
|
||||
"BUILDFLAG": "readonly",
|
||||
"ENABLE_DESKTOP_CAPTURER": "readonly",
|
||||
"ENABLE_REMOTE_MODULE": "readonly",
|
||||
"ENABLE_VIEWS_API": "readonly"
|
||||
"ENABLE_VIEWS_API": "readonly",
|
||||
"BigInt": "readonly"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
||||
38
BUILD.gn
38
BUILD.gn
@@ -94,6 +94,15 @@ npm_action("build_electron_definitions") {
|
||||
outputs = [ "$target_gen_dir/tsc/typings/electron.d.ts" ]
|
||||
}
|
||||
|
||||
webpack_build("electron_asar_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.asar_bundle_deps
|
||||
|
||||
config_file = "//electron/build/webpack/webpack.config.asar.js"
|
||||
out_file = "$target_gen_dir/js2c/asar_bundle.js"
|
||||
}
|
||||
|
||||
webpack_build("electron_browser_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
@@ -139,25 +148,18 @@ webpack_build("electron_isolated_renderer_bundle") {
|
||||
out_file = "$target_gen_dir/js2c/isolated_bundle.js"
|
||||
}
|
||||
|
||||
copy("electron_js2c_copy") {
|
||||
sources = [
|
||||
"lib/common/asar.js",
|
||||
"lib/common/asar_init.js",
|
||||
]
|
||||
outputs = [ "$target_gen_dir/js2c/{{source_file_part}}" ]
|
||||
}
|
||||
|
||||
action("electron_js2c") {
|
||||
deps = [
|
||||
":electron_asar_bundle",
|
||||
":electron_browser_bundle",
|
||||
":electron_isolated_renderer_bundle",
|
||||
":electron_js2c_copy",
|
||||
":electron_renderer_bundle",
|
||||
":electron_sandboxed_renderer_bundle",
|
||||
":electron_worker_bundle",
|
||||
]
|
||||
|
||||
webpack_sources = [
|
||||
sources = [
|
||||
"$target_gen_dir/js2c/asar_bundle.js",
|
||||
"$target_gen_dir/js2c/browser_init.js",
|
||||
"$target_gen_dir/js2c/isolated_bundle.js",
|
||||
"$target_gen_dir/js2c/renderer_init.js",
|
||||
@@ -165,11 +167,6 @@ action("electron_js2c") {
|
||||
"$target_gen_dir/js2c/worker_init.js",
|
||||
]
|
||||
|
||||
sources = webpack_sources + [
|
||||
"$target_gen_dir/js2c/asar.js",
|
||||
"$target_gen_dir/js2c/asar_init.js",
|
||||
]
|
||||
|
||||
inputs = sources + [ "//third_party/electron_node/tools/js2c.py" ]
|
||||
outputs = [ "$root_gen_dir/electron_natives.cc" ]
|
||||
|
||||
@@ -468,7 +465,7 @@ source_set("electron_lib") {
|
||||
deps += [ "//third_party/crashpad/crashpad/client" ]
|
||||
}
|
||||
|
||||
libs = [
|
||||
frameworks = [
|
||||
"AVFoundation.framework",
|
||||
"Carbon.framework",
|
||||
"LocalAuthentication.framework",
|
||||
@@ -495,7 +492,7 @@ source_set("electron_lib") {
|
||||
"shell/common/crash_keys.h",
|
||||
]
|
||||
} else {
|
||||
libs += [
|
||||
frameworks += [
|
||||
"Squirrel.framework",
|
||||
"ReactiveObjC.framework",
|
||||
"Mantle.framework",
|
||||
@@ -661,6 +658,7 @@ source_set("electron_lib") {
|
||||
deps += [
|
||||
"//components/pdf/browser",
|
||||
"//components/pdf/renderer",
|
||||
"//pdf:pdf_ppapi",
|
||||
]
|
||||
sources += [
|
||||
"shell/browser/electron_pdf_web_contents_helper_client.cc",
|
||||
@@ -805,10 +803,10 @@ if (is_mac) {
|
||||
|
||||
include_dirs = [ "." ]
|
||||
sources = filenames.framework_sources
|
||||
libs = []
|
||||
frameworks = []
|
||||
|
||||
if (enable_osr) {
|
||||
libs += [ "IOSurface.framework" ]
|
||||
frameworks += [ "IOSurface.framework" ]
|
||||
}
|
||||
|
||||
ldflags = [
|
||||
@@ -917,7 +915,7 @@ if (is_mac) {
|
||||
output_name = electron_login_helper_name
|
||||
sources = filenames.login_helper_sources
|
||||
include_dirs = [ "." ]
|
||||
libs = [ "AppKit.framework" ]
|
||||
frameworks = [ "AppKit.framework" ]
|
||||
info_plist = "shell/app/resources/mac/loginhelper-Info.plist"
|
||||
extra_substitutions =
|
||||
[ "ELECTRON_BUNDLE_ID=$electron_mac_bundle_id.loginhelper" ]
|
||||
|
||||
9
DEPS
9
DEPS
@@ -8,14 +8,15 @@ gclient_gn_args = [
|
||||
'checkout_pgo_profiles',
|
||||
'checkout_oculus_sdk',
|
||||
'checkout_openxr',
|
||||
'checkout_google_benchmark'
|
||||
'checkout_google_benchmark',
|
||||
'mac_xcode_version',
|
||||
]
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'9ae03ef8f7d4f6ac663f725bcfe70311987652f3',
|
||||
'93a6ebbe22f1a093e6a0cb5e72ba78990fe39824',
|
||||
'node_version':
|
||||
'v12.18.2',
|
||||
'v12.18.3',
|
||||
'nan_version':
|
||||
'2c4ee8a32a299eada3cd6e468bbd0a473bfea96d',
|
||||
'squirrel.mac_version':
|
||||
@@ -54,6 +55,8 @@ vars = {
|
||||
# Python "requests" module is used for releases only.
|
||||
'checkout_requests': False,
|
||||
|
||||
'mac_xcode_version': 'default',
|
||||
|
||||
# To allow running hooks without parsing the DEPS tree
|
||||
'process_deps': True,
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
11.0.0-nightly.20200709
|
||||
11.0.0-nightly.20200731
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
version: 1.0.{build}
|
||||
build_cloud: electron-16-core
|
||||
image: vs2019bt-16.4.0
|
||||
image: vs2019bt-16.6.2
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\electron\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
@@ -61,6 +61,7 @@ build_script:
|
||||
- git config --global core.longpaths true
|
||||
- cd ..
|
||||
- mkdir src
|
||||
- update_depot_tools.bat
|
||||
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
|
||||
- ps: $env:CHROMIUM_BUILDTOOLS_PATH="$pwd\src\buildtools"
|
||||
- ps: $env:SCCACHE_PATH="$pwd\src\electron\external_binaries\sccache.exe"
|
||||
|
||||
104
azure-pipelines-arm.yml
Normal file
104
azure-pipelines-arm.yml
Normal file
@@ -0,0 +1,104 @@
|
||||
steps:
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy Files to: src/electron'
|
||||
inputs:
|
||||
TargetFolder: src/electron
|
||||
|
||||
- bash: |
|
||||
cd src/electron
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
displayName: 'Yarn install'
|
||||
|
||||
- bash: |
|
||||
export ZIP_DEST=$PWD/src/out/Default
|
||||
echo "##vso[task.setvariable variable=ZIP_DEST]$ZIP_DEST"
|
||||
mkdir -p $ZIP_DEST
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=dist.zip --dest=$ZIP_DEST
|
||||
cd $ZIP_DEST
|
||||
unzip -o dist.zip
|
||||
displayName: 'Download and unzip dist files for test'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export FFMPEG_ZIP_DEST=$PWD/src/out/ffmpeg
|
||||
mkdir -p $FFMPEG_ZIP_DEST
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=ffmpeg.zip --dest=$FFMPEG_ZIP_DEST
|
||||
cd $FFMPEG_ZIP_DEST
|
||||
unzip -o ffmpeg.zip
|
||||
displayName: 'Download and unzip ffmpeg for test'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export NODE_HEADERS_DEST=$PWD/src/out/Default/gen
|
||||
mkdir -p $NODE_HEADERS_DEST
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=node_headers.tar.gz --dest=$NODE_HEADERS_DEST
|
||||
cd $NODE_HEADERS_DEST
|
||||
tar xzf node_headers.tar.gz
|
||||
displayName: 'Download and untar node header files for test'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export CROSS_ARCH_SNAPSHOTS=$PWD/src/out/Default/cross-arch-snapshots
|
||||
mkdir -p $CROSS_ARCH_SNAPSHOTS
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/snapshot_blob.bin --dest=$CROSS_ARCH_SNAPSHOTS
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/v8_context_snapshot.bin --dest=$CROSS_ARCH_SNAPSHOTS
|
||||
displayName: 'Download cross arch snapshot files'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export NATIVE_UNITTESTS_DEST=$PWD/src/out/Default
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=shell_browser_ui_unittests --dest=$NATIVE_UNITTESTS_DEST
|
||||
chmod +x $NATIVE_UNITTESTS_DEST/shell_browser_ui_unittests
|
||||
displayName: 'Download native unittest executables'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
set npm_config_arch=arm64
|
||||
(cd electron && node script/yarn test -- --enable-logging)
|
||||
displayName: 'Run Electron tests'
|
||||
timeoutInMinutes: 20
|
||||
env:
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
python electron/script/verify-ffmpeg.py --source-root "$PWD" --build-dir out/Default --ffmpeg-path out/ffmpeg
|
||||
displayName: Verify non proprietary ffmpeg
|
||||
timeoutInMinutes: 5
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
echo Verify cross arch snapshot
|
||||
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/out/Default/cross-arch-snapshots
|
||||
displayName: Verify cross arch snapshot
|
||||
timeoutInMinutes: 5
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
inputs:
|
||||
testResultsFiles: '*.xml'
|
||||
|
||||
searchFolder: '$(System.DefaultWorkingDirectory)/src/junit/'
|
||||
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
|
||||
displayName: 'Clean Agent Directories'
|
||||
|
||||
condition: always()
|
||||
@@ -1,2 +1,2 @@
|
||||
process.env.PRINT_WEBPACK_GRAPH = true
|
||||
require('./run-compiler')
|
||||
process.env.PRINT_WEBPACK_GRAPH = true;
|
||||
require('./run-compiler');
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
const configPath = process.argv[2]
|
||||
const outPath = path.resolve(process.argv[3])
|
||||
const config = require(configPath)
|
||||
const configPath = process.argv[2];
|
||||
const outPath = path.resolve(process.argv[3]);
|
||||
const config = require(configPath);
|
||||
config.output = {
|
||||
path: path.dirname(outPath),
|
||||
filename: path.basename(outPath)
|
||||
}
|
||||
};
|
||||
|
||||
const { wrapInitWithProfilingTimeout } = config;
|
||||
delete config.wrapInitWithProfilingTimeout;
|
||||
|
||||
webpack(config, (err, stats) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
} else if (stats.hasErrors()) {
|
||||
console.error(stats.toString('normal'))
|
||||
process.exit(1)
|
||||
console.error(stats.toString('normal'));
|
||||
process.exit(1);
|
||||
} else {
|
||||
if (wrapInitWithProfilingTimeout) {
|
||||
const contents = fs.readFileSync(outPath, 'utf8');
|
||||
@@ -33,6 +33,6 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
|
||||
}`;
|
||||
fs.writeFileSync(outPath, newContents);
|
||||
}
|
||||
process.exit(0)
|
||||
process.exit(0);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
5
build/webpack/webpack.config.asar.js
Normal file
5
build/webpack/webpack.config.asar.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'asar',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true
|
||||
});
|
||||
@@ -1,39 +1,39 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '../..')
|
||||
const electronRoot = path.resolve(__dirname, '../..');
|
||||
|
||||
const onlyPrintingGraph = !!process.env.PRINT_WEBPACK_GRAPH
|
||||
const onlyPrintingGraph = !!process.env.PRINT_WEBPACK_GRAPH;
|
||||
|
||||
class AccessDependenciesPlugin {
|
||||
apply(compiler) {
|
||||
apply (compiler) {
|
||||
// Only hook into webpack when we are printing the dependency graph
|
||||
if (!onlyPrintingGraph) return
|
||||
if (!onlyPrintingGraph) return;
|
||||
|
||||
compiler.hooks.compilation.tap('AccessDependenciesPlugin', compilation => {
|
||||
compilation.hooks.finishModules.tap('AccessDependenciesPlugin', modules => {
|
||||
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p))
|
||||
console.info(JSON.stringify(filePaths))
|
||||
})
|
||||
})
|
||||
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p));
|
||||
console.info(JSON.stringify(filePaths));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const defines = {
|
||||
BUILDFLAG: onlyPrintingGraph ? '(a => a)' : ''
|
||||
}
|
||||
};
|
||||
|
||||
const buildFlagsPrefix = '--buildflags='
|
||||
const buildFlagsPrefix = '--buildflags=';
|
||||
const buildFlagArg = process.argv.find(arg => arg.startsWith(buildFlagsPrefix));
|
||||
|
||||
if (buildFlagArg) {
|
||||
const buildFlagPath = buildFlagArg.substr(buildFlagsPrefix.length)
|
||||
const buildFlagPath = buildFlagArg.substr(buildFlagsPrefix.length);
|
||||
|
||||
const flagFile = fs.readFileSync(buildFlagPath, 'utf8')
|
||||
const flagFile = fs.readFileSync(buildFlagPath, 'utf8');
|
||||
for (const line of flagFile.split(/(\r\n|\r|\n)/g)) {
|
||||
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/)
|
||||
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/);
|
||||
if (flagMatch) {
|
||||
const [, flagName, flagValue] = flagMatch;
|
||||
defines[flagName] = JSON.stringify(Boolean(parseInt(flagValue, 10)));
|
||||
@@ -41,27 +41,27 @@ if (buildFlagArg) {
|
||||
}
|
||||
}
|
||||
|
||||
const ignoredModules = []
|
||||
const ignoredModules = [];
|
||||
|
||||
if (defines['ENABLE_DESKTOP_CAPTURER'] === 'false') {
|
||||
if (defines.ENABLE_DESKTOP_CAPTURER === 'false') {
|
||||
ignoredModules.push(
|
||||
'@electron/internal/browser/desktop-capturer',
|
||||
'@electron/internal/browser/api/desktop-capturer',
|
||||
'@electron/internal/renderer/api/desktop-capturer'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (defines['ENABLE_REMOTE_MODULE'] === 'false') {
|
||||
if (defines.ENABLE_REMOTE_MODULE === 'false') {
|
||||
ignoredModules.push(
|
||||
'@electron/internal/browser/remote/server',
|
||||
'@electron/internal/renderer/api/remote'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (defines['ENABLE_VIEWS_API'] === 'false') {
|
||||
if (defines.ENABLE_VIEWS_API === 'false') {
|
||||
ignoredModules.push(
|
||||
'@electron/internal/browser/api/views/image-view.js'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = ({
|
||||
@@ -71,11 +71,13 @@ module.exports = ({
|
||||
target,
|
||||
wrapInitWithProfilingTimeout
|
||||
}) => {
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts')
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
|
||||
if (!fs.existsSync(entry)) {
|
||||
entry = path.resolve(electronRoot, 'lib', target, 'init.js')
|
||||
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
|
||||
}
|
||||
|
||||
const electronAPIFile = path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts');
|
||||
|
||||
return ({
|
||||
mode: 'development',
|
||||
devtool: false,
|
||||
@@ -88,16 +90,19 @@ module.exports = ({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@electron/internal': path.resolve(electronRoot, 'lib'),
|
||||
'electron': path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts'),
|
||||
electron$: electronAPIFile,
|
||||
'electron/main$': electronAPIFile,
|
||||
'electron/renderer$': electronAPIFile,
|
||||
'electron/common$': electronAPIFile,
|
||||
// Force timers to resolve to our dependency that doesn't use window.postMessage
|
||||
'timers': path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
|
||||
timers: path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
|
||||
},
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
|
||||
loader: 'null-loader',
|
||||
loader: 'null-loader'
|
||||
}, {
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader',
|
||||
@@ -106,7 +111,7 @@ module.exports = ({
|
||||
transpileOnly: onlyPrintingGraph,
|
||||
ignoreDiagnostics: [
|
||||
// File '{0}' is not under 'rootDir' '{1}'.
|
||||
6059,
|
||||
6059
|
||||
]
|
||||
}
|
||||
}]
|
||||
@@ -116,7 +121,7 @@ module.exports = ({
|
||||
__filename: false,
|
||||
// We provide our own "timers" import above, any usage of setImmediate inside
|
||||
// one of our renderer bundles should import it from the 'timers' package
|
||||
setImmediate: false,
|
||||
setImmediate: false
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
@@ -124,24 +129,24 @@ module.exports = ({
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
keep_classnames: true,
|
||||
keep_fnames: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
keep_fnames: true
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new AccessDependenciesPlugin(),
|
||||
...(targetDeletesNodeGlobals ? [
|
||||
new webpack.ProvidePlugin({
|
||||
process: ['@electron/internal/renderer/webpack-provider', 'process'],
|
||||
global: ['@electron/internal/renderer/webpack-provider', '_global'],
|
||||
Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'],
|
||||
process: ['@electron/internal/common/webpack-provider', 'process'],
|
||||
global: ['@electron/internal/common/webpack-provider', '_global'],
|
||||
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer']
|
||||
})
|
||||
] : []),
|
||||
new webpack.ProvidePlugin({
|
||||
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise'],
|
||||
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise']
|
||||
}),
|
||||
new webpack.DefinePlugin(defines),
|
||||
new webpack.DefinePlugin(defines)
|
||||
]
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'browser',
|
||||
alwaysHasNode: true
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'isolated_renderer',
|
||||
alwaysHasNode: false
|
||||
})
|
||||
});
|
||||
|
||||
@@ -3,4 +3,4 @@ module.exports = require('./webpack.config.base')({
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithProfilingTimeout: true
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'sandboxed_renderer',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
})
|
||||
wrapInitWithProfilingTimeout: true
|
||||
});
|
||||
|
||||
@@ -3,4 +3,4 @@ module.exports = require('./webpack.config.base')({
|
||||
loadElectronFromAlternateTarget: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true
|
||||
})
|
||||
});
|
||||
|
||||
@@ -32,6 +32,8 @@ PATHS_TO_SKIP = [
|
||||
# On Linux, we don't use crashpad, but this binary is still built for some
|
||||
# reason. Exclude it from the zip.
|
||||
'./crashpad_handler',
|
||||
|
||||
'resources/inspector', #Skipping because these are outputs that we don't need
|
||||
]
|
||||
|
||||
def skip_path(dep, dist_zip, target_cpu):
|
||||
|
||||
@@ -12,6 +12,8 @@ import("//third_party/widevine/cdm/widevine.gni")
|
||||
static_library("chrome") {
|
||||
visibility = [ "//electron:electron_lib" ]
|
||||
sources = [
|
||||
"//chrome/browser/accessibility/accessibility_ui.cc",
|
||||
"//chrome/browser/accessibility/accessibility_ui.h",
|
||||
"//chrome/browser/browser_process.cc",
|
||||
"//chrome/browser/browser_process.h",
|
||||
"//chrome/browser/crash_upload_list/crash_upload_list_crashpad.cc",
|
||||
@@ -62,18 +64,20 @@ static_library("chrome") {
|
||||
"//extensions/browser/app_window/size_constraints.cc",
|
||||
"//extensions/browser/app_window/size_constraints.h",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
"//chrome/common",
|
||||
"//chrome/common:version_header",
|
||||
"//components/keyed_service/content",
|
||||
"//components/paint_preview/buildflags",
|
||||
"//components/proxy_config",
|
||||
"//components/security_state/content",
|
||||
"//content/public/browser",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//chrome/browser:resource_prefetch_predictor_proto",
|
||||
"//chrome/services/speech:buildflags",
|
||||
"//components/feature_engagement:buildflags",
|
||||
"//components/optimization_guide/proto:optimization_guide_proto",
|
||||
]
|
||||
|
||||
@@ -93,6 +97,14 @@ static_library("chrome") {
|
||||
]
|
||||
}
|
||||
|
||||
if (is_win) {
|
||||
sources += [
|
||||
"//chrome/browser/win/icon_reader_service.cc",
|
||||
"//chrome/browser/win/icon_reader_service.h",
|
||||
]
|
||||
public_deps += [ "//chrome/services/util_win:lib" ]
|
||||
}
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
sources += [
|
||||
"//chrome/browser/media/webrtc/desktop_media_list.h",
|
||||
@@ -153,13 +165,6 @@ static_library("chrome") {
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_tts) {
|
||||
sources += [
|
||||
"//chrome/browser/speech/tts_controller_delegate_impl.cc",
|
||||
"//chrome/browser/speech/tts_controller_delegate_impl.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_widevine) {
|
||||
sources += [
|
||||
"//chrome/renderer/media/chrome_key_systems.cc",
|
||||
@@ -266,7 +271,7 @@ static_library("chrome") {
|
||||
source_set("plugins") {
|
||||
sources = []
|
||||
deps = []
|
||||
libs = []
|
||||
frameworks = []
|
||||
|
||||
# browser side
|
||||
sources += [
|
||||
@@ -300,7 +305,7 @@ source_set("plugins") {
|
||||
"//chrome/browser/renderer_host/pepper/monitor_finder_mac.h",
|
||||
"//chrome/browser/renderer_host/pepper/monitor_finder_mac.mm",
|
||||
]
|
||||
libs += [ "CoreGraphics.framework" ]
|
||||
frameworks += [ "CoreGraphics.framework" ]
|
||||
}
|
||||
if (is_linux) {
|
||||
deps += [ "//components/services/font/public/cpp" ]
|
||||
@@ -364,8 +369,8 @@ source_set("chrome_spellchecker") {
|
||||
"//chrome/browser/spellchecker/spellcheck_factory.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.cc",
|
||||
"//chrome/browser/spellchecker/spellcheck_language_policy_handler.h",
|
||||
"//chrome/browser/spellchecker/spellcheck_service.cc",
|
||||
|
||||
@@ -53,31 +53,31 @@
|
||||
<nav>
|
||||
<div class="linkcol">
|
||||
<a class="hero-link" target="_blank" href="https://electronjs.org/blog">
|
||||
<span class="octicon hero-octicon octicon-gist" aria-hidden="true"></span>
|
||||
<span class="octicon hero-octicon octicon-code-square-24" aria-hidden="true"></span>
|
||||
<h4>Blog</h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkcol">
|
||||
<a class="hero-link" target="_blank" href="https://github.com/electron/electron">
|
||||
<span class="octicon hero-octicon octicon-mark-github" aria-hidden="true"></span>
|
||||
<span class="octicon hero-octicon octicon-mark-github-16" aria-hidden="true"></span>
|
||||
<h4>Repository</h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkcol">
|
||||
<a class="hero-link" target="_blank" href="https://electronjs.org/docs">
|
||||
<span class="octicon hero-octicon octicon-gear" aria-hidden="true"></span>
|
||||
<span class="octicon hero-octicon octicon-book-24" aria-hidden="true"></span>
|
||||
<h4>Docs</h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkcol">
|
||||
<a class="hero-link" target="_blank" href="https://github.com/electron/electron-api-demos">
|
||||
<span class="octicon hero-octicon octicon-star" aria-hidden="true"></span>
|
||||
<span class="octicon hero-octicon octicon-star-fill-24" aria-hidden="true"></span>
|
||||
<h4>API Demos</h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkcol">
|
||||
<a class="hero-link" target="_blank" href="https://electronforge.io">
|
||||
<span class="octicon hero-octicon octicon-gift" aria-hidden="true"></span>
|
||||
<span class="octicon hero-octicon octicon-gift-24" aria-hidden="true"></span>
|
||||
<h4>Forge</h4>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -360,7 +360,7 @@ page.
|
||||
|
||||
Emitted whenever there is a GPU info update.
|
||||
|
||||
### Event: 'gpu-process-crashed'
|
||||
### Event: 'gpu-process-crashed' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -369,6 +369,11 @@ Returns:
|
||||
|
||||
Emitted when the GPU process crashes or is killed.
|
||||
|
||||
**Deprecated:** This event is superceded by the `child-process-gone` event
|
||||
which contains more information about why the child process dissapeared. It
|
||||
isn't always because it crashed. The `killed` boolean can be replaced by
|
||||
checking `reason === 'killed'` when you switch to that event.
|
||||
|
||||
### Event: 'renderer-process-crashed' _Deprecated_
|
||||
|
||||
Returns:
|
||||
@@ -403,6 +408,36 @@ Returns:
|
||||
Emitted when the renderer process unexpectedly dissapears. This is normally
|
||||
because it was crashed or killed.
|
||||
|
||||
#### Event: 'child-process-gone'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `type` String - Process type. One of the following values:
|
||||
* `Utility`
|
||||
* `Zygote`
|
||||
* `Sandbox helper`
|
||||
* `GPU`
|
||||
* `Pepper Plugin`
|
||||
* `Pepper Plugin Broker`
|
||||
* `Unknown`
|
||||
* `reason` String - The reason the child process is gone. Possible values:
|
||||
* `clean-exit` - Process exited with an exit code of zero
|
||||
* `abnormal-exit` - Process exited with a non-zero exit code
|
||||
* `killed` - Process was sent a SIGTERM or otherwise killed externally
|
||||
* `crashed` - Process crashed
|
||||
* `oom` - Process ran out of memory
|
||||
* `launch-failure` - Process never successfully launched
|
||||
* `integrity-failure` - Windows code integrity checks failed
|
||||
* `exitCode` Number - The exit code for the process
|
||||
(e.g. status from waitpid if on posix, from GetExitCodeProcess on Windows).
|
||||
* `name` String (optional) - The name of the process. i.e. for plugins it might be Flash.
|
||||
Examples for utility: `Audio Service`, `Content Decryption Module Service`, `Network Service`, `Video Capture`, etc.
|
||||
|
||||
Emitted when the child process unexpectedly dissapears. This is normally
|
||||
because it was crashed or killed. It does not include renderer processes.
|
||||
|
||||
### Event: 'accessibility-support-changed' _macOS_ _Windows_
|
||||
|
||||
Returns:
|
||||
@@ -990,6 +1025,7 @@ if (!gotTheLock) {
|
||||
|
||||
// Create myWindow, load the rest of the app, etc...
|
||||
app.whenReady().then(() => {
|
||||
myWindow = createWindow()
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -1105,8 +1141,10 @@ For `infoType` equal to `complete`:
|
||||
For `infoType` equal to `basic`:
|
||||
Promise is fulfilled with `Object` containing fewer attributes than when requested with `complete`. Here's an example of basic response:
|
||||
```js
|
||||
{ auxAttributes:
|
||||
{ amdSwitchable: true,
|
||||
{
|
||||
auxAttributes:
|
||||
{
|
||||
amdSwitchable: true,
|
||||
canSupportThreadedTextureMailbox: false,
|
||||
directComposition: false,
|
||||
directRendering: true,
|
||||
@@ -1119,12 +1157,14 @@ For `infoType` equal to `basic`:
|
||||
sandboxed: false,
|
||||
softwareRendering: false,
|
||||
supportsOverlays: false,
|
||||
videoDecodeAcceleratorFlags: 0 },
|
||||
gpuDevice:
|
||||
[ { active: true, deviceId: 26657, vendorId: 4098 },
|
||||
{ active: false, deviceId: 3366, vendorId: 32902 } ],
|
||||
machineModelName: 'MacBookPro',
|
||||
machineModelVersion: '11.5' }
|
||||
videoDecodeAcceleratorFlags: 0
|
||||
},
|
||||
gpuDevice:
|
||||
[{ active: true, deviceId: 26657, vendorId: 4098 },
|
||||
{ active: false, deviceId: 3366, vendorId: 32902 }],
|
||||
machineModelName: 'MacBookPro',
|
||||
machineModelVersion: '11.5'
|
||||
}
|
||||
```
|
||||
|
||||
Using `basic` should be preferred if only basic information like `vendorId` or `driverId` is needed.
|
||||
@@ -1176,6 +1216,13 @@ Returns `Object`:
|
||||
should restore the state from the previous session. This indicates that the
|
||||
app should restore the windows that were open the last time the app was
|
||||
closed. This setting is not available on [MAS builds][mas-builds].
|
||||
* `executableWillLaunchAtLogin` Boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
|
||||
* `launchItems` Object[] _Windows_
|
||||
* `name` String _Windows_ - name value of a registry entry.
|
||||
* `path` String _Windows_ - The executable to an app that corresponds to a registry entry.
|
||||
* `args` String[] _Windows_ - the command-line arguments to pass to the executable.
|
||||
* `scope` String _Windows_ - one of `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
|
||||
* `enabled` Boolean _Windows_ - `true` if the app registry key is startup approved and therfore shows as `enabled` in Task Manager and Windows settings.
|
||||
|
||||
### `app.setLoginItemSettings(settings)` _macOS_ _Windows_
|
||||
|
||||
@@ -1191,7 +1238,9 @@ Returns `Object`:
|
||||
* `args` String[] (optional) _Windows_ - The command-line arguments to pass to
|
||||
the executable. Defaults to an empty array. Take care to wrap paths in
|
||||
quotes.
|
||||
|
||||
* `enabled` Boolean (optional) _Windows_ - `true` will change the startup approved registry key and `enable / disable` the App in Task Manager and Windows Settings.
|
||||
Defaults to `true`.
|
||||
* `name` String (optional) _Windows_ - value name to write into registry. Defaults to the app's AppUserModelId().
|
||||
Set the app's login item settings.
|
||||
|
||||
To work with Electron's `autoUpdater` on Windows, which uses [Squirrel][Squirrel-Windows],
|
||||
|
||||
@@ -104,7 +104,7 @@ The `autoUpdater` object has the following methods:
|
||||
* `options` Object
|
||||
* `url` String
|
||||
* `headers` Record<String, String> (optional) _macOS_ - HTTP request headers.
|
||||
* `serverType` String (optional) _macOS_ - Either `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
|
||||
* `serverType` String (optional) _macOS_ - Can be `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
|
||||
README for more information.
|
||||
|
||||
Sets the `url` and initialize the auto updater.
|
||||
|
||||
@@ -28,25 +28,6 @@ view.webContents.loadURL('https://electronjs.org')
|
||||
* `options` Object (optional)
|
||||
* `webPreferences` Object (optional) - See [BrowserWindow](browser-window.md).
|
||||
|
||||
### Static Methods
|
||||
|
||||
#### `BrowserView.getAllViews()`
|
||||
|
||||
Returns `BrowserView[]` - An array of all opened BrowserViews.
|
||||
|
||||
#### `BrowserView.fromWebContents(webContents)`
|
||||
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Returns `BrowserView | null` - The BrowserView that owns the given `webContents`
|
||||
or `null` if the contents are not owned by a BrowserView.
|
||||
|
||||
#### `BrowserView.fromId(id)`
|
||||
|
||||
* `id` Integer
|
||||
|
||||
Returns `BrowserView` - The view with the given `id`.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
Objects created with `new BrowserView` have the following properties:
|
||||
@@ -55,10 +36,6 @@ Objects created with `new BrowserView` have the following properties:
|
||||
|
||||
A [`WebContents`](web-contents.md) object owned by this view.
|
||||
|
||||
#### `view.id` _Experimental_
|
||||
|
||||
A `Integer` representing the unique ID of the view.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
Objects created with `new BrowserView` have the following instance methods:
|
||||
|
||||
@@ -38,7 +38,7 @@ the window after this event will have no visual flash:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ show: false })
|
||||
const win = new BrowserWindow({ show: false })
|
||||
win.once('ready-to-show', () => {
|
||||
win.show()
|
||||
})
|
||||
@@ -60,7 +60,7 @@ immediately, and use a `backgroundColor` close to your app's background:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let win = new BrowserWindow({ backgroundColor: '#2e2c29' })
|
||||
const win = new BrowserWindow({ backgroundColor: '#2e2c29' })
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
|
||||
@@ -74,8 +74,8 @@ By using `parent` option, you can create child windows:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let top = new BrowserWindow()
|
||||
let child = new BrowserWindow({ parent: top })
|
||||
const top = new BrowserWindow()
|
||||
const child = new BrowserWindow({ parent: top })
|
||||
child.show()
|
||||
top.show()
|
||||
```
|
||||
@@ -90,7 +90,7 @@ window, you have to set both `parent` and `modal` options:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let child = new BrowserWindow({ parent: top, modal: true, show: false })
|
||||
const child = new BrowserWindow({ parent: top, modal: true, show: false })
|
||||
child.loadURL('https://github.com')
|
||||
child.once('ready-to-show', () => {
|
||||
child.show()
|
||||
@@ -348,6 +348,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
You can access this context in the dev tools by selecting the
|
||||
'Electron Isolated Context' entry in the combo box at the top of the
|
||||
Console tab.
|
||||
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
|
||||
can't unsafely cross between worlds when using `contextIsolation`. The default
|
||||
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
|
||||
* `nativeWindowOpen` Boolean (optional) - Whether to use native
|
||||
`window.open()`. Defaults to `false`. Child windows will always have node
|
||||
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently
|
||||
@@ -597,7 +600,7 @@ e.g. `APPCOMMAND_BROWSER_BACKWARD` is emitted as `browser-backward`.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.on('app-command', (e, cmd) => {
|
||||
// Navigate the window back when the user hits their mouse back button
|
||||
if (cmd === 'browser-backward' && win.webContents.canGoBack()) {
|
||||
@@ -772,7 +775,7 @@ To check if a DevTools extension is installed you can run the following:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let installed = BrowserWindow.getDevToolsExtensions().hasOwnProperty('devtron')
|
||||
const installed = 'devtron' in BrowserWindow.getDevToolsExtensions()
|
||||
console.log(installed)
|
||||
```
|
||||
|
||||
@@ -789,7 +792,7 @@ Objects created with `new BrowserWindow` have the following properties:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
// In this example `win` is our instance
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
|
||||
@@ -1123,7 +1126,7 @@ Disable or enable the window.
|
||||
|
||||
#### `win.isEnabled()`
|
||||
|
||||
Returns Boolean - whether the window is enabled.
|
||||
Returns `Boolean` - whether the window is enabled.
|
||||
|
||||
#### `win.setSize(width, height[, animate])`
|
||||
|
||||
@@ -1314,9 +1317,9 @@ a HTML-rendered toolbar. For example:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
|
||||
let toolbarRect = document.getElementById('toolbar').getBoundingClientRect()
|
||||
const toolbarRect = document.getElementById('toolbar').getBoundingClientRect()
|
||||
win.setSheetOffset(toolbarRect.height)
|
||||
```
|
||||
|
||||
@@ -1440,7 +1443,7 @@ Node's [`url.format`](https://nodejs.org/api/url.html#url_url_format_urlobject)
|
||||
method:
|
||||
|
||||
```javascript
|
||||
let url = require('url').format({
|
||||
const url = require('url').format({
|
||||
protocol: 'file',
|
||||
slashes: true,
|
||||
pathname: require('path').join(__dirname, 'index.html')
|
||||
|
||||
@@ -83,4 +83,4 @@ Returns `Promise<Object>` - Resolves with an object containing the `value` and `
|
||||
Get the maximum usage across processes of trace buffer as a percentage of the
|
||||
full state.
|
||||
|
||||
[trace viewer]: https://github.com/catapult-project/catapult/blob/master/tracing
|
||||
[trace viewer]: https://chromium.googlesource.com/catapult/+/HEAD/tracing/README.md
|
||||
|
||||
@@ -156,10 +156,16 @@ parameters in a renderer process will not result in those parameters being sent
|
||||
with crashes that occur in other renderer processes or in the main process.
|
||||
|
||||
**Note:** Parameters have limits on the length of the keys and values. Key
|
||||
names must be no longer than 39 bytes, and values must be no longer than 127
|
||||
names must be no longer than 39 bytes, and values must be no longer than 20320
|
||||
bytes. Keys with names longer than the maximum will be silently ignored. Key
|
||||
values longer than the maximum length will be truncated.
|
||||
|
||||
**Note:** On linux values that are longer than 127 bytes will be chunked into
|
||||
multiple keys, each 127 bytes in length. E.g. `addExtraParameter('foo', 'a'.repeat(130))`
|
||||
will result in two chunked keys `foo__1` and `foo__2`, the first will contain
|
||||
the first 127 bytes and the second will contain the remaining 3 bytes. On
|
||||
your crash reporting backend you should stitch together keys in this format.
|
||||
|
||||
### `crashReporter.removeExtraParameter(key)`
|
||||
|
||||
* `key` String - Parameter key, must be no longer than 39 bytes.
|
||||
|
||||
@@ -9,7 +9,7 @@ runtime that allows interacting with pages and instrumenting them.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
|
||||
try {
|
||||
win.webContents.debugger.attach('1.1')
|
||||
|
||||
@@ -11,7 +11,7 @@ control the download item.
|
||||
```javascript
|
||||
// In the main process.
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.webContents.session.on('will-download', (event, item, webContents) => {
|
||||
// Set the save path, making Electron not to prompt a save dialog.
|
||||
item.setSavePath('/tmp/save.pdf')
|
||||
|
||||
@@ -15,7 +15,7 @@ To create a frameless window, you need to set `frame` to `false` in
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ width: 800, height: 600, frame: false })
|
||||
const win = new BrowserWindow({ width: 800, height: 600, frame: false })
|
||||
win.show()
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ Results in a hidden title bar and a full size content window, yet the title bar
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ titleBarStyle: 'hidden' })
|
||||
const win = new BrowserWindow({ titleBarStyle: 'hidden' })
|
||||
win.show()
|
||||
```
|
||||
|
||||
@@ -43,7 +43,7 @@ Results in a hidden title bar with an alternative look where the traffic light b
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
|
||||
const win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
|
||||
win.show()
|
||||
```
|
||||
|
||||
@@ -58,7 +58,7 @@ This option is only applicable for frameless windows.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: false })
|
||||
const win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: false })
|
||||
win.show()
|
||||
```
|
||||
|
||||
@@ -69,7 +69,7 @@ window transparent:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ transparent: true, frame: false })
|
||||
const win = new BrowserWindow({ transparent: true, frame: false })
|
||||
win.show()
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@ API:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.setIgnoreMouseEvents(true)
|
||||
```
|
||||
|
||||
@@ -112,8 +112,8 @@ optional parameter can be used to forward mouse move messages to the web page,
|
||||
allowing events such as `mouseleave` to be emitted:
|
||||
|
||||
```javascript
|
||||
let win = require('electron').remote.getCurrentWindow()
|
||||
let el = document.getElementById('clickThroughElement')
|
||||
const win = require('electron').remote.getCurrentWindow()
|
||||
const el = document.getElementById('clickThroughElement')
|
||||
el.addEventListener('mouseenter', () => {
|
||||
win.setIgnoreMouseEvents(true, { forward: true })
|
||||
})
|
||||
|
||||
@@ -148,4 +148,4 @@ found in the [`ipc-main-invoke-event`](structures/ipc-main-invoke-event.md)
|
||||
structure docs.
|
||||
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[web-contents-send]: web-contents.md#contentssendchannel-arg1-arg2-
|
||||
[web-contents-send]: web-contents.md#contentssendchannel-args
|
||||
|
||||
@@ -195,8 +195,8 @@ const template = [
|
||||
{
|
||||
label: 'Speech',
|
||||
submenu: [
|
||||
{ role: 'startspeaking' },
|
||||
{ role: 'stopspeaking' }
|
||||
{ role: 'startSpeaking' },
|
||||
{ role: 'stopSpeaking' }
|
||||
]
|
||||
}
|
||||
] : [
|
||||
@@ -211,12 +211,12 @@ const template = [
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ role: 'reload' },
|
||||
{ role: 'forcereload' },
|
||||
{ role: 'toggledevtools' },
|
||||
{ role: 'forceReload' },
|
||||
{ role: 'toggleDevTools' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetzoom' },
|
||||
{ role: 'zoomin' },
|
||||
{ role: 'zoomout' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
{ role: 'zoomOut' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' }
|
||||
]
|
||||
|
||||
@@ -17,7 +17,7 @@ renderer process:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ const { app, BrowserWindow, screen } = require('electron')
|
||||
let win
|
||||
|
||||
app.whenReady().then(() => {
|
||||
let displays = screen.getAllDisplays()
|
||||
let externalDisplay = displays.find((display) => {
|
||||
const displays = screen.getAllDisplays()
|
||||
const externalDisplay = displays.find((display) => {
|
||||
return display.bounds.x !== 0 || display.bounds.y !== 0
|
||||
})
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ property of [`WebContents`](web-contents.md), or from the `session` module.
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
const ses = win.webContents.session
|
||||
@@ -332,7 +332,7 @@ verify proc.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
|
||||
win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
const { hostname } = request
|
||||
@@ -348,8 +348,16 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
|
||||
* `handler` Function | null
|
||||
* `webContents` [WebContents](web-contents.md) - WebContents requesting the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin.
|
||||
* `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex',
|
||||
'pointerLock', 'fullscreen', 'openExternal'.
|
||||
* `permission` String - The type of requested permission.
|
||||
* `media` - Request access to media devices such as camera, microphone and speakers.
|
||||
* `mediaKeySystem` - Request access to DRM protected content.
|
||||
* `geolocation` - Request access to user's current location.
|
||||
* `notifications` - Request notification creation and the ability to display them in the user's system tray.
|
||||
* `midi` - Request MIDI access in the `webmidi` API.
|
||||
* `midiSysex` - Request the use of system exclusive messages in the `webmidi` API.
|
||||
* `pointerLock` - Request to directly interpret mouse movements as an input method. Click [here](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) to know more.
|
||||
* `fullscreen` - Request for the app to enter fullscreen mode.
|
||||
* `openExternal` - Request to open links in external applications.
|
||||
* `callback` Function
|
||||
* `permissionGranted` Boolean - Allow or deny the permission.
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
@@ -655,7 +663,7 @@ const path = require('path')
|
||||
app.whenReady().then(() => {
|
||||
const protocol = session.fromPartition('some-partition').protocol
|
||||
protocol.registerFileProtocol('atom', (request, callback) => {
|
||||
let url = request.url.substr(7)
|
||||
const url = request.url.substr(7)
|
||||
callback({ path: path.normalize(`${__dirname}/${url}`) })
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# KeyboardEvent Object extends `Event`
|
||||
# KeyboardEvent Object
|
||||
|
||||
* `ctrlKey` Boolean (optional) - whether the Control key was used in an accelerator to trigger the Event
|
||||
* `metaKey` Boolean (optional) - whether a meta key was used in an accelerator to trigger the Event
|
||||
|
||||
@@ -41,7 +41,7 @@ An example TraceConfig that roughly matches what Chrome DevTools records:
|
||||
'disabled-by-default-v8.cpu_profiler',
|
||||
'disabled-by-default-v8.cpu_profiler.hires'
|
||||
],
|
||||
excluded_categories: [ '*' ]
|
||||
excluded_categories: ['*']
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ not (transparent windows won't work correctly when DWM composition is disabled):
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow, systemPreferences } = require('electron')
|
||||
let browserOptions = { width: 1000, height: 800 }
|
||||
const browserOptions = { width: 1000, height: 800 }
|
||||
|
||||
// Make the window transparent only if the platform supports it.
|
||||
if (process.platform !== 'win32' || systemPreferences.isAeroGlassEnabled()) {
|
||||
@@ -218,7 +218,7 @@ if (process.platform !== 'win32' || systemPreferences.isAeroGlassEnabled()) {
|
||||
}
|
||||
|
||||
// Create the window.
|
||||
let win = new BrowserWindow(browserOptions)
|
||||
const win = new BrowserWindow(browserOptions)
|
||||
|
||||
// Navigate.
|
||||
if (browserOptions.transparent) {
|
||||
|
||||
@@ -41,6 +41,10 @@ the button in the touch bar.
|
||||
A `NativeImage` representing the button's current icon. Changing this value immediately updates the button
|
||||
in the touch bar.
|
||||
|
||||
#### `touchBarButton.iconPosition`
|
||||
|
||||
A `String` - Can be `left`, `right` or `overlay`. Defaults to `overlay`.
|
||||
|
||||
#### `touchBarButton.enabled`
|
||||
|
||||
A `Boolean` representing whether the button is in an enabled state.
|
||||
|
||||
@@ -49,3 +49,7 @@ updates the control in the touch bar. Updating deep properties inside this array
|
||||
|
||||
An `Integer` representing the currently selected segment. Changing this value immediately updates the control
|
||||
in the touch bar. User interaction with the touch bar will update this value automatically.
|
||||
|
||||
#### `touchBarSegmentedControl.mode`
|
||||
|
||||
A `String` representing the current selection mode of the control. Can be `single`, `multiple` or `buttons`.
|
||||
|
||||
@@ -11,3 +11,11 @@ Process: [Main](../tutorial/application-architecture.md#main-and-renderer-proces
|
||||
* `small` - Small space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceSmall`. This is the default.
|
||||
* `large` - Large space between items. Maps to `NSTouchBarItemIdentifierFixedSpaceLarge`.
|
||||
* `flexible` - Take up all available space. Maps to `NSTouchBarItemIdentifierFlexibleSpace`.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following properties are available on instances of `TouchBarSpacer`:
|
||||
|
||||
#### `touchBarSpacer.size`
|
||||
|
||||
A `String` representing the size of the spacer. Can be `small`, `large` or `flexible`.
|
||||
|
||||
@@ -12,10 +12,10 @@ the [`BrowserWindow`](browser-window.md) object. An example of accessing the
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let win = new BrowserWindow({ width: 800, height: 1500 })
|
||||
const win = new BrowserWindow({ width: 800, height: 1500 })
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
let contents = win.webContents
|
||||
const contents = win.webContents
|
||||
console.log(contents)
|
||||
```
|
||||
|
||||
@@ -418,7 +418,7 @@ To only prevent the menu shortcuts, use
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
|
||||
win.webContents.on('before-input-event', (event, input) => {
|
||||
// For example, only enable application menu keyboard shortcuts when
|
||||
@@ -665,7 +665,7 @@ app.whenReady().then(() => {
|
||||
win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
||||
event.preventDefault()
|
||||
let result = deviceList.find((device) => {
|
||||
const result = deviceList.find((device) => {
|
||||
return device.deviceName === 'test'
|
||||
})
|
||||
if (!result) {
|
||||
@@ -691,7 +691,7 @@ buffer.
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let win = new BrowserWindow({ webPreferences: { offscreen: true } })
|
||||
const win = new BrowserWindow({ webPreferences: { offscreen: true } })
|
||||
win.webContents.on('paint', (event, dirty, image) => {
|
||||
// updateBitmap(dirty, image.getBitmap())
|
||||
})
|
||||
@@ -907,7 +907,7 @@ Returns `String` - The URL of the current web page.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('http://github.com').then(() => {
|
||||
const currentURL = win.webContents.getURL()
|
||||
console.log(currentURL)
|
||||
@@ -1311,6 +1311,8 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
|
||||
* `success` Boolean - Indicates success of the print call.
|
||||
* `failureReason` String - Error description called back if the print fails.
|
||||
|
||||
When a custom `pageSize` is passed, Chromium attempts to validate platform specific minumum values for `width_microns` and `height_microns`. Width and height must both be minimum 353 microns but may be higher on some operating systems.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1319,7 +1321,14 @@ Use `page-break-before: always;` CSS style to force to print to a new page.
|
||||
Example usage:
|
||||
|
||||
```js
|
||||
const options = { silent: true, deviceName: 'My-Printer' }
|
||||
const options = {
|
||||
silent: true,
|
||||
deviceName: 'My-Printer',
|
||||
pageRanges: {
|
||||
from: 0,
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
win.webContents.print(options, (success, errorType) => {
|
||||
if (!success) console.log(errorType)
|
||||
})
|
||||
@@ -1334,13 +1343,12 @@ win.webContents.print(options, (success, errorType) => {
|
||||
* `landscape` Boolean (optional) - `true` for landscape, `false` for portrait.
|
||||
* `marginsType` Integer (optional) - Specifies the type of margins to use. Uses 0 for
|
||||
default margin, 1 for no margin, and 2 for minimum margin.
|
||||
and `width` in microns.
|
||||
* `scaleFactor` Number (optional) - The scale factor of the web page. Can range from 0 to 100.
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print.
|
||||
* `from` Number - the first page to print.
|
||||
* `to` Number - the last page to print (inclusive).
|
||||
* `from` Number - zero-based index of the first page to print.
|
||||
* `to` Number - zero-based index of the last page to print (inclusive).
|
||||
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height` and `width` in microns.
|
||||
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
|
||||
* `printSelectionOnly` Boolean (optional) - Whether to print selection only.
|
||||
|
||||
@@ -1371,19 +1379,22 @@ An example of `webContents.printToPDF`:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
// Use default printing options
|
||||
win.webContents.printToPDF({}).then(data => {
|
||||
fs.writeFile('/tmp/print.pdf', data, (error) => {
|
||||
const pdfPath = path.join(os.homedir(), 'Desktop', 'temp.pdf')
|
||||
fs.writeFile(pdfPath, data, (error) => {
|
||||
if (error) throw error
|
||||
console.log('Write PDF successfully.')
|
||||
console.log(`Wrote PDF successfully to ${pdfPath}`)
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
console.log(`Failed to write PDF to ${pdfPath}: `, error)
|
||||
})
|
||||
})
|
||||
```
|
||||
@@ -1397,7 +1408,7 @@ creation:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.webContents.on('devtools-opened', () => {
|
||||
win.webContents.addWorkSpace(__dirname)
|
||||
})
|
||||
@@ -1718,7 +1729,7 @@ Returns `Promise<void>` - resolves if the page is saved.
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
|
||||
win.loadURL('https://github.com')
|
||||
|
||||
|
||||
@@ -561,8 +561,8 @@ Stops any `findInPage` request for the `webview` with the provided `action`.
|
||||
* `collate` Boolean (optional) - Whether the web page should be collated.
|
||||
* `copies` Number (optional) - The number of copies of the web page to print.
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print.
|
||||
* `from` Number - the start page.
|
||||
* `to` Number - the end page.
|
||||
* `from` Number - zero-based index of the first page to print.
|
||||
* `to` Number - zero-based index of the last page to print (inclusive).
|
||||
* `duplexMode` String (optional) - Set the duplex mode of the printed web page. Can be `simplex`, `shortEdge`, or `longEdge`.
|
||||
* `dpi` Record<string, number> (optional)
|
||||
* `horizontal` Number (optional) - The horizontal dpi.
|
||||
@@ -816,7 +816,7 @@ const { shell } = require('electron')
|
||||
const webview = document.querySelector('webview')
|
||||
|
||||
webview.addEventListener('new-window', async (e) => {
|
||||
const protocol = require('url').parse(e.url).protocol
|
||||
const protocol = (new URL(e.url)).protocol
|
||||
if (protocol === 'http:' || protocol === 'https:') {
|
||||
await shell.openExternal(e.url)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,6 @@ mainWindow.webContents.on('new-window', (event, url, frameName, disposition, opt
|
||||
|
||||
```javascript
|
||||
// renderer process (mainWindow)
|
||||
let modal = window.open('', 'modal')
|
||||
const modal = window.open('', 'modal')
|
||||
modal.document.write('<h1>Hello</h1>')
|
||||
```
|
||||
|
||||
@@ -266,7 +266,7 @@ const getGuestForWebContents = (webContentsId, contents) => {
|
||||
throw new Error(`Invalid webContentsId: ${webContentsId}`)
|
||||
}
|
||||
if (guest.hostWebContents !== contents) {
|
||||
throw new Error(`Access denied to webContents`)
|
||||
throw new Error('Access denied to webContents')
|
||||
}
|
||||
return guest
|
||||
}
|
||||
@@ -322,7 +322,7 @@ powerMonitor.querySystemIdleState(threshold, callback)
|
||||
const idleState = powerMonitor.getSystemIdleState(threshold)
|
||||
```
|
||||
|
||||
### API Changed: `powerMonitor.querySystemIdleTime` is now `powerMonitor.getSystemIdleState`
|
||||
### API Changed: `powerMonitor.querySystemIdleTime` is now `powerMonitor.getSystemIdleTime`
|
||||
|
||||
```js
|
||||
// Removed in Electron 7.0
|
||||
@@ -610,11 +610,11 @@ const { memory } = metrics[0] // Deprecated property
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
let optionsA = { webPreferences: { blinkFeatures: '' } }
|
||||
let windowA = new BrowserWindow(optionsA)
|
||||
const optionsA = { webPreferences: { blinkFeatures: '' } }
|
||||
const windowA = new BrowserWindow(optionsA)
|
||||
// Replace with
|
||||
let optionsB = { webPreferences: { enableBlinkFeatures: '' } }
|
||||
let windowB = new BrowserWindow(optionsB)
|
||||
const optionsB = { webPreferences: { enableBlinkFeatures: '' } }
|
||||
const windowB = new BrowserWindow(optionsB)
|
||||
|
||||
// Deprecated
|
||||
window.on('app-command', (e, cmd) => {
|
||||
@@ -785,11 +785,11 @@ The following list includes the breaking API changes made in Electron 2.0.
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
let optionsA = { titleBarStyle: 'hidden-inset' }
|
||||
let windowA = new BrowserWindow(optionsA)
|
||||
const optionsA = { titleBarStyle: 'hidden-inset' }
|
||||
const windowA = new BrowserWindow(optionsA)
|
||||
// Replace with
|
||||
let optionsB = { titleBarStyle: 'hiddenInset' }
|
||||
let windowB = new BrowserWindow(optionsB)
|
||||
const optionsB = { titleBarStyle: 'hiddenInset' }
|
||||
const windowB = new BrowserWindow(optionsB)
|
||||
```
|
||||
|
||||
### `menu`
|
||||
|
||||
@@ -15,7 +15,7 @@ calls, and other compiler optimizations. The only workaround is to build an
|
||||
unoptimized local build.
|
||||
|
||||
The official symbol server URL for Electron is
|
||||
https://electron-symbols.githubapp.com.
|
||||
https://symbols.electronjs.org.
|
||||
You cannot visit this URL directly, you must add it to the symbol path of your
|
||||
debugging tool. In the examples below, a local cache directory is used to avoid
|
||||
repeatedly fetching the PDB from the server. Replace `c:\code\symbols` with an
|
||||
@@ -30,7 +30,7 @@ directory on your computer, if you'd prefer a different location for downloaded
|
||||
symbols):
|
||||
|
||||
```powershell
|
||||
SRV*c:\code\symbols\*https://electron-symbols.githubapp.com
|
||||
SRV*c:\code\symbols\*https://symbols.electronjs.org
|
||||
```
|
||||
|
||||
Set this string as `_NT_SYMBOL_PATH` in the environment, using the Windbg menus,
|
||||
@@ -38,7 +38,7 @@ or by typing the `.sympath` command. If you would like to get symbols from
|
||||
Microsoft's symbol server as well, you should list that first:
|
||||
|
||||
```powershell
|
||||
SRV*c:\code\symbols\*https://msdl.microsoft.com/download/symbols;SRV*c:\code\symbols\*https://electron-symbols.githubapp.com
|
||||
SRV*c:\code\symbols\*https://msdl.microsoft.com/download/symbols;SRV*c:\code\symbols\*https://symbols.electronjs.org
|
||||
```
|
||||
|
||||
## Using the symbol server in Visual Studio
|
||||
|
||||
@@ -107,7 +107,7 @@ To solve this, you can turn off node integration in Electron:
|
||||
```javascript
|
||||
// In the main process.
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({
|
||||
const win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: false
|
||||
}
|
||||
@@ -155,7 +155,7 @@ To achieve this goal, set the background in the constructor for [BrowserWindow][
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow({
|
||||
const win = new BrowserWindow({
|
||||
backgroundColor: '#fff'
|
||||
})
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@ can open them programmatically by calling the `openDevTools()` API on the
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.webContents.openDevTools()
|
||||
```
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ fs.readdirSync('/path/to/example.asar')
|
||||
Use a module from the archive:
|
||||
|
||||
```javascript
|
||||
require('/path/to/example.asar/dir/module.js')
|
||||
require('./path/to/example.asar/dir/module.js')
|
||||
```
|
||||
|
||||
You can also display a web page in an `asar` archive with `BrowserWindow`:
|
||||
|
||||
@@ -9,9 +9,9 @@ const childProcess = require('child_process')
|
||||
const electronPath = require('electron')
|
||||
|
||||
// spawn the process
|
||||
let env = { /* ... */ }
|
||||
let stdio = ['inherit', 'inherit', 'inherit', 'ipc']
|
||||
let appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
|
||||
const env = { /* ... */ }
|
||||
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
|
||||
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
|
||||
|
||||
// listen for IPC messages from the app
|
||||
appProcess.on('message', (msg) => {
|
||||
@@ -50,7 +50,7 @@ class TestDriver {
|
||||
// handle rpc responses
|
||||
this.process.on('message', (message) => {
|
||||
// pop the handler
|
||||
let rpcCall = this.rpcCalls[message.msgId]
|
||||
const rpcCall = this.rpcCalls[message.msgId]
|
||||
if (!rpcCall) return
|
||||
this.rpcCalls[message.msgId] = null
|
||||
// reject/resolve
|
||||
@@ -70,7 +70,7 @@ class TestDriver {
|
||||
// to use: driver.rpc('method', 1, 2, 3).then(...)
|
||||
async rpc (cmd, ...args) {
|
||||
// send rpc request
|
||||
let msgId = this.rpcCalls.length
|
||||
const msgId = this.rpcCalls.length
|
||||
this.process.send({ msgId, cmd, args })
|
||||
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
|
||||
}
|
||||
@@ -92,10 +92,10 @@ async function onMessage ({ msgId, cmd, args }) {
|
||||
let method = METHODS[cmd]
|
||||
if (!method) method = () => new Error('Invalid method: ' + cmd)
|
||||
try {
|
||||
let resolve = await method(...args)
|
||||
const resolve = await method(...args)
|
||||
process.send({ msgId, resolve })
|
||||
} catch (err) {
|
||||
let reject = {
|
||||
const reject = {
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
name: err.name
|
||||
@@ -119,7 +119,7 @@ Then, in your test suite, you can use your test-driver as follows:
|
||||
const test = require('ava')
|
||||
const electronPath = require('electron')
|
||||
|
||||
let app = new TestDriver({
|
||||
const app = new TestDriver({
|
||||
path: electronPath,
|
||||
args: ['./app'],
|
||||
env: {
|
||||
|
||||
@@ -96,7 +96,7 @@ const { app, BrowserWindow } = require('electron')
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
let win = new BrowserWindow({
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -37,18 +37,18 @@ inAppPurchase.on('transactions-updated', (event, transactions) => {
|
||||
|
||||
// Check each transaction.
|
||||
transactions.forEach(function (transaction) {
|
||||
let payment = transaction.payment
|
||||
const payment = transaction.payment
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case 'purchasing':
|
||||
console.log(`Purchasing ${payment.productIdentifier}...`)
|
||||
break
|
||||
case 'purchased':
|
||||
|
||||
case 'purchased': {
|
||||
console.log(`${payment.productIdentifier} purchased.`)
|
||||
|
||||
// Get the receipt url.
|
||||
let receiptURL = inAppPurchase.getReceiptURL()
|
||||
const receiptURL = inAppPurchase.getReceiptURL()
|
||||
|
||||
console.log(`Receipt URL: ${receiptURL}`)
|
||||
|
||||
@@ -62,6 +62,8 @@ inAppPurchase.on('transactions-updated', (event, transactions) => {
|
||||
inAppPurchase.finishTransactionByDate(transaction.transactionDate)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'failed':
|
||||
|
||||
console.log(`Failed to purchase ${payment.productIdentifier}.`)
|
||||
@@ -105,8 +107,8 @@ inAppPurchase.getProducts(PRODUCT_IDS).then(products => {
|
||||
})
|
||||
|
||||
// Ask the user which product he/she wants to purchase.
|
||||
let selectedProduct = products[0]
|
||||
let selectedQuantity = 1
|
||||
const selectedProduct = products[0]
|
||||
const selectedQuantity = 1
|
||||
|
||||
// Purchase the selected product.
|
||||
inAppPurchase.purchaseProduct(selectedProduct.productIdentifier, selectedQuantity).then(isProductValid => {
|
||||
|
||||
@@ -10,7 +10,7 @@ so the `nodeIntegrationInWorker` option should be set to `true` in
|
||||
`webPreferences`.
|
||||
|
||||
```javascript
|
||||
let win = new BrowserWindow({
|
||||
const win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegrationInWorker: true
|
||||
}
|
||||
@@ -44,7 +44,7 @@ loads no native modules after the Web Workers get started.
|
||||
process.dlopen = () => {
|
||||
throw new Error('Load native module is not safe')
|
||||
}
|
||||
let worker = new Worker('script.js')
|
||||
const worker = new Worker('script.js')
|
||||
```
|
||||
|
||||
[web-workers]: https://developer.mozilla.org/en/docs/Web/API/Web_Workers_API/Using_web_workers
|
||||
|
||||
@@ -10,7 +10,7 @@ you want to show Notifications in the main process please check out the
|
||||
[Notification](../api/notification.md) module.
|
||||
|
||||
```javascript
|
||||
let myNotification = new Notification('Title', {
|
||||
const myNotification = new Notification('Title', {
|
||||
body: 'Lorem Ipsum Dolor Sit Amet'
|
||||
})
|
||||
|
||||
|
||||
@@ -39,6 +39,29 @@ will be started with the path of the file added as a command line argument.
|
||||
|
||||
## macOS Notes
|
||||
|
||||
### Adding the Recent Documents list to the application menu:
|
||||
|
||||
![macOS Recent Documents menu item][menu-item-image]
|
||||
|
||||
You can add menu items to access and clear recent documents by adding the following code snippet to your menu's template.
|
||||
|
||||
```json
|
||||
{
|
||||
"submenu":[
|
||||
{
|
||||
"label":"Open Recent",
|
||||
"role":"recentdocuments",
|
||||
"submenu":[
|
||||
{
|
||||
"label":"Clear Recent",
|
||||
"role":"clearrecentdocuments"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When a file is requested from the recent documents menu, the `open-file` event
|
||||
of `app` module will be emitted for it.
|
||||
|
||||
@@ -47,3 +70,4 @@ of `app` module will be emitted for it.
|
||||
[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-macos-windows
|
||||
[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-macos-windows
|
||||
[app-registration]: https://msdn.microsoft.com/en-us/library/cc144104(VS.85).aspx
|
||||
[menu-item-image]: https://user-images.githubusercontent.com/3168941/33003655-ea601c3a-cd70-11e7-97fa-7c062149cfb1.png
|
||||
|
||||
@@ -42,7 +42,7 @@ app.commandLine.appendSwitch('ppapi-flash-path', path.join(__dirname, pluginName
|
||||
app.commandLine.appendSwitch('ppapi-flash-version', '17.0.0.169')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
let win = new BrowserWindow({
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -146,7 +146,7 @@ const options = {
|
||||
}
|
||||
}
|
||||
|
||||
let client = webdriverio.remote(options)
|
||||
const client = webdriverio.remote(options)
|
||||
|
||||
client
|
||||
.init()
|
||||
|
||||
@@ -149,7 +149,7 @@ To set the overlay icon for a window, you can use the
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.setOverlayIcon('path/to/overlay.png', 'Description for overlay')
|
||||
```
|
||||
|
||||
@@ -168,7 +168,7 @@ To flash the BrowserWindow taskbar button, you can use the
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
let win = new BrowserWindow()
|
||||
const win = new BrowserWindow()
|
||||
win.once('focus', () => win.flashFrame(false))
|
||||
win.flashFrame(true)
|
||||
```
|
||||
|
||||
@@ -54,6 +54,7 @@ template("electron_extra_paks") {
|
||||
])
|
||||
output = "${invoker.output_dir}/resources.pak"
|
||||
sources = [
|
||||
"$root_gen_dir/chrome/dev_ui_browser_resources.pak",
|
||||
"$root_gen_dir/components/components_resources.pak",
|
||||
"$root_gen_dir/content/browser/resources/media/media_internals_resources.pak",
|
||||
"$root_gen_dir/content/browser/tracing/tracing_resources.pak",
|
||||
@@ -67,6 +68,7 @@ template("electron_extra_paks") {
|
||||
"$target_gen_dir/electron_resources.pak",
|
||||
]
|
||||
deps = [
|
||||
"//chrome/browser:dev_ui_browser_resources",
|
||||
"//components/resources",
|
||||
"//content:content_resources",
|
||||
"//content:dev_ui_content_resources",
|
||||
|
||||
@@ -75,4 +75,9 @@
|
||||
<message name="IDS_ACCEPT_LANGUAGES" use_name_for_id="true">
|
||||
en-US,en
|
||||
</message>
|
||||
<if expr="is_win">
|
||||
<message name="IDS_UTILITY_PROCESS_UTILITY_WIN_NAME" desc="The name of the utility process used to handle Windows utility operations.">
|
||||
Windows Utilities
|
||||
</message>
|
||||
</if>
|
||||
</grit-part>
|
||||
|
||||
@@ -174,7 +174,7 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"lib/isolated_renderer/init.js",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"package.json",
|
||||
@@ -217,7 +217,7 @@ auto_filenames = {
|
||||
"lib/browser/api/screen.ts",
|
||||
"lib/browser/api/session.ts",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/touch-bar.js",
|
||||
"lib/browser/api/touch-bar.ts",
|
||||
"lib/browser/api/tray.ts",
|
||||
"lib/browser/api/view.ts",
|
||||
"lib/browser/api/views/image-view.ts",
|
||||
@@ -238,7 +238,6 @@ auto_filenames = {
|
||||
"lib/browser/remote/objects-registry.ts",
|
||||
"lib/browser/remote/server.ts",
|
||||
"lib/browser/rpc-server.ts",
|
||||
"lib/browser/utils.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
@@ -273,6 +272,7 @@ auto_filenames = {
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
@@ -294,7 +294,6 @@ auto_filenames = {
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"lib/renderer/web-view/web-view-init.ts",
|
||||
"lib/renderer/webpack-provider.ts",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
@@ -315,6 +314,7 @@ auto_filenames = {
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
@@ -326,8 +326,18 @@ auto_filenames = {
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/renderer/webpack-provider.ts",
|
||||
"lib/worker/init.js",
|
||||
"lib/worker/init.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
asar_bundle_deps = [
|
||||
"lib/asar/fs-wrapper.ts",
|
||||
"lib/asar/init.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
|
||||
@@ -14,11 +14,11 @@ filenames = {
|
||||
|
||||
default_app_octicon_sources = [
|
||||
"node_modules/@primer/octicons/build/build.css",
|
||||
"node_modules/@primer/octicons/build/svg/gear.svg",
|
||||
"node_modules/@primer/octicons/build/svg/gift.svg",
|
||||
"node_modules/@primer/octicons/build/svg/gist.svg",
|
||||
"node_modules/@primer/octicons/build/svg/mark-github.svg",
|
||||
"node_modules/@primer/octicons/build/svg/star.svg",
|
||||
"node_modules/@primer/octicons/build/svg/book-24.svg",
|
||||
"node_modules/@primer/octicons/build/svg/code-square-24.svg",
|
||||
"node_modules/@primer/octicons/build/svg/gift-24.svg",
|
||||
"node_modules/@primer/octicons/build/svg/mark-github-16.svg",
|
||||
"node_modules/@primer/octicons/build/svg/star-fill-24.svg",
|
||||
]
|
||||
|
||||
lib_sources = [
|
||||
@@ -81,7 +81,6 @@ filenames = {
|
||||
"shell/browser/api/electron_api_native_theme.h",
|
||||
"shell/browser/api/electron_api_native_theme_mac.mm",
|
||||
"shell/browser/api/electron_api_net.cc",
|
||||
"shell/browser/api/electron_api_net.h",
|
||||
"shell/browser/api/electron_api_net_log.cc",
|
||||
"shell/browser/api/electron_api_net_log.h",
|
||||
"shell/browser/api/electron_api_notification.cc",
|
||||
@@ -357,8 +356,6 @@ filenames = {
|
||||
"shell/browser/ui/inspectable_web_contents.cc",
|
||||
"shell/browser/ui/inspectable_web_contents.h",
|
||||
"shell/browser/ui/inspectable_web_contents_delegate.h",
|
||||
"shell/browser/ui/inspectable_web_contents_impl.cc",
|
||||
"shell/browser/ui/inspectable_web_contents_impl.h",
|
||||
"shell/browser/ui/inspectable_web_contents_view.h",
|
||||
"shell/browser/ui/inspectable_web_contents_view_delegate.cc",
|
||||
"shell/browser/ui/inspectable_web_contents_view_delegate.h",
|
||||
@@ -401,6 +398,8 @@ filenames = {
|
||||
"shell/browser/ui/views/submenu_button.h",
|
||||
"shell/browser/ui/views/win_frame_view.cc",
|
||||
"shell/browser/ui/views/win_frame_view.h",
|
||||
"shell/browser/ui/webui/accessibility_ui.cc",
|
||||
"shell/browser/ui/webui/accessibility_ui.h",
|
||||
"shell/browser/ui/win/dialog_thread.cc",
|
||||
"shell/browser/ui/win/dialog_thread.h",
|
||||
"shell/browser/ui/win/electron_desktop_native_widget_aura.cc",
|
||||
|
||||
@@ -10,6 +10,7 @@ hunspell_dictionaries = [
|
||||
"//third_party/hunspell_dictionaries/en-AU-9-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/en-CA-9-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/en-GB-9-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/en-GB-oxendict-9-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/en-US-9-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/es-ES-3-0.bdic",
|
||||
"//third_party/hunspell_dictionaries/et-EE-3-0.bdic",
|
||||
|
||||
805
lib/asar/fs-wrapper.ts
Normal file
805
lib/asar/fs-wrapper.ts
Normal file
@@ -0,0 +1,805 @@
|
||||
import { Buffer } from 'buffer';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
|
||||
const asar = process._linkedBinding('electron_common_asar');
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
const Module = require('module');
|
||||
|
||||
const Promise: PromiseConstructor = global.Promise as any;
|
||||
|
||||
const envNoAsar = process.env.ELECTRON_NO_ASAR &&
|
||||
process.type !== 'browser' &&
|
||||
process.type !== 'renderer';
|
||||
const isAsarDisabled = () => process.noAsar || envNoAsar;
|
||||
|
||||
const internalBinding = (process as any).internalBinding;
|
||||
delete (process as any).internalBinding;
|
||||
|
||||
const nextTick = (functionToCall: Function, args: any[] = []) => {
|
||||
process.nextTick(() => functionToCall(...args));
|
||||
};
|
||||
|
||||
// Cache asar archive objects.
|
||||
const cachedArchives = new Map<string, NodeJS.AsarArchive>();
|
||||
|
||||
const getOrCreateArchive = (archivePath: string) => {
|
||||
const isCached = cachedArchives.has(archivePath);
|
||||
if (isCached) {
|
||||
return cachedArchives.get(archivePath);
|
||||
}
|
||||
|
||||
const newArchive = asar.createArchive(archivePath);
|
||||
if (!newArchive) return null;
|
||||
|
||||
cachedArchives.set(archivePath, newArchive);
|
||||
return newArchive;
|
||||
};
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
const splitPath = (archivePathOrBuffer: string | Buffer) => {
|
||||
// Shortcut for disabled asar.
|
||||
if (isAsarDisabled()) return { isAsar: <const>false };
|
||||
|
||||
// Check for a bad argument type.
|
||||
let archivePath = archivePathOrBuffer;
|
||||
if (Buffer.isBuffer(archivePathOrBuffer)) {
|
||||
archivePath = archivePathOrBuffer.toString();
|
||||
}
|
||||
if (typeof archivePath !== 'string') return { isAsar: <const>false };
|
||||
|
||||
return asar.splitPath(path.normalize(archivePath));
|
||||
};
|
||||
|
||||
// Convert asar archive's Stats object to fs's Stats object.
|
||||
let nextInode = 0;
|
||||
|
||||
const uid = process.getuid != null ? process.getuid() : 0;
|
||||
const gid = process.getgid != null ? process.getgid() : 0;
|
||||
|
||||
const fakeTime = new Date();
|
||||
|
||||
const asarStatsToFsStats = function (stats: NodeJS.AsarFileStat) {
|
||||
const { Stats, constants } = require('fs');
|
||||
|
||||
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR;
|
||||
|
||||
if (stats.isFile) {
|
||||
mode ^= constants.S_IFREG;
|
||||
} else if (stats.isDirectory) {
|
||||
mode ^= constants.S_IFDIR;
|
||||
} else if (stats.isLink) {
|
||||
mode ^= constants.S_IFLNK;
|
||||
}
|
||||
|
||||
return new Stats(
|
||||
1, // dev
|
||||
mode, // mode
|
||||
1, // nlink
|
||||
uid,
|
||||
gid,
|
||||
0, // rdev
|
||||
undefined, // blksize
|
||||
++nextInode, // ino
|
||||
stats.size,
|
||||
undefined, // blocks,
|
||||
fakeTime.getTime(), // atim_msec
|
||||
fakeTime.getTime(), // mtim_msec
|
||||
fakeTime.getTime(), // ctim_msec
|
||||
fakeTime.getTime() // birthtim_msec
|
||||
);
|
||||
};
|
||||
|
||||
const enum AsarError {
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
NOT_DIR = 'NOT_DIR',
|
||||
NO_ACCESS = 'NO_ACCESS',
|
||||
INVALID_ARCHIVE = 'INVALID_ARCHIVE'
|
||||
}
|
||||
|
||||
type AsarErrorObject = Error & { code?: string, errno?: number };
|
||||
|
||||
const createError = (errorType: AsarError, { asarPath, filePath }: { asarPath?: string, filePath?: string } = {}) => {
|
||||
let error: AsarErrorObject;
|
||||
switch (errorType) {
|
||||
case AsarError.NOT_FOUND:
|
||||
error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`);
|
||||
error.code = 'ENOENT';
|
||||
error.errno = -2;
|
||||
break;
|
||||
case AsarError.NOT_DIR:
|
||||
error = new Error('ENOTDIR, not a directory');
|
||||
error.code = 'ENOTDIR';
|
||||
error.errno = -20;
|
||||
break;
|
||||
case AsarError.NO_ACCESS:
|
||||
error = new Error(`EACCES: permission denied, access '${filePath}'`);
|
||||
error.code = 'EACCES';
|
||||
error.errno = -13;
|
||||
break;
|
||||
case AsarError.INVALID_ARCHIVE:
|
||||
error = new Error(`Invalid package ${asarPath}`);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid error type "${errorType}" passed to createError.`);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
const overrideAPISync = function (module: Record<string, any>, name: string, pathArgumentIndex?: number | null, fromAsync: boolean = false) {
|
||||
if (pathArgumentIndex == null) pathArgumentIndex = 0;
|
||||
const old = module[name];
|
||||
const func = function (this: any, ...args: any[]) {
|
||||
const pathArgument = args[pathArgumentIndex!];
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return old.apply(this, args);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
|
||||
const newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
args[pathArgumentIndex!] = newPath;
|
||||
return old.apply(this, args);
|
||||
};
|
||||
if (fromAsync) {
|
||||
return func;
|
||||
}
|
||||
module[name] = func;
|
||||
};
|
||||
|
||||
const overrideAPI = function (module: Record<string, any>, name: string, pathArgumentIndex?: number | null) {
|
||||
if (pathArgumentIndex == null) pathArgumentIndex = 0;
|
||||
const old = module[name];
|
||||
module[name] = function (this: any, ...args: any[]) {
|
||||
const pathArgument = args[pathArgumentIndex!];
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return old.apply(this, args);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const callback = args[args.length - 1];
|
||||
if (typeof callback !== 'function') {
|
||||
return overrideAPISync(module, name, pathArgumentIndex!, true)!.apply(this, args);
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
args[pathArgumentIndex!] = newPath;
|
||||
return old.apply(this, args);
|
||||
};
|
||||
|
||||
if (old[util.promisify.custom]) {
|
||||
module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex);
|
||||
}
|
||||
|
||||
if (module.promises && module.promises[name]) {
|
||||
module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex);
|
||||
}
|
||||
};
|
||||
|
||||
const makePromiseFunction = function (orig: Function, pathArgumentIndex: number) {
|
||||
return function (this: any, ...args: any[]) {
|
||||
const pathArgument = args[pathArgumentIndex];
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return orig.apply(this, args);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath }));
|
||||
}
|
||||
|
||||
const newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) {
|
||||
return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath }));
|
||||
}
|
||||
|
||||
args[pathArgumentIndex] = newPath;
|
||||
return orig.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Override fs APIs.
|
||||
export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
const logFDs: Record<string, number> = {};
|
||||
const logASARAccess = (asarPath: string, filePath: string, offset: number) => {
|
||||
if (!process.env.ELECTRON_LOG_ASAR_READS) return;
|
||||
if (!logFDs[asarPath]) {
|
||||
const path = require('path');
|
||||
const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`;
|
||||
const logPath = path.join(require('os').tmpdir(), logFilename);
|
||||
logFDs[asarPath] = fs.openSync(logPath, 'a');
|
||||
}
|
||||
fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`);
|
||||
};
|
||||
|
||||
const { lstatSync } = fs;
|
||||
fs.lstatSync = (pathArgument: string, options: any) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return lstatSync(pathArgument, options);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
return asarStatsToFsStats(stats);
|
||||
};
|
||||
|
||||
const { lstat } = fs;
|
||||
fs.lstat = function (pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!pathInfo.isAsar) return lstat(pathArgument, options, callback);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fsStats = asarStatsToFsStats(stats);
|
||||
nextTick(callback, [null, fsStats]);
|
||||
};
|
||||
|
||||
fs.promises.lstat = util.promisify(fs.lstat);
|
||||
|
||||
const { statSync } = fs;
|
||||
fs.statSync = (pathArgument: string, options: any) => {
|
||||
const { isAsar } = splitPath(pathArgument);
|
||||
if (!isAsar) return statSync(pathArgument, options);
|
||||
|
||||
// Do not distinguish links for now.
|
||||
return fs.lstatSync(pathArgument, options);
|
||||
};
|
||||
|
||||
const { stat } = fs;
|
||||
fs.stat = (pathArgument: string, options: any, callback: any) => {
|
||||
const { isAsar } = splitPath(pathArgument);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!isAsar) return stat(pathArgument, options, callback);
|
||||
|
||||
// Do not distinguish links for now.
|
||||
process.nextTick(() => fs.lstat(pathArgument, options, callback));
|
||||
};
|
||||
|
||||
fs.promises.stat = util.promisify(fs.stat);
|
||||
|
||||
const wrapRealpathSync = function (realpathSync: Function) {
|
||||
return function (this: any, pathArgument: string, options: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return realpathSync.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
}
|
||||
|
||||
const fileRealPath = archive.realpath(filePath);
|
||||
if (fileRealPath === false) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
return path.join(realpathSync(asarPath, options), fileRealPath);
|
||||
};
|
||||
};
|
||||
|
||||
const { realpathSync } = fs;
|
||||
fs.realpathSync = wrapRealpathSync(realpathSync);
|
||||
fs.realpathSync.native = wrapRealpathSync(realpathSync.native);
|
||||
|
||||
const wrapRealpath = function (realpath: Function) {
|
||||
return function (this: any, pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return realpath.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (arguments.length < 3) {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileRealPath = archive.realpath(filePath);
|
||||
if (fileRealPath === false) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
realpath(asarPath, options, (error: Error | null, archiveRealPath: string) => {
|
||||
if (error === null) {
|
||||
const fullPath = path.join(archiveRealPath, fileRealPath);
|
||||
callback(null, fullPath);
|
||||
} else {
|
||||
callback(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const { realpath } = fs;
|
||||
fs.realpath = wrapRealpath(realpath);
|
||||
fs.realpath.native = wrapRealpath(realpath.native);
|
||||
|
||||
fs.promises.realpath = util.promisify(fs.realpath.native);
|
||||
|
||||
const { exists } = fs;
|
||||
fs.exists = (pathArgument: string, callback: any) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return exists(pathArgument, callback);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const pathExists = (archive.stat(filePath) !== false);
|
||||
nextTick(callback, [pathExists]);
|
||||
};
|
||||
|
||||
fs.exists[util.promisify.custom] = (pathArgument: string) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return exists[util.promisify.custom](pathArgument);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return Promise.resolve(archive.stat(filePath) !== false);
|
||||
};
|
||||
|
||||
const { existsSync } = fs;
|
||||
fs.existsSync = (pathArgument: string) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return existsSync(pathArgument);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) return false;
|
||||
|
||||
return archive.stat(filePath) !== false;
|
||||
};
|
||||
|
||||
const { access } = fs;
|
||||
fs.access = function (pathArgument: string, mode: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return access.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (typeof mode === 'function') {
|
||||
callback = mode;
|
||||
mode = fs.constants.F_OK;
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.access(realPath, mode, callback);
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs.constants.W_OK) {
|
||||
const error = createError(AsarError.NO_ACCESS, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(callback);
|
||||
};
|
||||
|
||||
fs.promises.access = util.promisify(fs.access);
|
||||
|
||||
const { accessSync } = fs;
|
||||
fs.accessSync = function (pathArgument: string, mode: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return accessSync.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (mode == null) mode = fs.constants.F_OK;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.accessSync(realPath, mode);
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (mode & fs.constants.W_OK) {
|
||||
throw createError(AsarError.NO_ACCESS, { asarPath, filePath });
|
||||
}
|
||||
};
|
||||
|
||||
const { readFile } = fs;
|
||||
fs.readFile = function (pathArgument: string, options: any, callback: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return readFile.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (options === null || options === undefined) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.size === 0) {
|
||||
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
};
|
||||
|
||||
fs.promises.readFile = util.promisify(fs.readFile);
|
||||
|
||||
const { readFileSync } = fs;
|
||||
fs.readFileSync = function (pathArgument: string, options: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return readFileSync.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
if (info.size === 0) return (options) ? '' : Buffer.alloc(0);
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFileSync(realPath, options);
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||
return (encoding) ? buffer.toString(encoding) : buffer;
|
||||
};
|
||||
|
||||
const { readdir } = fs;
|
||||
fs.readdir = function (pathArgument: string, options: { encoding?: string | null; withFileTypes?: boolean } = {}, callback?: Function) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!pathInfo.isAsar) return readdir.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback!, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const files = archive.readdir(filePath);
|
||||
if (!files) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback!, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.withFileTypes) {
|
||||
const dirents = [];
|
||||
for (const file of files) {
|
||||
const stats = archive.stat(file);
|
||||
if (!stats) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath: file });
|
||||
nextTick(callback!, [error]);
|
||||
return;
|
||||
}
|
||||
if (stats.isFile) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
|
||||
} else if (stats.isDirectory) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
|
||||
} else if (stats.isLink) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
|
||||
}
|
||||
}
|
||||
nextTick(callback!, [null, dirents]);
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(callback!, [null, files]);
|
||||
};
|
||||
|
||||
fs.promises.readdir = util.promisify(fs.readdir);
|
||||
|
||||
const { readdirSync } = fs;
|
||||
fs.readdirSync = function (pathArgument: string, options: { encoding: BufferEncoding | null; withFileTypes?: false } | BufferEncoding | null) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return readdirSync.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
}
|
||||
|
||||
const files = archive.readdir(filePath);
|
||||
if (!files) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (options && (options as any).withFileTypes) {
|
||||
const dirents = [];
|
||||
for (const file of files) {
|
||||
const stats = archive.stat(file);
|
||||
if (!stats) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath: file });
|
||||
}
|
||||
if (stats.isFile) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
|
||||
} else if (stats.isDirectory) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
|
||||
} else if (stats.isLink) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
|
||||
}
|
||||
}
|
||||
return dirents;
|
||||
}
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
const { internalModuleReadJSON } = internalBinding('fs');
|
||||
internalBinding('fs').internalModuleReadJSON = (pathArgument: string) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return internalModuleReadJSON(pathArgument);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) return [];
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) return [];
|
||||
if (info.size === 0) return ['', false];
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFileSync(realPath, { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) return [];
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||
const str = buffer.toString('utf8');
|
||||
return [str, str.length > 0];
|
||||
};
|
||||
|
||||
const { internalModuleStat } = internalBinding('fs');
|
||||
internalBinding('fs').internalModuleStat = (pathArgument: string) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return internalModuleStat(pathArgument);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
// -ENOENT
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) return -34;
|
||||
|
||||
// -ENOENT
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) return -34;
|
||||
|
||||
return (stats.isDirectory) ? 1 : 0;
|
||||
};
|
||||
|
||||
// Calling mkdir for directory inside asar archive should throw ENOTDIR
|
||||
// error, but on Windows it throws ENOENT.
|
||||
if (process.platform === 'win32') {
|
||||
const { mkdir } = fs;
|
||||
fs.mkdir = (pathArgument: string, options: any, callback: any) => {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (pathInfo.isAsar && pathInfo.filePath.length > 0) {
|
||||
const error = createError(AsarError.NOT_DIR);
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
mkdir(pathArgument, options, callback);
|
||||
};
|
||||
|
||||
fs.promises.mkdir = util.promisify(fs.mkdir);
|
||||
|
||||
const { mkdirSync } = fs;
|
||||
fs.mkdirSync = function (pathArgument: string, options: any) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (pathInfo.isAsar && pathInfo.filePath.length) throw createError(AsarError.NOT_DIR);
|
||||
return mkdirSync(pathArgument, options);
|
||||
};
|
||||
}
|
||||
|
||||
function invokeWithNoAsar (func: Function) {
|
||||
return function (this: any) {
|
||||
const processNoAsarOriginalValue = process.noAsar;
|
||||
process.noAsar = true;
|
||||
try {
|
||||
return func.apply(this, arguments);
|
||||
} finally {
|
||||
process.noAsar = processNoAsarOriginalValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Strictly implementing the flags of fs.copyFile is hard, just do a simple
|
||||
// implementation for now. Doing 2 copies won't spend much time more as OS
|
||||
// has filesystem caching.
|
||||
overrideAPI(fs, 'copyFile');
|
||||
overrideAPISync(fs, 'copyFileSync');
|
||||
|
||||
overrideAPI(fs, 'open');
|
||||
overrideAPISync(process, 'dlopen', 1);
|
||||
overrideAPISync(Module._extensions, '.node', 1);
|
||||
overrideAPISync(fs, 'openSync');
|
||||
|
||||
const overrideChildProcess = (childProcess: Record<string, any>) => {
|
||||
// Executing a command string containing a path to an asar archive
|
||||
// confuses `childProcess.execFile`, which is internally called by
|
||||
// `childProcess.{exec,execSync}`, causing Electron to consider the full
|
||||
// command as a single path to an archive.
|
||||
const { exec, execSync } = childProcess;
|
||||
childProcess.exec = invokeWithNoAsar(exec);
|
||||
childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]);
|
||||
childProcess.execSync = invokeWithNoAsar(execSync);
|
||||
|
||||
overrideAPI(childProcess, 'execFile');
|
||||
overrideAPISync(childProcess, 'execFileSync');
|
||||
};
|
||||
|
||||
// Lazily override the child_process APIs only when child_process is
|
||||
// fetched the first time. We will eagerly override the child_process APIs
|
||||
// when this env var is set so that stack traces generated inside node unit
|
||||
// tests will match. This env var will only slow things down in users apps
|
||||
// and should not be used.
|
||||
if (process.env.ELECTRON_EAGER_ASAR_HOOK_FOR_TESTING) {
|
||||
overrideChildProcess(require('child_process'));
|
||||
} else {
|
||||
const originalModuleLoad = Module._load;
|
||||
Module._load = (request: string, ...args: any[]) => {
|
||||
const loadResult = originalModuleLoad(request, ...args);
|
||||
if (request === 'child_process') {
|
||||
if (!v8Util.getHiddenValue(loadResult, 'asar-ready')) {
|
||||
v8Util.setHiddenValue(loadResult, 'asar-ready', true);
|
||||
// Just to make it obvious what we are dealing with here
|
||||
const childProcess = loadResult;
|
||||
|
||||
overrideChildProcess(childProcess);
|
||||
}
|
||||
}
|
||||
return loadResult;
|
||||
};
|
||||
}
|
||||
};
|
||||
3
lib/asar/init.ts
Normal file
3
lib/asar/init.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { wrapFsWithAsar } from './fs-wrapper';
|
||||
|
||||
wrapFsWithAsar(require('fs'));
|
||||
21
lib/browser/.eslintrc.json
Normal file
21
lib/browser/.eslintrc.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
"electron",
|
||||
"electron/renderer"
|
||||
],
|
||||
"patterns": [
|
||||
"./*",
|
||||
"../*",
|
||||
"@electron/internal/isolated_renderer/*",
|
||||
"@electron/internal/renderer/*",
|
||||
"@electron/internal/sandboxed_worker/*",
|
||||
"@electron/internal/worker/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,36 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { deprecate, Menu } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate, Menu } from 'electron/main';
|
||||
|
||||
const bindings = process._linkedBinding('electron_browser_app');
|
||||
const commandLine = process._linkedBinding('electron_common_command_line');
|
||||
const { app, App } = bindings;
|
||||
const { app } = bindings;
|
||||
|
||||
// Only one app object permitted.
|
||||
export default app;
|
||||
|
||||
let dockMenu: Electron.Menu | null = null;
|
||||
|
||||
// App is an EventEmitter.
|
||||
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', {
|
||||
Object.defineProperty(app, 'accessibilitySupportEnabled', {
|
||||
get: () => nativeASGetter.call(app),
|
||||
set: (enabled) => nativeASSetter.call(app, enabled)
|
||||
});
|
||||
|
||||
const nativeBCGetter = app.getBadgeCount;
|
||||
const nativeBCSetter = app.setBadgeCount;
|
||||
Object.defineProperty(App.prototype, 'badgeCount', {
|
||||
Object.defineProperty(app, 'badgeCount', {
|
||||
get: () => nativeBCGetter.call(app),
|
||||
set: (count) => nativeBCSetter.call(app, count)
|
||||
});
|
||||
|
||||
const nativeNGetter = app.getName;
|
||||
const nativeNSetter = app.setName;
|
||||
Object.defineProperty(App.prototype, 'name', {
|
||||
Object.defineProperty(app, 'name', {
|
||||
get: () => nativeNGetter.call(app),
|
||||
set: (name) => nativeNSetter.call(app, name)
|
||||
});
|
||||
@@ -60,7 +55,7 @@ Object.defineProperty(app, 'applicationMenu', {
|
||||
}
|
||||
});
|
||||
|
||||
App.prototype.isPackaged = (() => {
|
||||
(app as any).isPackaged = (() => {
|
||||
const execFile = path.basename(process.execPath).toLowerCase();
|
||||
if (process.platform === 'win32') {
|
||||
return execFile !== 'electron.exe';
|
||||
@@ -137,4 +132,4 @@ for (const name of events) {
|
||||
|
||||
// Deprecate allowRendererProcessReuse but only if they set it to false, no need to log if
|
||||
// they are setting it to true
|
||||
deprecate.removeProperty(app, 'allowRendererProcessReuse', [false]);
|
||||
deprecate.removeProperty({ __proto__: app } as any, 'allowRendererProcessReuse', [false]);
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { EventEmitter } from 'events';
|
||||
const { autoUpdater, AutoUpdater } = process._linkedBinding('electron_browser_auto_updater');
|
||||
|
||||
// AutoUpdater is an EventEmitter.
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(autoUpdater);
|
||||
const { autoUpdater } = process._linkedBinding('electron_browser_auto_updater');
|
||||
|
||||
export default autoUpdater;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { app } from 'electron';
|
||||
import { app } from 'electron/main';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as squirrelUpdate from './squirrel-update-win';
|
||||
import * as squirrelUpdate from '@electron/internal/browser/api/auto-updater/squirrel-update-win';
|
||||
|
||||
class AutoUpdater extends EventEmitter {
|
||||
updateAvailable: boolean = false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import type { BaseWindow as TLWT } from 'electron';
|
||||
import type { BaseWindow as TLWT } from 'electron/main';
|
||||
const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as { BaseWindow: typeof TLWT };
|
||||
|
||||
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { BrowserView } = process._linkedBinding('electron_browser_browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents: Electron.WebContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default BrowserView;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron';
|
||||
import type { BrowserWindow as BWT } from 'electron';
|
||||
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron/main';
|
||||
import type { BrowserWindow as BWT } from 'electron/main';
|
||||
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, deprecate } from 'electron';
|
||||
import { app, deprecate } from 'electron/main';
|
||||
|
||||
const binding = process._linkedBinding('electron_browser_crash_reporter');
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import { OpenDialogOptions, OpenDialogReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, MessageBoxReturnValue, CertificateTrustDialogOptions } from 'electron/main';
|
||||
import { app, BrowserWindow } from 'electron/main';
|
||||
import type { OpenDialogOptions, OpenDialogReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, MessageBoxReturnValue, CertificateTrustDialogOptions } from 'electron/main';
|
||||
const dialogBinding = process._linkedBinding('electron_browser_dialog');
|
||||
|
||||
const DialogType = {
|
||||
|
||||
@@ -3,11 +3,7 @@ import { EventEmitter } from 'events';
|
||||
let _inAppPurchase;
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const { inAppPurchase, InAppPurchase } = process._linkedBinding('electron_browser_in_app_purchase');
|
||||
|
||||
// inAppPurchase is an EventEmitter.
|
||||
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(inAppPurchase);
|
||||
const { inAppPurchase } = process._linkedBinding('electron_browser_in_app_purchase');
|
||||
|
||||
_inAppPurchase = inAppPurchase;
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron';
|
||||
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron/main';
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as roles from './menu-item-roles';
|
||||
import { Menu, Event, BrowserWindow, WebContents } from 'electron';
|
||||
import * as roles from '@electron/internal/browser/api/menu-item-roles';
|
||||
import { Menu, Event, BrowserWindow, WebContents } from 'electron/main';
|
||||
|
||||
let nextCommandId = 0;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron';
|
||||
import { sortMenuItems } from './menu-utils';
|
||||
import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron/main';
|
||||
import { sortMenuItems } from '@electron/internal/browser/api/menu-utils';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const bindings = process._linkedBinding('electron_browser_menu');
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { NativeTheme, nativeTheme } = process._linkedBinding('electron_common_native_theme');
|
||||
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(nativeTheme as any);
|
||||
const { nativeTheme } = process._linkedBinding('electron_common_native_theme');
|
||||
|
||||
module.exports = nativeTheme;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
import { app, session } from 'electron';
|
||||
import { app, session } from 'electron/main';
|
||||
|
||||
const startLogging: typeof session.defaultSession.netLog.startLogging = async (path, options) => {
|
||||
if (!app.isReady()) return;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import * as url from 'url';
|
||||
import { Readable, Writable } from 'stream';
|
||||
import { app } from 'electron';
|
||||
import { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||
import { app } from 'electron/main';
|
||||
import type { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||
const {
|
||||
net,
|
||||
Net,
|
||||
isValidHeaderName,
|
||||
isValidHeaderValue,
|
||||
createURLLoader
|
||||
@@ -134,6 +132,7 @@ class SlurpStream extends Writable {
|
||||
this._data = Buffer.concat([this._data, chunk]);
|
||||
callback();
|
||||
}
|
||||
|
||||
data () { return this._data; }
|
||||
}
|
||||
|
||||
@@ -271,7 +270,7 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||
return urlLoaderOptions;
|
||||
}
|
||||
|
||||
class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
_started: boolean = false;
|
||||
_firstWrite: boolean = false;
|
||||
_aborted: boolean = false;
|
||||
@@ -494,10 +493,6 @@ class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
}
|
||||
}
|
||||
|
||||
Net.prototype.request = function (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
return new ClientRequest(options, callback);
|
||||
};
|
||||
|
||||
net.ClientRequest = ClientRequest;
|
||||
|
||||
module.exports = net;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { app } from 'electron';
|
||||
import { app } from 'electron/main';
|
||||
|
||||
const {
|
||||
createPowerMonitor,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, session } from 'electron';
|
||||
import { app, session } from 'electron/main';
|
||||
|
||||
// Global protocol APIs.
|
||||
const protocol = process._linkedBinding('electron_browser_protocol');
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
'use strict';
|
||||
const { createScreen } = process._linkedBinding('electron_common_screen');
|
||||
|
||||
import { createLazyInstance } from '../utils';
|
||||
const { EventEmitter } = require('events');
|
||||
const { Screen, createScreen } = process._linkedBinding('electron_common_screen');
|
||||
let _screen: Electron.Screen;
|
||||
|
||||
// Screen is an EventEmitter.
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
|
||||
|
||||
module.exports = createLazyInstance(createScreen, Screen, true);
|
||||
// We can't call createScreen until after app.on('ready'), but this module
|
||||
// exposes an instance created by createScreen. In order to avoid
|
||||
// side-effecting and calling createScreen upon import of this module, instead
|
||||
// we export a proxy which lazily calls createScreen on first access.
|
||||
export default new Proxy({}, {
|
||||
get: (target, prop) => {
|
||||
if (_screen === undefined) {
|
||||
_screen = createScreen();
|
||||
}
|
||||
const v = (_screen as any)[prop];
|
||||
if (typeof v === 'function') {
|
||||
return v.bind(_screen);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate } from 'electron';
|
||||
const { systemPreferences, SystemPreferences } = process._linkedBinding('electron_browser_system_preferences');
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(systemPreferences);
|
||||
import { deprecate } from 'electron/main';
|
||||
const { systemPreferences } = process._linkedBinding('electron_browser_system_preferences');
|
||||
|
||||
if ('getAppLevelAppearance' in systemPreferences) {
|
||||
const nativeALAGetter = systemPreferences.getAppLevelAppearance;
|
||||
const nativeALASetter = systemPreferences.setAppLevelAppearance;
|
||||
Object.defineProperty(SystemPreferences.prototype, 'appLevelAppearance', {
|
||||
Object.defineProperty(systemPreferences, 'appLevelAppearance', {
|
||||
get: () => nativeALAGetter.call(systemPreferences),
|
||||
set: (appearance) => nativeALASetter.call(systemPreferences, appearance)
|
||||
});
|
||||
@@ -17,25 +12,25 @@ if ('getAppLevelAppearance' in systemPreferences) {
|
||||
|
||||
if ('getEffectiveAppearance' in systemPreferences) {
|
||||
const nativeEAGetter = systemPreferences.getAppLevelAppearance;
|
||||
Object.defineProperty(SystemPreferences.prototype, 'effectiveAppearance', {
|
||||
Object.defineProperty(systemPreferences, 'effectiveAppearance', {
|
||||
get: () => nativeEAGetter.call(systemPreferences)
|
||||
});
|
||||
}
|
||||
|
||||
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isDarkMode,
|
||||
systemPreferences.isDarkMode = deprecate.moveAPI(
|
||||
systemPreferences.isDarkMode,
|
||||
'systemPreferences.isDarkMode()',
|
||||
'nativeTheme.shouldUseDarkColors'
|
||||
);
|
||||
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isInvertedColorScheme,
|
||||
systemPreferences.isInvertedColorScheme = deprecate.moveAPI(
|
||||
systemPreferences.isInvertedColorScheme,
|
||||
'systemPreferences.isInvertedColorScheme()',
|
||||
'nativeTheme.shouldUseInvertedColorScheme'
|
||||
);
|
||||
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
|
||||
SystemPreferences.prototype.isHighContrastColorScheme,
|
||||
systemPreferences.isHighContrastColorScheme = deprecate.moveAPI(
|
||||
systemPreferences.isHighContrastColorScheme,
|
||||
'systemPreferences.isHighContrastColorScheme()',
|
||||
'nativeTheme.shouldUseHighContrastColors'
|
||||
);
|
||||
|
||||
module.exports = systemPreferences;
|
||||
export default systemPreferences;
|
||||
|
||||
@@ -1,365 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (touchBar == null) {
|
||||
window._setTouchBarItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(touchBar)) {
|
||||
touchBar = new TouchBar(touchBar);
|
||||
}
|
||||
touchBar._addToWindow(window);
|
||||
}
|
||||
|
||||
constructor (options) {
|
||||
super();
|
||||
|
||||
if (options == null) {
|
||||
throw new Error('Must specify options object as first argument');
|
||||
}
|
||||
|
||||
let { items, escapeItem } = options;
|
||||
|
||||
if (!Array.isArray(items)) {
|
||||
items = [];
|
||||
}
|
||||
|
||||
this.changeListener = (item) => {
|
||||
this.emit('change', item.id, item.type);
|
||||
};
|
||||
|
||||
this.windowListeners = {};
|
||||
this.items = {};
|
||||
this.ordereredItems = [];
|
||||
this.escapeItem = escapeItem;
|
||||
|
||||
const registerItem = (item) => {
|
||||
this.items[item.id] = item;
|
||||
item.on('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
item.child.ordereredItems.forEach(registerItem);
|
||||
}
|
||||
};
|
||||
|
||||
let hasOtherItemsProxy = false;
|
||||
const idSet = new Set();
|
||||
items.forEach((item) => {
|
||||
if (!(item instanceof TouchBarItem)) {
|
||||
throw new Error('Each item must be an instance of TouchBarItem');
|
||||
}
|
||||
|
||||
if (item.type === 'other_items_proxy') {
|
||||
if (!hasOtherItemsProxy) {
|
||||
hasOtherItemsProxy = true;
|
||||
} else {
|
||||
throw new Error('Must only have one OtherItemsProxy per TouchBar');
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
this._escapeItem = item;
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.on('change', this.changeListener);
|
||||
}
|
||||
this.emit('escape-item-change', item);
|
||||
}
|
||||
|
||||
get escapeItem () {
|
||||
return this._escapeItem;
|
||||
}
|
||||
|
||||
_addToWindow (window) {
|
||||
const { id } = window;
|
||||
|
||||
// Already added to window
|
||||
if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return;
|
||||
|
||||
window._touchBar = this;
|
||||
|
||||
const changeListener = (itemID) => {
|
||||
window._refreshTouchBarItem(itemID);
|
||||
};
|
||||
this.on('change', changeListener);
|
||||
|
||||
const escapeItemListener = (item) => {
|
||||
window._setEscapeTouchBarItem(item != null ? item : {});
|
||||
};
|
||||
this.on('escape-item-change', escapeItemListener);
|
||||
|
||||
const interactionListener = (event, itemID, details) => {
|
||||
let item = this.items[itemID];
|
||||
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
|
||||
item = this.escapeItem;
|
||||
}
|
||||
if (item != null && item.onInteraction != null) {
|
||||
item.onInteraction(details);
|
||||
}
|
||||
};
|
||||
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];
|
||||
const unregisterItems = (items) => {
|
||||
for (const item of items) {
|
||||
item.removeListener('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
unregisterItems(item.child.ordereredItems);
|
||||
}
|
||||
}
|
||||
};
|
||||
unregisterItems(this.ordereredItems);
|
||||
if (this.escapeItem) {
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
};
|
||||
window.once('closed', removeListeners);
|
||||
this.windowListeners[id] = removeListeners;
|
||||
|
||||
window._setTouchBarItems(this.ordereredItems);
|
||||
escapeItemListener(this.escapeItem);
|
||||
}
|
||||
|
||||
_removeFromWindow (window) {
|
||||
const removeListeners = this.windowListeners[window.id];
|
||||
if (removeListeners != null) removeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class TouchBarItem extends EventEmitter {
|
||||
constructor () {
|
||||
super();
|
||||
this._addImmutableProperty('id', `${nextItemID++}`);
|
||||
this._parents = [];
|
||||
}
|
||||
|
||||
_addImmutableProperty (name, value) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function () {
|
||||
return value;
|
||||
},
|
||||
set: function () {
|
||||
throw new Error(`Cannot override property ${name}`);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
}
|
||||
|
||||
_addLiveProperty (name, initialValue) {
|
||||
const privateName = `_${name}`;
|
||||
this[privateName] = initialValue;
|
||||
Object.defineProperty(this, name, {
|
||||
get: function () {
|
||||
return this[privateName];
|
||||
},
|
||||
set: function (value) {
|
||||
this[privateName] = value;
|
||||
this.emit('change', this);
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
|
||||
_addParent (item) {
|
||||
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);
|
||||
if (typeof config.click === 'function') {
|
||||
this._addImmutableProperty('onInteraction', () => {
|
||||
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);
|
||||
|
||||
if (typeof config.change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (typeof config.change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (typeof config.change === 'function') {
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
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');
|
||||
|
||||
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 = () => {};
|
||||
this._addImmutableProperty('onInteraction', (details) => {
|
||||
if (details.type === 'select' && typeof select === 'function') {
|
||||
select(details.selectedIndex);
|
||||
} else if (details.type === 'highlight' && typeof highlight === 'function') {
|
||||
highlight(details.highlightedIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchBar.TouchBarOtherItemsProxy = class TouchBarOtherItemsProxy extends TouchBarItem {
|
||||
constructor (config) {
|
||||
super();
|
||||
this._addImmutableProperty('type', 'other_items_proxy');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TouchBar;
|
||||
459
lib/browser/api/touch-bar.ts
Normal file
459
lib/browser/api/touch-bar.ts
Normal file
@@ -0,0 +1,459 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
let nextItemID = 1;
|
||||
|
||||
const hiddenProperties = Symbol('hidden touch bar props');
|
||||
|
||||
const extendConstructHook = (target: any, hook: Function) => {
|
||||
const existingHook = target._hook;
|
||||
target._hook = function () {
|
||||
hook.call(this);
|
||||
if (existingHook) existingHook.call(this);
|
||||
};
|
||||
};
|
||||
|
||||
const ImmutableProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never, setInternalProp: <K extends keyof T>(k: K, v: T[K]) => void) => any) => (target: T, propertyKey: keyof T) => {
|
||||
extendConstructHook(target as any, function (this: T) {
|
||||
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config, (k, v) => {
|
||||
(this as any)[hiddenProperties][k] = v;
|
||||
});
|
||||
});
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
get: function () {
|
||||
return (this as any)[hiddenProperties][propertyKey];
|
||||
},
|
||||
set: function () {
|
||||
throw new Error(`Cannot override property ${name}`);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
};
|
||||
|
||||
const LiveProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never) => any, onMutate?: (self: T, newValue: any) => void) => (target: T, propertyKey: keyof T) => {
|
||||
extendConstructHook(target as any, function (this: T) {
|
||||
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config);
|
||||
if (onMutate) onMutate((this as any), (this as any)[hiddenProperties][propertyKey]);
|
||||
});
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
get: function () {
|
||||
return this[hiddenProperties][propertyKey];
|
||||
},
|
||||
set: function (value) {
|
||||
if (onMutate) onMutate((this as any), value);
|
||||
this[hiddenProperties][propertyKey] = value;
|
||||
this.emit('change', this);
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
};
|
||||
|
||||
abstract class TouchBarItem<ConfigType> extends EventEmitter {
|
||||
@ImmutableProperty(() => `${nextItemID++}`) id!: string;
|
||||
abstract type: string;
|
||||
abstract onInteraction: Function | null;
|
||||
child?: TouchBar;
|
||||
|
||||
private _parents: { id: string; type: string }[] = [];
|
||||
private _config!: ConfigType;
|
||||
|
||||
constructor (config: ConfigType) {
|
||||
super();
|
||||
this._config = this._config || config || {} as any;
|
||||
(this as any)[hiddenProperties] = {};
|
||||
const hook = (this as any)._hook;
|
||||
if (hook) hook.call(this);
|
||||
delete (this as any)._hook;
|
||||
}
|
||||
|
||||
public _addParent (item: TouchBarItem<any>) {
|
||||
const existing = this._parents.some(test => test.id === item.id);
|
||||
if (!existing) {
|
||||
this._parents.push({
|
||||
id: item.id,
|
||||
type: item.type
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public _removeParent (item: TouchBarItem<any>) {
|
||||
this._parents = this._parents.filter(test => test.id !== item.id);
|
||||
}
|
||||
}
|
||||
|
||||
class TouchBarButton extends TouchBarItem<Electron.TouchBarButtonConstructorOptions> implements Electron.TouchBarButton {
|
||||
@ImmutableProperty(() => 'button')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarButton>(config => config.label)
|
||||
label!: string;
|
||||
|
||||
@LiveProperty<TouchBarButton>(config => config.accessibilityLabel)
|
||||
accessibilityLabel!: string;
|
||||
|
||||
@LiveProperty<TouchBarButton>(config => config.backgroundColor)
|
||||
backgroundColor!: string;
|
||||
|
||||
@LiveProperty<TouchBarButton>(config => config.icon)
|
||||
icon!: Electron.NativeImage;
|
||||
|
||||
@LiveProperty<TouchBarButton>(config => config.iconPosition)
|
||||
iconPosition!: Electron.TouchBarButton['iconPosition'];
|
||||
|
||||
@LiveProperty<TouchBarButton>(config => typeof config.enabled !== 'boolean' ? true : config.enabled)
|
||||
enabled!: boolean;
|
||||
|
||||
@ImmutableProperty<TouchBarButton>(({ click: onClick }) => typeof onClick === 'function' ? () => onClick() : null)
|
||||
onInteraction!: Function | null;
|
||||
}
|
||||
|
||||
class TouchBarColorPicker extends TouchBarItem<Electron.TouchBarColorPickerConstructorOptions> implements Electron.TouchBarColorPicker {
|
||||
@ImmutableProperty(() => 'colorpicker')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarColorPicker>(config => config.availableColors)
|
||||
availableColors!: string[];
|
||||
|
||||
@LiveProperty<TouchBarColorPicker>(config => config.selectedColor)
|
||||
selectedColor!: string;
|
||||
|
||||
@ImmutableProperty<TouchBarColorPicker>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { color: string }) => {
|
||||
setInternalProp('selectedColor', details.color);
|
||||
onChange(details.color);
|
||||
} : null)
|
||||
onInteraction!: Function | null;
|
||||
}
|
||||
|
||||
class TouchBarGroup extends TouchBarItem<Electron.TouchBarGroupConstructorOptions> implements Electron.TouchBarGroup {
|
||||
@ImmutableProperty(() => 'group')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarGroup>(config => config.items instanceof TouchBar ? config.items : new TouchBar(config.items), (self, newChild: TouchBar) => {
|
||||
if (self.child) {
|
||||
for (const item of self.child.orderedItems) {
|
||||
item._removeParent(self);
|
||||
}
|
||||
}
|
||||
for (const item of newChild.orderedItems) {
|
||||
item._addParent(item);
|
||||
}
|
||||
})
|
||||
child!: TouchBar;
|
||||
|
||||
onInteraction = null;
|
||||
}
|
||||
|
||||
class TouchBarLabel extends TouchBarItem<Electron.TouchBarLabelConstructorOptions> implements Electron.TouchBarLabel {
|
||||
@ImmutableProperty(() => 'label')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarLabel>(config => config.label)
|
||||
label!: string;
|
||||
|
||||
@LiveProperty<TouchBarLabel>(config => config.accessibilityLabel)
|
||||
accessibilityLabel!: string;
|
||||
|
||||
@LiveProperty<TouchBarLabel>(config => config.textColor)
|
||||
textColor!: string;
|
||||
|
||||
onInteraction = null;
|
||||
}
|
||||
|
||||
class TouchBarPopover extends TouchBarItem<Electron.TouchBarPopoverConstructorOptions> implements Electron.TouchBarPopover {
|
||||
@ImmutableProperty(() => 'popover')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarPopover>(config => config.label)
|
||||
label!: string;
|
||||
|
||||
@LiveProperty<TouchBarPopover>(config => config.icon)
|
||||
icon!: Electron.NativeImage;
|
||||
|
||||
@LiveProperty<TouchBarPopover>(config => config.showCloseButton)
|
||||
showCloseButton!: boolean;
|
||||
|
||||
@LiveProperty<TouchBarPopover>(config => config.items instanceof TouchBar ? config.items : new TouchBar(config.items), (self, newChild: TouchBar) => {
|
||||
if (self.child) {
|
||||
for (const item of self.child.orderedItems) {
|
||||
item._removeParent(self);
|
||||
}
|
||||
}
|
||||
for (const item of newChild.orderedItems) {
|
||||
item._addParent(item);
|
||||
}
|
||||
})
|
||||
child!: TouchBar;
|
||||
|
||||
onInteraction = null;
|
||||
}
|
||||
|
||||
class TouchBarSlider extends TouchBarItem<Electron.TouchBarSliderConstructorOptions> implements Electron.TouchBarSlider {
|
||||
@ImmutableProperty(() => 'slider')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarSlider>(config => config.label)
|
||||
label!: string;
|
||||
|
||||
@LiveProperty<TouchBarSlider>(config => config.minValue)
|
||||
minValue!: number;
|
||||
|
||||
@LiveProperty<TouchBarSlider>(config => config.maxValue)
|
||||
maxValue!: number;
|
||||
|
||||
@LiveProperty<TouchBarSlider>(config => config.value)
|
||||
value!: number;
|
||||
|
||||
@ImmutableProperty<TouchBarSlider>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { value: number }) => {
|
||||
setInternalProp('value', details.value);
|
||||
onChange(details.value);
|
||||
} : null)
|
||||
onInteraction!: Function | null;
|
||||
}
|
||||
|
||||
class TouchBarSpacer extends TouchBarItem<Electron.TouchBarSpacerConstructorOptions> implements Electron.TouchBarSpacer {
|
||||
@ImmutableProperty(() => 'spacer')
|
||||
type!: string;
|
||||
|
||||
@ImmutableProperty<TouchBarSpacer>(config => config.size)
|
||||
size!: Electron.TouchBarSpacer['size'];
|
||||
|
||||
onInteraction = null;
|
||||
}
|
||||
|
||||
class TouchBarSegmentedControl extends TouchBarItem<Electron.TouchBarSegmentedControlConstructorOptions> implements Electron.TouchBarSegmentedControl {
|
||||
@ImmutableProperty(() => 'segmented_control')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarSegmentedControl>(config => config.segmentStyle)
|
||||
segmentStyle!: Electron.TouchBarSegmentedControl['segmentStyle'];
|
||||
|
||||
@LiveProperty<TouchBarSegmentedControl>(config => config.segments || [])
|
||||
segments!: Electron.SegmentedControlSegment[];
|
||||
|
||||
@LiveProperty<TouchBarSegmentedControl>(config => config.selectedIndex)
|
||||
selectedIndex!: number;
|
||||
|
||||
@LiveProperty<TouchBarSegmentedControl>(config => config.mode)
|
||||
mode!: Electron.TouchBarSegmentedControl['mode'];
|
||||
|
||||
@ImmutableProperty<TouchBarSegmentedControl>(({ change: onChange }, setInternalProp) => typeof onChange === 'function' ? (details: { selectedIndex: number, isSelected: boolean }) => {
|
||||
setInternalProp('selectedIndex', details.selectedIndex);
|
||||
onChange(details.selectedIndex, details.isSelected);
|
||||
} : null)
|
||||
onInteraction!: Function | null;
|
||||
}
|
||||
|
||||
class TouchBarScrubber extends TouchBarItem<Electron.TouchBarScrubberConstructorOptions> implements Electron.TouchBarScrubber {
|
||||
@ImmutableProperty(() => 'scrubber')
|
||||
type!: string;
|
||||
|
||||
@LiveProperty<TouchBarScrubber>(config => config.items)
|
||||
items!: Electron.ScrubberItem[];
|
||||
|
||||
@LiveProperty<TouchBarScrubber>(config => config.selectedStyle || null)
|
||||
selectedStyle!: Electron.TouchBarScrubber['selectedStyle'];
|
||||
|
||||
@LiveProperty<TouchBarScrubber>(config => config.overlayStyle || null)
|
||||
overlayStyle!: Electron.TouchBarScrubber['overlayStyle'];
|
||||
|
||||
@LiveProperty<TouchBarScrubber>(config => config.showArrowButtons || false)
|
||||
showArrowButtons!: boolean;
|
||||
|
||||
@LiveProperty<TouchBarScrubber>(config => config.mode || 'free')
|
||||
mode!: Electron.TouchBarScrubber['mode'];
|
||||
|
||||
@LiveProperty<TouchBarScrubber>(config => typeof config.continuous === 'undefined' ? true : config.continuous)
|
||||
continuous!: boolean;
|
||||
|
||||
@ImmutableProperty<TouchBarScrubber>(({ select: onSelect, highlight: onHighlight }) => typeof onSelect === 'function' || typeof onHighlight === 'function' ? (details: { type: 'select'; selectedIndex: number } | { type: 'highlight'; highlightedIndex: number }) => {
|
||||
if (details.type === 'select') {
|
||||
if (onSelect) onSelect(details.selectedIndex);
|
||||
} else {
|
||||
if (onHighlight) onHighlight(details.highlightedIndex);
|
||||
}
|
||||
} : null)
|
||||
onInteraction!: Function | null;
|
||||
}
|
||||
|
||||
class TouchBarOtherItemsProxy extends TouchBarItem<null> implements Electron.TouchBarOtherItemsProxy {
|
||||
@ImmutableProperty(() => 'other_items_proxy') type!: string;
|
||||
onInteraction = null;
|
||||
}
|
||||
|
||||
const escapeItemSymbol = Symbol('escape item');
|
||||
|
||||
class TouchBar extends EventEmitter implements Electron.TouchBar {
|
||||
// Bind a touch bar to a window
|
||||
static _setOnWindow (touchBar: TouchBar | Electron.TouchBarConstructorOptions['items'], window: Electron.BrowserWindow) {
|
||||
if (window._touchBar != null) {
|
||||
window._touchBar._removeFromWindow(window);
|
||||
}
|
||||
|
||||
if (!touchBar) {
|
||||
window._setTouchBarItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(touchBar)) {
|
||||
touchBar = new TouchBar({ items: touchBar });
|
||||
}
|
||||
touchBar._addToWindow(window);
|
||||
}
|
||||
|
||||
private windowListeners: Record<number, Function> = {};
|
||||
private items: Record<string, TouchBarItem<any>> = {};
|
||||
orderedItems: TouchBarItem<any>[] = [];
|
||||
|
||||
constructor (options: Electron.TouchBarConstructorOptions) {
|
||||
super();
|
||||
|
||||
if (options == null) {
|
||||
throw new Error('Must specify options object as first argument');
|
||||
}
|
||||
|
||||
let { items, escapeItem } = options;
|
||||
|
||||
if (!Array.isArray(items)) {
|
||||
items = [];
|
||||
}
|
||||
|
||||
this.windowListeners = {};
|
||||
this.items = {};
|
||||
this.escapeItem = (escapeItem as any) || null;
|
||||
|
||||
const registerItem = (item: TouchBarItem<any>) => {
|
||||
this.items[item.id] = item;
|
||||
item.on('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
item.child.orderedItems.forEach(registerItem);
|
||||
}
|
||||
};
|
||||
|
||||
let hasOtherItemsProxy = false;
|
||||
const idSet = new Set();
|
||||
items.forEach((item) => {
|
||||
if (!(item instanceof TouchBarItem)) {
|
||||
throw new Error('Each item must be an instance of TouchBarItem');
|
||||
}
|
||||
|
||||
if (item.type === 'other_items_proxy') {
|
||||
if (!hasOtherItemsProxy) {
|
||||
hasOtherItemsProxy = true;
|
||||
} else {
|
||||
throw new Error('Must only have one OtherItemsProxy per TouchBar');
|
||||
}
|
||||
}
|
||||
|
||||
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 as TouchBarItem<any>[])) {
|
||||
this.orderedItems.push(item);
|
||||
registerItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private changeListener = (item: TouchBarItem<any>) => {
|
||||
this.emit('change', item.id, item.type);
|
||||
};
|
||||
|
||||
private [escapeItemSymbol]: TouchBarItem<unknown> | null = null;
|
||||
|
||||
set escapeItem (item: TouchBarItem<unknown> | null) {
|
||||
if (item != null && !(item instanceof TouchBarItem)) {
|
||||
throw new Error('Escape item must be an instance of TouchBarItem');
|
||||
}
|
||||
const escapeItem = this.escapeItem;
|
||||
if (escapeItem) {
|
||||
escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
this[escapeItemSymbol] = item;
|
||||
if (this.escapeItem != null) {
|
||||
this.escapeItem.on('change', this.changeListener);
|
||||
}
|
||||
this.emit('escape-item-change', item);
|
||||
}
|
||||
|
||||
get escapeItem (): TouchBarItem<unknown> | null {
|
||||
return this[escapeItemSymbol];
|
||||
}
|
||||
|
||||
_addToWindow (window: Electron.BrowserWindow) {
|
||||
const { id } = window;
|
||||
|
||||
// Already added to window
|
||||
if (Object.prototype.hasOwnProperty.call(this.windowListeners, id)) return;
|
||||
|
||||
window._touchBar = this;
|
||||
|
||||
const changeListener = (itemID: string) => {
|
||||
window._refreshTouchBarItem(itemID);
|
||||
};
|
||||
this.on('change', changeListener);
|
||||
|
||||
const escapeItemListener = (item: Electron.TouchBarItemType | null) => {
|
||||
window._setEscapeTouchBarItem(item != null ? item : {});
|
||||
};
|
||||
this.on('escape-item-change', escapeItemListener);
|
||||
|
||||
const interactionListener = (_: any, itemID: string, details: any) => {
|
||||
let item = this.items[itemID];
|
||||
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
|
||||
item = this.escapeItem;
|
||||
}
|
||||
if (item != null && item.onInteraction != null) {
|
||||
item.onInteraction(details);
|
||||
}
|
||||
};
|
||||
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];
|
||||
const unregisterItems = (items: TouchBarItem<any>[]) => {
|
||||
for (const item of items) {
|
||||
item.removeListener('change', this.changeListener);
|
||||
if (item.child instanceof TouchBar) {
|
||||
unregisterItems(item.child.orderedItems);
|
||||
}
|
||||
}
|
||||
};
|
||||
unregisterItems(this.orderedItems);
|
||||
if (this.escapeItem) {
|
||||
this.escapeItem.removeListener('change', this.changeListener);
|
||||
}
|
||||
};
|
||||
window.once('closed', removeListeners);
|
||||
this.windowListeners[id] = removeListeners;
|
||||
|
||||
window._setTouchBarItems(this.orderedItems);
|
||||
escapeItemListener(this.escapeItem);
|
||||
}
|
||||
|
||||
_removeFromWindow (window: Electron.BrowserWindow) {
|
||||
const removeListeners = this.windowListeners[window.id];
|
||||
if (removeListeners != null) removeListeners();
|
||||
}
|
||||
|
||||
static TouchBarButton = TouchBarButton;
|
||||
static TouchBarColorPicker = TouchBarColorPicker;
|
||||
static TouchBarGroup = TouchBarGroup;
|
||||
static TouchBarLabel = TouchBarLabel;
|
||||
static TouchBarPopover = TouchBarPopover;
|
||||
static TouchBarSlider = TouchBarSlider;
|
||||
static TouchBarSpacer = TouchBarSpacer;
|
||||
static TouchBarSegmentedControl = TouchBarSegmentedControl;
|
||||
static TouchBarScrubber = TouchBarScrubber;
|
||||
static TouchBarOtherItemsProxy = TouchBarOtherItemsProxy;
|
||||
}
|
||||
|
||||
export default TouchBar;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { View } from 'electron';
|
||||
import { View } from 'electron/main';
|
||||
|
||||
const { ImageView } = process._linkedBinding('electron_browser_image_view');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { View } from 'electron';
|
||||
import { View } from 'electron/main';
|
||||
|
||||
const { WebContentsView } = process._linkedBinding('electron_browser_web_contents_view');
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { app, ipcMain, session, deprecate } from 'electron';
|
||||
import type { MenuItem, MenuItemConstructorOptions, WebContentsInternal } from 'electron';
|
||||
import { app, ipcMain, session, deprecate } from 'electron/main';
|
||||
import type { MenuItem, MenuItemConstructorOptions } from 'electron/main';
|
||||
|
||||
import * as url from 'url';
|
||||
import * as path from 'path';
|
||||
import { internalWindowOpen } from '../guest-window-manager';
|
||||
import { NavigationController } from '../navigation-controller';
|
||||
import { ipcMainInternal } from '../ipc-main-internal';
|
||||
import * as ipcMainUtils from '../ipc-main-internal-utils';
|
||||
import { parseFeatures } from '../../common/parse-features-string';
|
||||
import { MessagePortMain } from '../message-port-main';
|
||||
import { EventEmitter } from 'events';
|
||||
import { internalWindowOpen } from '@electron/internal/browser/guest-window-manager';
|
||||
import { NavigationController } from '@electron/internal/browser/navigation-controller';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||
import { parseFeatures } from '@electron/internal/common/parse-features-string';
|
||||
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
|
||||
|
||||
// session is not used here, the purpose is to make sure session is initalized
|
||||
// before the webContents module.
|
||||
@@ -72,6 +71,19 @@ const PDFPageSizes: Record<string, MediaSize> = {
|
||||
}
|
||||
};
|
||||
|
||||
// The minimum micron size Chromium accepts is that where:
|
||||
// Per printing/units.h:
|
||||
// * kMicronsPerInch - Length of an inch in 0.001mm unit.
|
||||
// * kPointsPerInch - Length of an inch in CSS's 1pt unit.
|
||||
//
|
||||
// Formula: (kPointsPerInch / kMicronsPerInch) * size >= 1
|
||||
//
|
||||
// Practically, this means microns need to be > 352 microns.
|
||||
// We therefore need to verify this or it will silently fail.
|
||||
const isValidCustomPageSize = (width: number, height: number) => {
|
||||
return [width, height].every(x => x > 352);
|
||||
};
|
||||
|
||||
// Default printing setting
|
||||
const defaultPrintingSetting = {
|
||||
// Customizable.
|
||||
@@ -109,9 +121,7 @@ const defaultPrintingSetting = {
|
||||
|
||||
// JavaScript implementations of WebContents.
|
||||
const binding = process._linkedBinding('electron_browser_web_contents');
|
||||
const { WebContents } = binding as { WebContents: { prototype: WebContentsInternal } };
|
||||
|
||||
Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype);
|
||||
const { WebContents } = binding as { WebContents: { prototype: Electron.WebContentsInternal } };
|
||||
|
||||
WebContents.prototype.send = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
@@ -190,7 +200,7 @@ for (const method of webFrameMethods) {
|
||||
};
|
||||
}
|
||||
|
||||
const waitTillCanExecuteJavaScript = async (webContents: WebContentsInternal) => {
|
||||
const waitTillCanExecuteJavaScript = async (webContents: Electron.WebContentsInternal) => {
|
||||
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
@@ -212,7 +222,9 @@ WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (worldId
|
||||
};
|
||||
|
||||
// Translate the options of printToPDF.
|
||||
WebContents.prototype.printToPDF = function (options) {
|
||||
|
||||
let pendingPromise: Promise<any> | undefined;
|
||||
WebContents.prototype.printToPDF = async function (options) {
|
||||
const printSettings = {
|
||||
...defaultPrintingSetting,
|
||||
requestID: getNextId()
|
||||
@@ -315,13 +327,20 @@ WebContents.prototype.printToPDF = function (options) {
|
||||
const error = new Error('height and width properties are required for pageSize');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
// Dimensions in Microns
|
||||
// 1 meter = 10^6 microns
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
const error = new Error('height and width properties must be minimum 352 microns.');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
printSettings.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: Math.ceil(pageSize.height),
|
||||
width_microns: Math.ceil(pageSize.width)
|
||||
height_microns: height,
|
||||
width_microns: width
|
||||
};
|
||||
} else if (Object.prototype.hasOwnProperty.call(PDFPageSizes, pageSize)) {
|
||||
printSettings.mediaSize = PDFPageSizes[pageSize];
|
||||
@@ -338,7 +357,12 @@ WebContents.prototype.printToPDF = function (options) {
|
||||
// PrinterType enum from //printing/print_job_constants.h
|
||||
printSettings.printerType = 2;
|
||||
if (this._printToPDF) {
|
||||
return this._printToPDF(printSettings);
|
||||
if (pendingPromise) {
|
||||
pendingPromise = pendingPromise.then(() => this._printToPDF(printSettings));
|
||||
} else {
|
||||
pendingPromise = this._printToPDF(printSettings);
|
||||
}
|
||||
return pendingPromise;
|
||||
} else {
|
||||
const error = new Error('Printing feature is disabled');
|
||||
return Promise.reject(error);
|
||||
@@ -356,12 +380,19 @@ WebContents.prototype.print = function (options = {}, callback) {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
|
||||
(options as any).mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: Math.ceil(pageSize.height),
|
||||
width_microns: Math.ceil(pageSize.width)
|
||||
height_microns: height,
|
||||
width_microns: width
|
||||
};
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
(options as any).mediaSize = PDFPageSizes[pageSize];
|
||||
@@ -456,7 +487,7 @@ WebContents.prototype._init = function () {
|
||||
this.setMaxListeners(0);
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-message' as any, function (this: Electron.WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
if (internal) {
|
||||
addReplyInternalToEvent(event);
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
@@ -481,7 +512,7 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-message-sync' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-message-sync' as any, function (this: Electron.WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
addReturnValueToEvent(event);
|
||||
if (internal) {
|
||||
addReplyInternalToEvent(event);
|
||||
@@ -519,7 +550,7 @@ WebContents.prototype._init = function () {
|
||||
});
|
||||
|
||||
// The devtools requests the webContents to reload.
|
||||
this.on('devtools-reload-page', function (this: WebContentsInternal) {
|
||||
this.on('devtools-reload-page', function (this: Electron.WebContentsInternal) {
|
||||
this.reload();
|
||||
});
|
||||
|
||||
@@ -542,7 +573,7 @@ WebContents.prototype._init = function () {
|
||||
|
||||
// Create a new browser window for the native implementation of
|
||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
||||
this.on('-add-new-contents' as any, (event: any, webContents: WebContentsInternal, disposition: string,
|
||||
this.on('-add-new-contents' as any, (event: any, webContents: Electron.WebContentsInternal, disposition: string,
|
||||
userGesture: boolean, left: number, top: number, width: number, height: number, url: string, frameName: string,
|
||||
referrer: string, rawFeatures: string, postData: string) => {
|
||||
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// BrowserWindow-based extensions stuff to the new native-backed extensions
|
||||
// API.
|
||||
|
||||
import { app, session, BrowserWindow, deprecate } from 'electron';
|
||||
import { app, session, BrowserWindow, deprecate } from 'electron/main';
|
||||
|
||||
app.whenReady().then(function () {
|
||||
const addExtension = function (srcDirectory: string) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { shell, Menu } from 'electron';
|
||||
import { Menu } from 'electron/main';
|
||||
import { shell } from 'electron/common';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { dialog, Menu } from 'electron';
|
||||
import { dialog, Menu } from 'electron/main';
|
||||
import * as fs from 'fs';
|
||||
import * as url from 'url';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { IpcMainInvokeEvent } from 'electron';
|
||||
import { IpcMainInvokeEvent } from 'electron/main';
|
||||
|
||||
export class IpcMainImpl extends EventEmitter {
|
||||
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ipcMainInternal } from './ipc-main-internal';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import type { WebContents, LoadURLOptions } from 'electron/main';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
import { WebContents } from 'electron';
|
||||
import { WebContents } from 'electron/main';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import * as electron from 'electron';
|
||||
import * as electron from 'electron/main';
|
||||
import { EventEmitter } from 'events';
|
||||
import objectsRegistry from './objects-registry';
|
||||
import { ipcMainInternal } from '../ipc-main-internal';
|
||||
import { isPromise, isSerializableObject, deserialize, serialize } from '../../common/type-utils';
|
||||
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '../../common/remote/types';
|
||||
import objectsRegistry from '@electron/internal/browser/remote/objects-registry';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils';
|
||||
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '@electron/internal/common/remote/types';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
const { NativeImage } = process._linkedBinding('electron_common_native_image');
|
||||
|
||||
if (!features.isRemoteModuleEnabled()) {
|
||||
throw new Error('remote module is disabled');
|
||||
@@ -102,7 +101,7 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v
|
||||
// Recognize certain types of objects.
|
||||
if (value instanceof Buffer) {
|
||||
type = 'buffer';
|
||||
} else if (value instanceof NativeImage) {
|
||||
} else if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
||||
type = 'nativeimage';
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'array';
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import * as electron from 'electron';
|
||||
import { app } from 'electron/main';
|
||||
import type { IpcMainInvokeEvent, WebContents } from 'electron/main';
|
||||
import { clipboard, crashReporter } from 'electron/common';
|
||||
import * as fs from 'fs';
|
||||
import { ipcMainInternal } from './ipc-main-internal';
|
||||
import * as ipcMainUtils from './ipc-main-internal-utils';
|
||||
import * as guestViewManager from './guest-view-manager';
|
||||
import * as typeUtils from '../common/type-utils';
|
||||
import { IpcMainInvokeEvent } from 'electron/main';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||
import * as guestViewManager from '@electron/internal/browser/guest-view-manager';
|
||||
import * as typeUtils from '@electron/internal/common/type-utils';
|
||||
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
const clipboard = process._linkedBinding('electron_common_clipboard');
|
||||
|
||||
const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) {
|
||||
const emitCustomEvent = function (contents: WebContents, eventName: string, ...args: any[]) {
|
||||
const event = eventBinding.createWithSender(contents);
|
||||
|
||||
electron.app.emit(eventName, event, contents, ...args);
|
||||
app.emit(eventName, event, contents, ...args);
|
||||
contents.emit(eventName, event, ...args);
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
const logStack = function (contents: electron.WebContents, code: string, stack: string) {
|
||||
const logStack = function (contents: WebContents, code: string, stack: string) {
|
||||
if (stack) {
|
||||
console.warn(`WebContents (${contents.id}): ${code}`, stack);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD_SYNC', function (event: IpcM
|
||||
throw new Error(`Invalid method: ${method}`);
|
||||
}
|
||||
|
||||
return typeUtils.serialize((electron.clipboard as any)[method](...typeUtils.deserialize(args)));
|
||||
return typeUtils.serialize((clipboard as any)[method](...typeUtils.deserialize(args)));
|
||||
});
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
@@ -118,21 +118,21 @@ ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event: ElectronIn
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_LAST_CRASH_REPORT', () => {
|
||||
return electron.crashReporter.getLastCrashReport();
|
||||
return crashReporter.getLastCrashReport();
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOADED_REPORTS', () => {
|
||||
return electron.crashReporter.getUploadedReports();
|
||||
return crashReporter.getUploadedReports();
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER', () => {
|
||||
return electron.crashReporter.getUploadToServer();
|
||||
return crashReporter.getUploadToServer();
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', (event: IpcMainInvokeEvent, uploadToServer: boolean) => {
|
||||
return electron.crashReporter.setUploadToServer(uploadToServer);
|
||||
return crashReporter.setUploadToServer(uploadToServer);
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_CRASHES_DIRECTORY', () => {
|
||||
return electron.crashReporter.getCrashesDirectory();
|
||||
return crashReporter.getCrashesDirectory();
|
||||
});
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
/**
|
||||
* Creates a lazy instance of modules that can't be required before the
|
||||
* app 'ready' event by returning a proxy object to mitigate side effects
|
||||
* on 'require'
|
||||
*
|
||||
* @param {Function} creator - Function that creates a new module instance
|
||||
* @param {Object} holder - the object holding the module prototype
|
||||
* @param {Boolean} isEventEmitter - whether or not the module is an EventEmitter
|
||||
* @returns {Object} - a proxy object for the
|
||||
*/
|
||||
|
||||
export function createLazyInstance (
|
||||
creator: Function,
|
||||
holder: Object,
|
||||
isEventEmitter: Boolean
|
||||
) {
|
||||
let lazyModule: Object;
|
||||
const module: any = {};
|
||||
for (const method in (holder as any).prototype) { // eslint-disable-line guard-for-in
|
||||
module[method] = (...args: any) => {
|
||||
// create new instance of module at runtime if none exists
|
||||
if (!lazyModule) {
|
||||
lazyModule = creator();
|
||||
if (isEventEmitter) EventEmitter.call(lazyModule as any);
|
||||
}
|
||||
|
||||
// check for properties on the prototype chain that aren't functions
|
||||
if (typeof (lazyModule as any)[method] !== 'function') {
|
||||
return (lazyModule as any)[method];
|
||||
}
|
||||
|
||||
return (lazyModule as any)[method](...args);
|
||||
};
|
||||
}
|
||||
return module;
|
||||
}
|
||||
@@ -1,783 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
const asar = process._linkedBinding('electron_common_asar');
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const { Buffer } = require('buffer');
|
||||
const Module = require('module');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
|
||||
const Promise = global.Promise;
|
||||
|
||||
const envNoAsar = process.env.ELECTRON_NO_ASAR &&
|
||||
process.type !== 'browser' &&
|
||||
process.type !== 'renderer';
|
||||
const isAsarDisabled = () => process.noAsar || envNoAsar;
|
||||
|
||||
const internalBinding = process.internalBinding;
|
||||
delete process.internalBinding;
|
||||
|
||||
/**
|
||||
* @param {!Function} functionToCall
|
||||
* @param {!Array|undefined} args
|
||||
*/
|
||||
const nextTick = (functionToCall, args = []) => {
|
||||
process.nextTick(() => functionToCall(...args));
|
||||
};
|
||||
|
||||
// Cache asar archive objects.
|
||||
const cachedArchives = new Map();
|
||||
|
||||
const getOrCreateArchive = archivePath => {
|
||||
const isCached = cachedArchives.has(archivePath);
|
||||
if (isCached) {
|
||||
return cachedArchives.get(archivePath);
|
||||
}
|
||||
|
||||
const newArchive = asar.createArchive(archivePath);
|
||||
if (!newArchive) return null;
|
||||
|
||||
cachedArchives.set(archivePath, newArchive);
|
||||
return newArchive;
|
||||
};
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
const splitPath = archivePathOrBuffer => {
|
||||
// Shortcut for disabled asar.
|
||||
if (isAsarDisabled()) return { isAsar: false };
|
||||
|
||||
// Check for a bad argument type.
|
||||
let archivePath = archivePathOrBuffer;
|
||||
if (Buffer.isBuffer(archivePathOrBuffer)) {
|
||||
archivePath = archivePathOrBuffer.toString();
|
||||
}
|
||||
if (typeof archivePath !== 'string') return { isAsar: false };
|
||||
|
||||
return asar.splitPath(path.normalize(archivePath));
|
||||
};
|
||||
|
||||
// Convert asar archive's Stats object to fs's Stats object.
|
||||
let nextInode = 0;
|
||||
|
||||
const uid = process.getuid != null ? process.getuid() : 0;
|
||||
const gid = process.getgid != null ? process.getgid() : 0;
|
||||
|
||||
const fakeTime = new Date();
|
||||
const msec = (date) => (date || fakeTime).getTime();
|
||||
|
||||
const asarStatsToFsStats = function (stats) {
|
||||
const { Stats, constants } = require('fs');
|
||||
|
||||
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR;
|
||||
|
||||
if (stats.isFile) {
|
||||
mode ^= constants.S_IFREG;
|
||||
} else if (stats.isDirectory) {
|
||||
mode ^= constants.S_IFDIR;
|
||||
} else if (stats.isLink) {
|
||||
mode ^= constants.S_IFLNK;
|
||||
}
|
||||
|
||||
return new Stats(
|
||||
1, // dev
|
||||
mode, // mode
|
||||
1, // nlink
|
||||
uid,
|
||||
gid,
|
||||
0, // rdev
|
||||
undefined, // blksize
|
||||
++nextInode, // ino
|
||||
stats.size,
|
||||
undefined, // blocks,
|
||||
msec(stats.atime), // atim_msec
|
||||
msec(stats.mtime), // mtim_msec
|
||||
msec(stats.ctime), // ctim_msec
|
||||
msec(stats.birthtime) // birthtim_msec
|
||||
);
|
||||
};
|
||||
|
||||
const AsarError = {
|
||||
NOT_FOUND: 'NOT_FOUND',
|
||||
NOT_DIR: 'NOT_DIR',
|
||||
NO_ACCESS: 'NO_ACCESS',
|
||||
INVALID_ARCHIVE: 'INVALID_ARCHIVE'
|
||||
};
|
||||
|
||||
const createError = (errorType, { asarPath, filePath } = {}) => {
|
||||
let error;
|
||||
switch (errorType) {
|
||||
case AsarError.NOT_FOUND:
|
||||
error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`);
|
||||
error.code = 'ENOENT';
|
||||
error.errno = -2;
|
||||
break;
|
||||
case AsarError.NOT_DIR:
|
||||
error = new Error('ENOTDIR, not a directory');
|
||||
error.code = 'ENOTDIR';
|
||||
error.errno = -20;
|
||||
break;
|
||||
case AsarError.NO_ACCESS:
|
||||
error = new Error(`EACCES: permission denied, access '${filePath}'`);
|
||||
error.code = 'EACCES';
|
||||
error.errno = -13;
|
||||
break;
|
||||
case AsarError.INVALID_ARCHIVE:
|
||||
error = new Error(`Invalid package ${asarPath}`);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid error type "${errorType}" passed to createError.`);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
const overrideAPISync = function (module, name, pathArgumentIndex, fromAsync) {
|
||||
if (pathArgumentIndex == null) pathArgumentIndex = 0;
|
||||
const old = module[name];
|
||||
const func = function () {
|
||||
const pathArgument = arguments[pathArgumentIndex];
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return old.apply(this, arguments);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
|
||||
const newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
arguments[pathArgumentIndex] = newPath;
|
||||
return old.apply(this, arguments);
|
||||
};
|
||||
if (fromAsync) {
|
||||
return func;
|
||||
}
|
||||
module[name] = func;
|
||||
};
|
||||
|
||||
const overrideAPI = function (module, name, pathArgumentIndex) {
|
||||
if (pathArgumentIndex == null) pathArgumentIndex = 0;
|
||||
const old = module[name];
|
||||
module[name] = function () {
|
||||
const pathArgument = arguments[pathArgumentIndex];
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return old.apply(this, arguments);
|
||||
|
||||
const callback = arguments[arguments.length - 1];
|
||||
if (typeof callback !== 'function') {
|
||||
return overrideAPISync(module, name, pathArgumentIndex, true).apply(this, arguments);
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
arguments[pathArgumentIndex] = newPath;
|
||||
return old.apply(this, arguments);
|
||||
};
|
||||
|
||||
if (old[util.promisify.custom]) {
|
||||
module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex);
|
||||
}
|
||||
|
||||
if (module.promises && module.promises[name]) {
|
||||
module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex);
|
||||
}
|
||||
};
|
||||
|
||||
const makePromiseFunction = function (orig, pathArgumentIndex) {
|
||||
return function (...args) {
|
||||
const pathArgument = args[pathArgumentIndex];
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return orig.apply(this, args);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath }));
|
||||
}
|
||||
|
||||
const newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) {
|
||||
return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath }));
|
||||
}
|
||||
|
||||
args[pathArgumentIndex] = newPath;
|
||||
return orig.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Override fs APIs.
|
||||
exports.wrapFsWithAsar = fs => {
|
||||
const logFDs = {};
|
||||
const logASARAccess = (asarPath, filePath, offset) => {
|
||||
if (!process.env.ELECTRON_LOG_ASAR_READS) return;
|
||||
if (!logFDs[asarPath]) {
|
||||
const path = require('path');
|
||||
const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`;
|
||||
const logPath = path.join(require('os').tmpdir(), logFilename);
|
||||
logFDs[asarPath] = fs.openSync(logPath, 'a');
|
||||
}
|
||||
fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`);
|
||||
};
|
||||
|
||||
const { lstatSync } = fs;
|
||||
fs.lstatSync = (pathArgument, options) => {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return lstatSync(pathArgument, options);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
return asarStatsToFsStats(stats);
|
||||
};
|
||||
|
||||
const { lstat } = fs;
|
||||
fs.lstat = function (pathArgument, options, callback) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!isAsar) return lstat(pathArgument, options, callback);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fsStats = asarStatsToFsStats(stats);
|
||||
nextTick(callback, [null, fsStats]);
|
||||
};
|
||||
|
||||
fs.promises.lstat = util.promisify(fs.lstat);
|
||||
|
||||
const { statSync } = fs;
|
||||
fs.statSync = (pathArgument, options) => {
|
||||
const { isAsar } = splitPath(pathArgument);
|
||||
if (!isAsar) return statSync(pathArgument, options);
|
||||
|
||||
// Do not distinguish links for now.
|
||||
return fs.lstatSync(pathArgument, options);
|
||||
};
|
||||
|
||||
const { stat } = fs;
|
||||
fs.stat = (pathArgument, options, callback) => {
|
||||
const { isAsar } = splitPath(pathArgument);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!isAsar) return stat(pathArgument, options, callback);
|
||||
|
||||
// Do not distinguish links for now.
|
||||
process.nextTick(() => fs.lstat(pathArgument, options, callback));
|
||||
};
|
||||
|
||||
fs.promises.stat = util.promisify(fs.stat);
|
||||
|
||||
const wrapRealpathSync = function (realpathSync) {
|
||||
return function (pathArgument, options) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return realpathSync.apply(this, arguments);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
}
|
||||
|
||||
const fileRealPath = archive.realpath(filePath);
|
||||
if (fileRealPath === false) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
return path.join(realpathSync(asarPath, options), fileRealPath);
|
||||
};
|
||||
};
|
||||
|
||||
const { realpathSync } = fs;
|
||||
fs.realpathSync = wrapRealpathSync(realpathSync);
|
||||
fs.realpathSync.native = wrapRealpathSync(realpathSync.native);
|
||||
|
||||
const wrapRealpath = function (realpath) {
|
||||
return function (pathArgument, options, callback) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return realpath.apply(this, arguments);
|
||||
|
||||
if (arguments.length < 3) {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileRealPath = archive.realpath(filePath);
|
||||
if (fileRealPath === false) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
realpath(asarPath, options, (error, archiveRealPath) => {
|
||||
if (error === null) {
|
||||
const fullPath = path.join(archiveRealPath, fileRealPath);
|
||||
callback(null, fullPath);
|
||||
} else {
|
||||
callback(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const { realpath } = fs;
|
||||
fs.realpath = wrapRealpath(realpath);
|
||||
fs.realpath.native = wrapRealpath(realpath.native);
|
||||
|
||||
fs.promises.realpath = util.promisify(fs.realpath.native);
|
||||
|
||||
const { exists } = fs;
|
||||
fs.exists = (pathArgument, callback) => {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return exists(pathArgument, callback);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const pathExists = (archive.stat(filePath) !== false);
|
||||
nextTick(callback, [pathExists]);
|
||||
};
|
||||
|
||||
fs.exists[util.promisify.custom] = pathArgument => {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return exists[util.promisify.custom](pathArgument);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return Promise.resolve(archive.stat(filePath) !== false);
|
||||
};
|
||||
|
||||
const { existsSync } = fs;
|
||||
fs.existsSync = pathArgument => {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return existsSync(pathArgument);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) return false;
|
||||
|
||||
return archive.stat(filePath) !== false;
|
||||
};
|
||||
|
||||
const { access } = fs;
|
||||
fs.access = function (pathArgument, mode, callback) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return access.apply(this, arguments);
|
||||
|
||||
if (typeof mode === 'function') {
|
||||
callback = mode;
|
||||
mode = fs.constants.F_OK;
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.access(realPath, mode, callback);
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs.constants.W_OK) {
|
||||
const error = createError(AsarError.NO_ACCESS, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(callback);
|
||||
};
|
||||
|
||||
fs.promises.access = util.promisify(fs.access);
|
||||
|
||||
const { accessSync } = fs;
|
||||
fs.accessSync = function (pathArgument, mode) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return accessSync.apply(this, arguments);
|
||||
|
||||
if (mode == null) mode = fs.constants.F_OK;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.accessSync(realPath, mode);
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (mode & fs.constants.W_OK) {
|
||||
throw createError(AsarError.NO_ACCESS, { asarPath, filePath });
|
||||
}
|
||||
};
|
||||
|
||||
const { readFile } = fs;
|
||||
fs.readFile = function (pathArgument, options, callback) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return readFile.apply(this, arguments);
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (options === null || options === undefined) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.size === 0) {
|
||||
nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.read(fd, buffer, 0, info.size, info.offset, error => {
|
||||
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
};
|
||||
|
||||
fs.promises.readFile = util.promisify(fs.readFile);
|
||||
|
||||
const { readFileSync } = fs;
|
||||
fs.readFileSync = function (pathArgument, options) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return readFileSync.apply(this, arguments);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
if (info.size === 0) return (options) ? '' : Buffer.alloc(0);
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFileSync(realPath, options);
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = { encoding: null };
|
||||
} else if (typeof options === 'string') {
|
||||
options = { encoding: options };
|
||||
} else if (typeof options !== 'object') {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
|
||||
const { encoding } = options;
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||
return (encoding) ? buffer.toString(encoding) : buffer;
|
||||
};
|
||||
|
||||
const { readdir } = fs;
|
||||
fs.readdir = function (pathArgument, options = {}, callback) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!isAsar) return readdir.apply(this, arguments);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
const error = createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
const files = archive.readdir(filePath);
|
||||
if (!files) {
|
||||
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.withFileTypes) {
|
||||
const dirents = [];
|
||||
for (const file of files) {
|
||||
const stats = archive.stat(file);
|
||||
if (stats.isFile) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
|
||||
} else if (stats.isDirectory) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
|
||||
} else if (stats.isLink) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
|
||||
}
|
||||
}
|
||||
nextTick(callback, [null, dirents]);
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(callback, [null, files]);
|
||||
};
|
||||
|
||||
fs.promises.readdir = util.promisify(fs.readdir);
|
||||
|
||||
const { readdirSync } = fs;
|
||||
fs.readdirSync = function (pathArgument, options = {}) {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return readdirSync.apply(this, arguments);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
}
|
||||
|
||||
const files = archive.readdir(filePath);
|
||||
if (!files) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (options.withFileTypes) {
|
||||
const dirents = [];
|
||||
for (const file of files) {
|
||||
const stats = archive.stat(file);
|
||||
if (stats.isFile) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
|
||||
} else if (stats.isDirectory) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
|
||||
} else if (stats.isLink) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
|
||||
}
|
||||
}
|
||||
return dirents;
|
||||
}
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
const { internalModuleReadJSON } = internalBinding('fs');
|
||||
internalBinding('fs').internalModuleReadJSON = pathArgument => {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return internalModuleReadJSON(pathArgument);
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) return;
|
||||
|
||||
const info = archive.getFileInfo(filePath);
|
||||
if (!info) return;
|
||||
if (info.size === 0) return '';
|
||||
if (info.unpacked) {
|
||||
const realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFileSync(realPath, { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(info.size);
|
||||
const fd = archive.getFd();
|
||||
if (!(fd >= 0)) return;
|
||||
|
||||
logASARAccess(asarPath, filePath, info.offset);
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||
return buffer.toString('utf8');
|
||||
};
|
||||
|
||||
const { internalModuleStat } = internalBinding('fs');
|
||||
internalBinding('fs').internalModuleStat = pathArgument => {
|
||||
const { isAsar, asarPath, filePath } = splitPath(pathArgument);
|
||||
if (!isAsar) return internalModuleStat(pathArgument);
|
||||
|
||||
// -ENOENT
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) return -34;
|
||||
|
||||
// -ENOENT
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) return -34;
|
||||
|
||||
return (stats.isDirectory) ? 1 : 0;
|
||||
};
|
||||
|
||||
// Calling mkdir for directory inside asar archive should throw ENOTDIR
|
||||
// error, but on Windows it throws ENOENT.
|
||||
if (process.platform === 'win32') {
|
||||
const { mkdir } = fs;
|
||||
fs.mkdir = (pathArgument, options, callback) => {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const { isAsar, filePath } = splitPath(pathArgument);
|
||||
if (isAsar && filePath.length > 0) {
|
||||
const error = createError(AsarError.NOT_DIR);
|
||||
nextTick(callback, [error]);
|
||||
return;
|
||||
}
|
||||
|
||||
mkdir(pathArgument, options, callback);
|
||||
};
|
||||
|
||||
fs.promises.mkdir = util.promisify(fs.mkdir);
|
||||
|
||||
const { mkdirSync } = fs;
|
||||
fs.mkdirSync = function (pathArgument, options) {
|
||||
const { isAsar, filePath } = splitPath(pathArgument);
|
||||
if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR);
|
||||
return mkdirSync(pathArgument, options);
|
||||
};
|
||||
}
|
||||
|
||||
function invokeWithNoAsar (func) {
|
||||
return function () {
|
||||
const processNoAsarOriginalValue = process.noAsar;
|
||||
process.noAsar = true;
|
||||
try {
|
||||
return func.apply(this, arguments);
|
||||
} finally {
|
||||
process.noAsar = processNoAsarOriginalValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Strictly implementing the flags of fs.copyFile is hard, just do a simple
|
||||
// implementation for now. Doing 2 copies won't spend much time more as OS
|
||||
// has filesystem caching.
|
||||
overrideAPI(fs, 'copyFile');
|
||||
overrideAPISync(fs, 'copyFileSync');
|
||||
|
||||
overrideAPI(fs, 'open');
|
||||
overrideAPISync(process, 'dlopen', 1);
|
||||
overrideAPISync(Module._extensions, '.node', 1);
|
||||
overrideAPISync(fs, 'openSync');
|
||||
|
||||
const overrideChildProcess = (childProcess) => {
|
||||
// Executing a command string containing a path to an asar archive
|
||||
// confuses `childProcess.execFile`, which is internally called by
|
||||
// `childProcess.{exec,execSync}`, causing Electron to consider the full
|
||||
// command as a single path to an archive.
|
||||
const { exec, execSync } = childProcess;
|
||||
childProcess.exec = invokeWithNoAsar(exec);
|
||||
childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]);
|
||||
childProcess.execSync = invokeWithNoAsar(execSync);
|
||||
|
||||
overrideAPI(childProcess, 'execFile');
|
||||
overrideAPISync(childProcess, 'execFileSync');
|
||||
};
|
||||
|
||||
// Lazily override the child_process APIs only when child_process is
|
||||
// fetched the first time. We will eagerly override the child_process APIs
|
||||
// when this env var is set so that stack traces generated inside node unit
|
||||
// tests will match. This env var will only slow things down in users apps
|
||||
// and should not be used.
|
||||
if (process.env.ELECTRON_EAGER_ASAR_HOOK_FOR_TESTING) {
|
||||
overrideChildProcess(require('child_process'));
|
||||
} else {
|
||||
const originalModuleLoad = Module._load;
|
||||
Module._load = (request, ...args) => {
|
||||
const loadResult = originalModuleLoad(request, ...args);
|
||||
if (request === 'child_process') {
|
||||
if (!v8Util.getHiddenValue(loadResult, 'asar-ready')) {
|
||||
v8Util.setHiddenValue(loadResult, 'asar-ready', true);
|
||||
// Just to make it obvious what we are dealing with here
|
||||
const childProcess = loadResult;
|
||||
|
||||
overrideChildProcess(childProcess);
|
||||
}
|
||||
}
|
||||
return loadResult;
|
||||
};
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -1,4 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Monkey-patch the fs module.
|
||||
require('electron/js2c/asar').wrapFsWithAsar(require('fs'));
|
||||
@@ -1,4 +1,4 @@
|
||||
const { nativeImage, NativeImage } = process._linkedBinding('electron_common_native_image');
|
||||
const { nativeImage } = process._linkedBinding('electron_common_native_image');
|
||||
|
||||
export function isPromise (val: any) {
|
||||
return (
|
||||
@@ -80,7 +80,7 @@ function deserializeNativeImage (value: any) {
|
||||
}
|
||||
|
||||
export function serialize (value: any): any {
|
||||
if (value instanceof NativeImage) {
|
||||
if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
||||
return serializeNativeImage(value);
|
||||
} if (Array.isArray(value)) {
|
||||
return value.map(serialize);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user