Compare commits

...

37 Commits

Author SHA1 Message Date
trop[bot]
f1500363dc chore: fix dangling promise in npm install (#36399)
* Fix dangling promise introduced in #33979

Co-authored-by: hyrious <hyrious@outlook.com>

* fix reject in callback

Co-authored-by: hyrious <hyrious@outlook.com>

* simplify code

Co-authored-by: Black-Hole <158blackhole@gmail.com>

Co-authored-by: hyrious <hyrious@outlook.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: hyrious <hyrious@outlook.com>
2022-11-21 14:54:36 +01:00
Michaela Laurencin
4435461885 docs: add EOL dates to timeline doc (#36389) 2022-11-17 18:11:39 -05:00
trop[bot]
dcdfe231fc chore: compile addons with --gnu++17 (#36383)
* chore: compile addons with --gnu++17

Co-authored-by: deepak1556 <hop2deep@gmail.com>

* chore: update patch

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-11-17 22:23:50 +01:00
trop[bot]
bc2c39e668 build: remove out cache (#36359)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2022-11-15 20:03:58 -08:00
trop[bot]
95d1cf0670 build: make src cache smaller (#36351)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2022-11-15 13:15:23 -05:00
trop[bot]
423cdb02a7 docs: update README.md to use correct twitter handle (#36347)
doc: update README.md to use correct twitter handle

Co-authored-by: Tierney Cyren <bnb@electronjs.org>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Tierney Cyren <bnb@electronjs.org>
2022-11-15 00:30:38 -08:00
Samuel Attard
cc8d02db5d build: provide moduleVersion to docs-parser directly (#36336)
build: provide moduleVersion to docs-parser directly (#36329)
2022-11-14 16:28:11 -05:00
Samuel Attard
3afea231ca build: fix filenames.auto (#36338) 2022-11-14 10:03:18 -08:00
trop[bot]
49b624ef1f docs: update tutorials for Forge 6 (#36321)
docs: update tutorials for Forge 6 (#36313)

docs: update tutorial for Forge 6

Co-authored-by: Erick Zhao <erick@hotmail.ca>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2022-11-13 23:45:46 -08:00
trop[bot]
d8a1298eaf feat: add app.getPreferredSystemLanguages() API (#36290)
feat: add app.getPreferredSystemLanguages() API (#36035)

* feat: add app.getSystemLanguage() API

* Change the API to getPreferredSystemLanguages

* Fix test

* Clarify docs and add Linux impl

* Remove USE_GLIB

* Don't add C to list

* Remove examples since there's a lot of edge cases

* Fix lint

* Add examples

* Fix compile error

* Apply PR feedback

* Update the example

Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com>
2022-11-09 17:21:12 -05:00
electron-roller[bot]
3026615784 chore: bump chromium to 106.0.5249.181 (21-x-y) (#36288)
* chore: bump chromium in DEPS to 106.0.5249.181

* chore: update patches

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-11-09 17:05:58 -05:00
trop[bot]
23a81e6e59 fix: app.dock.setIcon() crash before app ready (#36294)
fix: dock.setIcon() crash before app ready

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-11-09 14:59:45 -05:00
Pedro Pontes
801cbe2ab3 revert: cherry-pick eef098d1c7d5 from webrtc (#36264)
Revert "chore: cherry-pick eef098d1c7d5 from webrtc (#36216)"

This reverts commit 2212933858.
2022-11-09 10:51:33 -05:00
trop[bot]
e857073d65 docs: fix driverId -> deviceId typo (#36270)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2022-11-08 11:30:58 +01:00
Pedro Pontes
86a1ee1e89 chore: cherry-pick 67c9cbc784d6 from chromium (#36219)
Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com>
2022-11-02 13:13:40 -04:00
Pedro Pontes
2212933858 chore: cherry-pick eef098d1c7d5 from webrtc (#36216)
* chore: cherry-pick eef098d1c7d5 from webrtc

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-11-02 11:48:42 -04:00
Pedro Pontes
c5346bde08 chore: cherry-pick 933cc81c6bad from chromium (#36222)
* chore: cherry-pick 933cc81c6bad from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-11-02 09:18:45 -04:00
trop[bot]
c678f7b872 docs: Fixed outdated documentation (uploading to App Store) (#36212)
* Updated docs on uploading

- replaced mention of Application Loader with Apple Transporter, its replacement
- replaced mention of iTunes Connect with App Store Connect
- updated link for creating a record

Co-authored-by: Georgescu Gabriel <29951139+gabi200@users.noreply.github.com>

* Update mac-app-store-submission-guide.md

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Georgescu Gabriel <29951139+gabi200@users.noreply.github.com>
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2022-11-01 16:39:27 -07:00
electron-roller[bot]
3aac61e60c chore: bump chromium to 106.0.5249.168 (21-x-y) (#36185)
chore: bump chromium in DEPS to 106.0.5249.168

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2022-11-01 11:10:23 -04:00
Milan Burda
e91dba1730 build: fix building with enable_plugins = false (#36202)
build: fix building with enable_plugins = false (#36193)

Co-authored-by: Milan Burda <miburda@microsoft.com>

Co-authored-by: Milan Burda <miburda@microsoft.com>
2022-11-01 11:09:55 -04:00
Keeley Hammond
019c05b924 build: determine electron version from tags not files (#36135)
* build: determine electron version from tags not files (#36106)

* build: determine electron version from tags not files

* build: make electron_version dependent on packed-refs and git HEAD

* build: do not delete electron/.git

* build: do not revert a commit we didn't make

* build: gen version file instead of just writing it

* build: update cache and ninja targets

* build: copy resource.h to generated electron.rc

* build: electron_win32_resources should be public deps

* build: also copy the icon

Co-authored-by: MarshallOfSound <marshallofsound@electronjs.org>

* chore: fixup patches

* chore: update patches

* build: ensure get-version runs in the electron git checkout (#36128)

* build: strip v in the getElectronVersion helper

* build: use npm@7 for npm view command

Co-authored-by: MarshallOfSound <marshallofsound@electronjs.org>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sam@electronjs.org>
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-10-28 13:13:03 -07:00
trop[bot]
0fe2a73f83 build: prefix version in uploader script (#36173)
build: prefix v in uploader script

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2022-10-27 17:47:26 -07:00
trop[bot]
69cb9c1581 test: fixup HID test for ARM CI (#36172)
(cherry picked from commit caf28066d6)

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-10-27 16:47:54 -04:00
trop[bot]
b176c95226 fix: resolve loadURL properly for in-page navigations (#36150)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-10-27 16:19:44 -04:00
trop[bot]
fa9ffa77b8 refactor: handle uncaught promise error (#36158)
Co-authored-by: daief <1437931235@qq.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: daief <1437931235@qq.com>
2022-10-27 14:25:43 -04:00
electron-roller[bot]
9870a31225 chore: bump chromium to 106.0.5249.165 (21-x-y) (#36162)
* chore: bump chromium in DEPS to 106.0.5249.165

* chore: update patches

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-10-27 14:20:54 -04:00
trop[bot]
0ee70e9431 docs: correcting main example in WebRequest documentation (#36160)
docs: correcting main example in WebRequest documentation (#36113)

Correcting main example

The entry `*://electron.github.io` is invalid and produces an exception. i.e.

> UnhandledPromiseRejectionWarning: TypeError: Invalid url pattern *://electron.github.io: Empty path.

Setting a valid path to resolve this issue

Co-authored-by: Shane Gannon <sgannon200@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shane Gannon <sgannon200@gmail.com>
2022-10-27 14:17:15 -04:00
trop[bot]
47bc841d6c test: fix flake in will-navigate test (#36166)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-10-27 14:07:07 -04:00
trop[bot]
a75e369e9c docs: document that when invoke rejects, it gives a different Error (#36147)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-10-26 17:28:19 -07:00
electron-roller[bot]
bec207c931 chore: bump chromium to 106.0.5249.120 (21-x-y) (#36145)
chore: bump chromium in DEPS to 106.0.5249.120

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2022-10-26 15:24:02 -04:00
trop[bot]
28b9856094 build: use python3 for appveyor commands (#36132)
Co-authored-by: Samuel Attard <sattard@salesforce.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-10-25 20:28:32 -07:00
trop[bot]
ed999f5456 docs: fixed MenuItem documentation to use the same formatting for all role options (#36119)
docs: fixed MenuItem documentation to use the same formatting for all role options (#36112)

Fixed MenuItem documentation

Some of the items in the list of possible roles were formatted differently, which made it harder to read.
Sorry if this was intended, but I found it easier to read with them all formatted the same.

Co-authored-by: flynow10 <37031514+flynow10@users.noreply.github.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: flynow10 <37031514+flynow10@users.noreply.github.com>
2022-10-25 20:45:26 +09:00
trop[bot]
67363441a5 test: exit properly when node-spec-runner validates specs (#36103)
test: exit properly when node spec runner validates specs

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-10-24 16:29:01 +02:00
George Xu
47dd47d8c4 refactor: migrate from asar to @electron/asar (#36070) (#36087)
* refactor: migrate from asar to @electron/asar (#36070)

* refactor: migrate from asar to @electron/asar

* fix: update asar require calls

* kick ci

Co-authored-by: Samuel Attard <sam@electronjs.org>
2022-10-21 11:14:36 +02:00
trop[bot]
0a34be3e55 test: disable flaky linux arm/arm64 crash test case (#36098)
test: disable failing linux arm/arm64 crash test case

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-10-21 11:13:32 +02:00
trop[bot]
72eea879e7 fix: printToPDF default margins (#36072)
fix: printToPDF default margins (#36060)

printToPDF default margins

The document printToPDF has a 1cm margin and this default value has been added in the code.

Co-authored-by: Yusoo <Yusoo@users.noreply.github.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Yusoo <Yusoo@users.noreply.github.com>
2022-10-20 16:34:16 -04:00
trop[bot]
bcfafc4386 fix: headless job tracking in printToPDF (#36064)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-10-20 14:28:21 -04:00
61 changed files with 1543 additions and 552 deletions

View File

@@ -454,7 +454,7 @@ step-delete-git-directories: &step-delete-git-directories
command: |
if [ "`uname`" == "Darwin" ]; then
cd src
( find . -type d -name ".git" -not -path "./third_party/angle/*" -not -path "./third_party/dawn/*" ) | xargs rm -rf
( find . -type d -name ".git" -not -path "./third_party/angle/*" -not -path "./third_party/dawn/*" -not -path "./electron/*" ) | xargs rm -rf
fi
# On macOS the yarn install command during gclient sync was run on a linux
@@ -813,7 +813,7 @@ step-maybe-zip-symbols: &step-maybe-zip-symbols
cd src
export BUILD_PATH="$PWD/out/Default"
ninja -C out/Default electron:licenses
ninja -C out/Default electron:electron_version
ninja -C out/Default electron:electron_version_file
DELETE_DSYMS_AFTER_ZIP=1 electron/script/zip-symbols.py -b $BUILD_PATH
step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
@@ -873,12 +873,12 @@ step-touch-sync-done: &step-touch-sync-done
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
restore_cache:
keys:
- v14-src-cache-{{ checksum "src/electron/.depshash" }}
- v16-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:
- v14-src-cache-marker-{{ checksum "src/electron/.depshash" }}
- v16-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
@@ -893,14 +893,6 @@ step-maybe-restore-git-cache: &step-maybe-restore-git-cache
- v1-git-cache-{{ checksum "src/electron/.circle-sync-done" }}
name: Conditionally restoring git cache
step-restore-out-cache: &step-restore-out-cache
restore_cache:
paths:
- ./src/out/Default
keys:
- v10-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
name: Restoring out cache
step-set-git-cache-path: &step-set-git-cache-path
run:
name: Set GIT_CACHE_PATH to make gclient to use the cache
@@ -918,13 +910,6 @@ step-save-git-cache: &step-save-git-cache
key: v1-git-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
name: Persisting git cache
step-save-out-cache: &step-save-out-cache
save_cache:
paths:
- ./src/out/Default
key: v10-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
name: Persisting out cache
step-run-electron-only-hooks: &step-run-electron-only-hooks
run:
name: Run Electron Only Hooks
@@ -954,13 +939,16 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c
rm -rf third_party/electron_node/deps/openssl
rm -rf third_party/electron_node/deps/v8
rm -rf chrome/test/data/xr/webvr_info
rm -rf src/third_party/angle/third_party/VK-GL-CTS/src
rm -rf src/third_party/swift-toolchain
rm -rf src/third_party/swiftshader/tests/regres/testlists
# Save the src cache based on the deps hash
step-save-src-cache: &step-save-src-cache
save_cache:
paths:
- /var/portal
key: v14-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
key: v16-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
name: Persisting src cache
step-make-src-cache-marker: &step-make-src-cache-marker
run:
@@ -970,7 +958,7 @@ step-save-src-cache-marker: &step-save-src-cache-marker
save_cache:
paths:
- .src-cache-marker
key: v14-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
key: v16-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
step-maybe-early-exit-no-doc-change: &step-maybe-early-exit-no-doc-change
run:
@@ -1302,9 +1290,6 @@ commands:
build:
type: boolean
default: true
use-out-cache:
type: boolean
default: true
restore-src-cache:
type: boolean
default: true
@@ -1427,10 +1412,6 @@ commands:
- *step-delete-git-directories
# Electron app
- when:
condition: << parameters.use-out-cache >>
steps:
- *step-restore-out-cache
- *step-gn-gen-default
- *step-electron-build
- *step-maybe-electron-dist-strip
@@ -1473,22 +1454,6 @@ commands:
condition: << parameters.build >>
steps:
- move_and_store_all_artifacts
- run:
name: Remove the big things on macOS, this seems to be better on average
command: |
if [ "`uname`" == "Darwin" ]; then
mkdir -p src/out/Default
cd src/out/Default
find . -type f -size +50M -delete
mkdir -p gen/electron
cd gen/electron
# These files do not seem to like being in a cache, let us remove them
find . -type f -name '*_pkg_info' -delete
fi
- when:
condition: << parameters.use-out-cache >>
steps:
- *step-save-out-cache
- *step-maybe-notify-slack-failure
@@ -1642,7 +1607,6 @@ jobs:
persist: true
checkout: false
checkout-and-assume-cache: true
use-out-cache: false
linux-x64-testing-asan:
executor:
@@ -1659,7 +1623,6 @@ jobs:
- electron-build:
persist: true
checkout: true
use-out-cache: false
build-nonproprietary-ffmpeg: false
linux-x64-testing-no-run-as-node:
@@ -1676,7 +1639,6 @@ jobs:
- electron-build:
persist: false
checkout: true
use-out-cache: false
linux-x64-testing-gn-check:
executor:
@@ -1727,7 +1689,6 @@ jobs:
persist: true
checkout: false
checkout-and-assume-cache: true
use-out-cache: false
linux-arm-publish:
executor:
@@ -1770,7 +1731,6 @@ jobs:
persist: true
checkout: false
checkout-and-assume-cache: true
use-out-cache: false
linux-arm64-testing-gn-check:
executor:

View File

@@ -107,6 +107,14 @@ branding = read_file("shell/app/BRANDING.json", "json")
electron_project_name = branding.project_name
electron_product_name = branding.product_name
electron_mac_bundle_id = branding.mac_bundle_id
electron_version = exec_script("script/print-version.py",
[],
"trim string",
[
".git/packed-refs",
".git/HEAD",
"script/lib/get-version.js",
])
if (is_mas_build) {
assert(is_mac,
@@ -302,12 +310,9 @@ npm_action("electron_version_args") {
outputs = [ "$target_gen_dir/electron_version.args" ]
args = rebase_path(outputs)
args = rebase_path(outputs) + [ "$electron_version" ]
inputs = [
"ELECTRON_VERSION",
"script/generate-version-json.js",
]
inputs = [ "script/generate-version-json.js" ]
}
templated_file("electron_version_header") {
@@ -319,6 +324,39 @@ templated_file("electron_version_header") {
args_files = get_target_outputs(":electron_version_args")
}
templated_file("electron_win_rc") {
deps = [ ":electron_version_args" ]
template = "build/templates/electron_rc.tmpl"
output = "$target_gen_dir/win-resources/electron.rc"
args_files = get_target_outputs(":electron_version_args")
}
copy("electron_win_resource_files") {
sources = [
"shell/browser/resources/win/electron.ico",
"shell/browser/resources/win/resource.h",
]
outputs = [ "$target_gen_dir/win-resources/{{source_file_part}}" ]
}
templated_file("electron_version_file") {
deps = [ ":electron_version_args" ]
template = "build/templates/version_string.tmpl"
output = "$root_build_dir/version"
args_files = get_target_outputs(":electron_version_args")
}
group("electron_win32_resources") {
public_deps = [
":electron_win_rc",
":electron_win_resource_files",
]
}
action("electron_fuses") {
script = "build/fuses/build.py"
@@ -617,6 +655,8 @@ source_set("electron_lib") {
if (enable_plugins) {
deps += [ "chromium_src:plugins" ]
sources += [
"shell/renderer/electron_renderer_pepper_host_factory.cc",
"shell/renderer/electron_renderer_pepper_host_factory.h",
"shell/renderer/pepper_helper.cc",
"shell/renderer/pepper_helper.h",
]
@@ -755,7 +795,6 @@ if (is_mac) {
electron_helper_name = "$electron_product_name Helper"
electron_login_helper_name = "$electron_product_name Login Helper"
electron_framework_version = "A"
electron_version = read_file("ELECTRON_VERSION", "trim string")
mac_xib_bundle_data("electron_xibs") {
sources = [ "shell/common/resources/mac/MainMenu.xib" ]
@@ -1196,6 +1235,7 @@ if (is_mac) {
":default_app_asar",
":electron_app_manifest",
":electron_lib",
":electron_win32_resources",
":packed_resources",
"//components/crash/core/app",
"//content:sandbox_helper_win",
@@ -1229,8 +1269,7 @@ if (is_mac) {
if (is_win) {
sources += [
# TODO: we should be generating our .rc files more like how chrome does
"shell/browser/resources/win/electron.rc",
"$target_gen_dir/win-resources/electron.rc",
"shell/browser/resources/win/resource.h",
]
@@ -1412,15 +1451,10 @@ group("licenses") {
]
}
copy("electron_version") {
sources = [ "ELECTRON_VERSION" ]
outputs = [ "$root_build_dir/version" ]
}
dist_zip("electron_dist_zip") {
data_deps = [
":electron_app",
":electron_version",
":electron_version_file",
":licenses",
]
if (is_linux) {

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'106.0.5249.119',
'106.0.5249.181',
'node_version':
'v16.16.0',
'nan_version':

View File

@@ -1 +0,0 @@
21.2.0

View File

@@ -12,7 +12,7 @@ using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) an
[Chromium](https://www.chromium.org) and is used by the [Atom
editor](https://github.com/atom/atom) and many other [apps](https://electronjs.org/apps).
Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important
Follow [@electronjs](https://twitter.com/electronjs) on Twitter for important
announcements.
This project adheres to the Contributor Covenant

View File

@@ -199,14 +199,14 @@ for:
}
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
python electron\script\zip-symbols.py
python3 electron\script\zip-symbols.py
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
} else {
# It's useful to have pdb files when debugging testing builds that are
# built on CI.
7z a pdb.zip out\Default\*.pdb
}
- python electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
- python3 electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
deploy_script:
- cd electron
@@ -214,10 +214,10 @@ for:
if (Test-Path Env:\ELECTRON_RELEASE) {
if (Test-Path Env:\UPLOAD_TO_STORAGE) {
Write-Output "Uploading Electron release distribution to azure"
& python script\release\uploaders\upload.py --verbose --upload_to_storage
& python3 script\release\uploaders\upload.py --verbose --upload_to_storage
} else {
Write-Output "Uploading Electron release distribution to github releases"
& python script\release\uploaders\upload.py --verbose
& python3 script\release\uploaders\upload.py --verbose
}
} elseif (Test-Path Env:\TEST_WOA) {
node script/release/ci-release-build.js --job=electron-woa-testing --ci=GHA --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
@@ -303,11 +303,11 @@ for:
- echo Running main test suite & node script/yarn test -- --trace-uncaught --runners=main --enable-logging=file --log-file=%cd%\electron.log
- echo Running native test suite & node script/yarn test -- --trace-uncaught --runners=native --enable-logging=file --log-file=%cd%\electron.log
- cd ..
- echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
- echo Verifying non proprietary ffmpeg & python3 electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
- echo "About to verify mksnapshot"
- echo Verifying mksnapshot & python electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd%
- echo Verifying mksnapshot & python3 electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd%
- echo "Done verifying mksnapshot"
- echo Verifying chromedriver & python electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd%
- echo Verifying chromedriver & python3 electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd%
- echo "Done verifying chromedriver"
on_finish:

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 21,2,0,0
PRODUCTVERSION 21,2,0,0
FILEVERSION $major,$minor,$patch,$prerelease_number
PRODUCTVERSION $major,$minor,$patch,$prerelease_number
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron"
VALUE "FileVersion", "21.2.0"
VALUE "FileVersion", "$major.$minor.$patch"
VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron"
VALUE "ProductVersion", "21.2.0"
VALUE "ProductVersion", "$major.$minor.$patch"
VALUE "SquirrelAwareVersion", "1"
END
END

View File

@@ -0,0 +1 @@
$full_version

View File

@@ -717,6 +717,8 @@ To set the locale, you'll want to use a command line switch at app startup, whic
**Note:** This API must be called after the `ready` event is emitted.
**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages).
### `app.getLocaleCountryCode()`
Returns `string` - User operating system's locale two-letter [ISO 3166](https://www.iso.org/iso-3166-country-codes.html) country code. The value is taken from native OS APIs.
@@ -725,10 +727,42 @@ Returns `string` - User operating system's locale two-letter [ISO 3166](https://
### `app.getSystemLocale()`
Returns `string` - The current system locale. On Windows and Linux, it is fetched using Chromium's `i18n` library. On macOS, the `NSLocale` object is used instead.
Returns `string` - The current system locale. On Windows and Linux, it is fetched using Chromium's `i18n` library. On macOS, `[NSLocale currentLocale]` is used instead. To get the user's current system language, which is not always the same as the locale, it is better to use [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages).
Different operating systems also use the regional data differently:
* Windows 11 uses the regional format for numbers, dates, and times.
* macOS Monterey uses the region for formatting numbers, dates, times, and for selecting the currency symbol to use.
Therefore, this API can be used for purposes such as choosing a format for rendering dates and times in a calendar app, especially when the developer wants the format to be consistent with the OS.
**Note:** This API must be called after the `ready` event is emitted.
**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages).
### `app.getPreferredSystemLanguages()`
Returns `string[]` - The user's preferred system languages from most preferred to least preferred, including the country codes if applicable. A user can modify and add to this list on Windows or macOS through the Language and Region settings.
The API uses `GlobalizationPreferences` (with a fallback to `GetSystemPreferredUILanguages`) on Windows, `\[NSLocale preferredLanguages\]` on macOS, and `g_get_language_names` on Linux.
This API can be used for purposes such as deciding what language to present the application in.
Here are some examples of return values of the various language and locale APIs with different configurations:
* For Windows, where the application locale is German, the regional format is Finnish (Finland), and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese (China), Finnish, and Spanish (Latin America):
* `app.getLocale()` returns `'de'`
* `app.getSystemLocale()` returns `'fi-FI'`
* `app.getPreferredSystemLanguages()` returns `['fr-CA', 'en-US', 'zh-Hans-CN', 'fi', 'es-419']`
* On macOS, where the application locale is German, the region is Finland, and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese, and Spanish (Latin America):
* `app.getLocale()` returns `'de'`
* `app.getSystemLocale()` returns `'fr-FI'`
* `app.getPreferredSystemLanguages()` returns `['fr-CA', 'en-US', 'zh-Hans-FI', 'es-419']`
Both the available languages and regions and the possible return values differ between the two operating systems.
As can be seen with the example above, on Windows, it is possible that a preferred system language has no country code, and that one of the preferred system languages corresponds with the language used for the regional format. On macOS, the region serves more as a default country code: the user doesn't need to have Finnish as a preferred language to use Finland as the region,and the country code `FI` is used as the country code for preferred system languages that do not have associated countries in the language name.
### `app.addRecentDocument(path)` _macOS_ _Windows_
* `path` string
@@ -1203,7 +1237,7 @@ For `infoType` equal to `basic`:
}
```
Using `basic` should be preferred if only basic information like `vendorId` or `driverId` is needed.
Using `basic` should be preferred if only basic information like `vendorId` or `deviceId` is needed.
### `app.setBadgeCount([count])` _Linux_ _macOS_

View File

@@ -96,14 +96,6 @@ Algorithm][SCA], just like [`window.postMessage`][], so prototype chains will no
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
throw an exception.
> **NOTE:** Sending non-standard JavaScript types such as DOM objects or
> special Electron objects will throw an exception.
>
> Since the main process does not have support for DOM objects such as
> `ImageBitmap`, `File`, `DOMMatrix` and so on, such objects cannot be sent over
> Electron's IPC to the main process, as the main process would have no way to decode
> them. Attempting to send such objects over IPC will result in an error.
The main process should listen for `channel` with
[`ipcMain.handle()`](./ipc-main.md#ipcmainhandlechannel-listener).
@@ -126,6 +118,21 @@ If you need to transfer a [`MessagePort`][] to the main process, use [`ipcRender
If you do not need a response to the message, consider using [`ipcRenderer.send`](#ipcrenderersendchannel-args).
> **Note**
> Sending non-standard JavaScript types such as DOM objects or
> special Electron objects will throw an exception.
>
> Since the main process does not have support for DOM objects such as
> `ImageBitmap`, `File`, `DOMMatrix` and so on, such objects cannot be sent over
> Electron's IPC to the main process, as the main process would have no way to decode
> them. Attempting to send such objects over IPC will result in an error.
> **Note**
> If the handler in the main process throws an error,
> the promise returned by `invoke` will reject.
> However, the `Error` object in the renderer process
> will not be the same as the one thrown in the main process.
### `ipcRenderer.sendSync(channel, ...args)`
* `channel` string

View File

@@ -14,7 +14,7 @@ See [`Menu`](menu.md) for examples.
* `menuItem` MenuItem
* `browserWindow` [BrowserWindow](browser-window.md) | undefined - This will not be defined if no window is open.
* `event` [KeyboardEvent](structures/keyboard-event.md)
* `role` string (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `toggleSpellChecker`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, 'showSubstitutions', 'toggleSmartQuotes', 'toggleSmartDashes', 'toggleTextReplacement', `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `shareMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
* `role` string (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `toggleSpellChecker`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, `showSubstitutions`, `toggleSmartQuotes`, `toggleSmartDashes`, `toggleTextReplacement`, `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `shareMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
`click` property will be ignored. See [roles](#roles).
* `type` string (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or
`radio`.

View File

@@ -28,7 +28,7 @@ const { session } = require('electron')
// Modify the user agent for all requests to the following urls.
const filter = {
urls: ['https://*.github.com/*', '*://electron.github.io']
urls: ['https://*.github.com/*', '*://electron.github.io/*']
}
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {

View File

@@ -52,15 +52,17 @@ ways to get your application signed and notarized.
If you're using Electron's favorite build tool, getting your application signed
and notarized requires a few additions to your configuration. [Forge](https://electronforge.io) is a
collection of the official Electron tools, using [`electron-packager`],
[`electron-osx-sign`], and [`electron-notarize`] under the hood.
[`@electron/osx-sign`], and [`@electron/notarize`] under the hood.
Detailed instructions on how to configure your application can be found in the [Electron Forge Code Signing Tutorial](https://www.electronforge.io/guides/code-signing/code-signing-macos).
Detailed instructions on how to configure your application can be found in the
[Signing macOS Apps](https://www.electronforge.io/guides/code-signing/code-signing-macos) guide in
the Electron Forge docs.
### Using Electron Packager
If you're not using an integrated build pipeline like Forge, you
are likely using [`electron-packager`], which includes [`electron-osx-sign`] and
[`electron-notarize`].
are likely using [`electron-packager`], which includes [`@electron/osx-sign`] and
[`@electron/notarize`].
If you're using Packager's API, you can pass [in configuration that both signs
and notarizes your application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
@@ -70,13 +72,7 @@ const packager = require('electron-packager')
packager({
dir: '/path/to/my/app',
osxSign: {
identity: 'Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)',
'hardened-runtime': true,
entitlements: 'entitlements.plist',
'entitlements-inherit': 'entitlements.plist',
'signature-flags': 'library'
},
osxSign: {},
osxNotarize: {
appleId: 'felix@felix.fun',
appleIdPassword: 'my-apple-id-password'
@@ -84,26 +80,6 @@ packager({
})
```
The `entitlements.plist` file referenced here needs the following macOS-specific entitlements
to assure the Apple security mechanisms that your app is doing these things
without meaning any harm:
```xml title="entitlements.plist"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
</dict>
</plist>
```
Up until Electron 12, the `com.apple.security.cs.allow-unsigned-executable-memory` entitlement was required
as well. However, it should not be used anymore if it can be avoided.
### Signing Mac App Store applications
See the [Mac App Store Guide].
@@ -213,15 +189,14 @@ can find [its documentation here](https://www.electron.build/code-signing).
See the [Windows Store Guide].
[apple developer program]: https://developer.apple.com/programs/
[`electron-builder`]: https://github.com/electron-userland/electron-builder
[`electron-forge`]: https://github.com/electron-userland/electron-forge
[`electron-osx-sign`]: https://github.com/electron-userland/electron-osx-sign
[`electron-forge`]: https://github.com/electron/forge
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
[`electron-packager`]: https://github.com/electron/electron-packager
[`electron-notarize`]: https://github.com/electron/electron-notarize
[`@electron/notarize`]: https://github.com/electron/notarize
[`electron-winstaller`]: https://github.com/electron/windows-installer
[`electron-wix-msi`]: https://github.com/felixrieseberg/electron-wix-msi
[`electron-wix-msi`]: https://github.com/electron-userland/electron-wix-msi
[xcode]: https://developer.apple.com/xcode
[signing certificates]: https://github.com/electron/electron-osx-sign/wiki/1.-Getting-Started#certificates
[signing certificates]: https://developer.apple.com/support/certificates/
[mac app store guide]: ./mac-app-store-submission-guide.md
[windows store guide]: ./windows-store-guide.md
[maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows

View File

@@ -7,28 +7,29 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
## Timeline
| Electron | Alpha | Beta | Stable | Chrome | Node | Supported |
| ------- | ----- | ------- | ------ | ------ | ---- | ---- |
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | M61 | v8.9 | 🚫 |
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | M66 | v10.2 | 🚫 |
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | M69 | v10.11 | 🚫 |
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-24 | M73 | v12.0 | 🚫 |
| 6.0.0 | -- | 2019-May-01 | 2019-Jul-30 | M76 | v12.4 | 🚫 |
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | M78 | v12.8 | 🚫 |
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | M80 | v12.13 | 🚫 |
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | M83 | v12.14 | 🚫 |
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | M85 | v12.16 | 🚫 |
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | M87 | v12.18 | 🚫 |
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | M89 | v14.16 | 🚫 |
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | M91 | v14.16 | 🚫 |
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | v14.17 | 🚫 |
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | v16.5 | 🚫 |
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | M96 | v16.9 | 🚫 |
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | M98 | v16.13 | 🚫 |
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | M100 | v16.13 | |
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | M102 | v16.14 | |
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | M104 | v16.15 | |
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | M106 | TBD | |
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | TBD | M108 | TBD | |
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | TBD | M106 | v16.17 | |
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | TBD | M104 | v16.15 | |
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | TBD | M102 | v16.14 | |
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | 2022-Sep-27 | M100 | v16.13 | 🚫 |
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | 2022-Aug-02 | M98 | v16.13 | 🚫 |
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | 2022-May-24 | M96 | v16.9 | 🚫 |
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | 2022-May-24 | M94 | v16.5 | 🚫 |
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | 2022-Mar-29 | M93 | v14.17 | 🚫 |
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | 2022-Feb-01 | M91 | v14.16 | 🚫 |
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | 2021-Nov-16 | M89 | v14.16 | 🚫 |
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | 2021-Aug-31 | M87 | v12.18 | 🚫 |
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | 2021-May-25 | M85 | v12.16 | 🚫 |
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | 2021-Mar-02 | M83 | v12.14 | 🚫 |
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | 2020-Nov-17 | M80 | v12.13 | 🚫 |
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | 2020-Aug-25 | M78 | v12.8 | 🚫 |
| 6.0.0 | -- | 2019-Apr-25 | 2019-Jul-30 | 2020-May-19 | M76 | v12.14.0 | 🚫 |
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-23 | 2020-Feb-04 | M73 | v12.0 | 🚫 |
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | 2019-Oct-22 | M69 | v10.11 | 🚫 |
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | 2019-Jul-30 | M66 | v10.2 | 🚫 |
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | 2019-Apr-23 | M61 | v8.9 | 🚫 |
**Notes:**

View File

@@ -11,7 +11,7 @@ This guide provides information on:
To sign Electron apps, the following tools must be installed first:
* Xcode 11 or above.
* The [electron-osx-sign][electron-osx-sign] npm module.
* The [@electron/osx-sign] npm module.
You also have to register an Apple Developer account and join the
[Apple Developer Program][developer-program].
@@ -103,7 +103,7 @@ Apps submitted to the Mac App Store must run under Apple's
the App Sandbox. The standard darwin build of Electron will fail to launch
when run under App Sandbox.
When signing the app with `electron-osx-sign`, it will automatically add the
When signing the app with `@electron/osx-sign`, it will automatically add the
necessary entitlements to your app's entitlements, but if you are using custom
entitlements, you must ensure App Sandbox capacity is added:
@@ -120,7 +120,7 @@ entitlements, you must ensure App Sandbox capacity is added:
#### Extra steps without `electron-osx-sign`
If you are signing your app without using `electron-osx-sign`, you must ensure
If you are signing your app without using `@electron/osx-sign`, you must ensure
the app bundle's entitlements have at least following keys:
```xml
@@ -170,22 +170,22 @@ your Apple Developer account's Team ID as its value:
</plist>
```
When using `electron-osx-sign` the `ElectronTeamID` key will be added
When using `@electron/osx-sign` the `ElectronTeamID` key will be added
automatically by extracting the Team ID from the certificate's name. You may
need to manually add this key if `electron-osx-sign` could not find the correct
need to manually add this key if `@electron/osx-sign` could not find the correct
Team ID.
### Sign apps for development
To sign an app that can run on your development machine, you must sign it with
the "Apple Development" certificate and pass the provisioning profile to
`electron-osx-sign`.
`@electron/osx-sign`.
```bash
electron-osx-sign YourApp.app --identity='Apple Development' --provisioning-profile=/path/to/yourapp.provisionprofile
```
If you are signing without `electron-osx-sign`, you must place the provisioning
If you are signing without `@electron/osx-sign`, you must place the provisioning
profile to `YourApp.app/Contents/embedded.provisionprofile`.
The signed app can only run on the machines that registered by the provisioning
@@ -213,7 +213,7 @@ use App Sandbox.
electron-osx-sign YourApp.app --identity='Developer ID Application' --no-gatekeeper-assess
```
By passing `--no-gatekeeper-assess`, the `electron-osx-sign` will skip the macOS
By passing `--no-gatekeeper-assess`, `@electron/osx-sign` will skip the macOS
GateKeeper check as your app usually has not been notarized yet by this step.
<!-- TODO(zcbenz): Add a chapter about App Notarization -->
@@ -232,7 +232,7 @@ how to meet the Mac App Store requirements.
### Upload
The Application Loader should be used to upload the signed app to iTunes
[Apple Transporter][apple-transporter] should be used to upload the signed app to App Store
Connect for processing, making sure you have [created a record][create-record]
before uploading.
@@ -341,11 +341,12 @@ Electron uses following cryptographic algorithms:
* RIPEMD - [ISO/IEC 10118-3](https://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2FIEC%2010118-3:2004)
[developer-program]: https://developer.apple.com/support/compare-memberships/
[electron-osx-sign]: https://github.com/electron/electron-osx-sign
[@electron/osx-sign]: https://github.com/electron/electron-osx-sign
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
[app-notarization]: https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
[submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html
[create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html
[create-record]: https://help.apple.com/app-store-connect/#/dev2cd126805
[apple-transporter]: https://help.apple.com/itc/transporteruserguide/en.lproj/static.html
[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
[export-compliance]: https://help.apple.com/app-store-connect/#/devc3f64248f
[user-selected]: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW6

View File

@@ -26,6 +26,15 @@ work on Windows, macOS, and Linux with a single JavaScript codebase.
This tutorial will guide you through the process of developing a desktop
application with Electron and distributing it to end users.
## Goals
This tutorial starts by guiding you through the process of piecing together
a minimal Electron application from scratch, then teaches you how to
package and distribute it to users using Electron Forge.
If you prefer to get a project started with a single-command boilerplate, we recommend you start
with Electron Forge's [`create-electron-app`](https://www.electronforge.io/) command.
## Assumptions
Electron is a native wrapper layer for web apps and is run in a Node.js environment.

View File

@@ -70,10 +70,9 @@ the [Electron Forge CLI documentation].
:::
You should also notice that your package.json now has a few more packages installed
under your `devDependencies`, and contains an added `config.forge` field with an array
of makers configured. **Makers** are Forge plugins that create distributables from
your source code. You should see multiple makers in the pre-populated configuration,
one for each target platform.
under `devDependencies`, and a new `forge.config.js` file that exports a configuration
object. You should see multiple makers (packages that generate distributable app bundles) in the
pre-populated configuration, one for each target platform.
### Creating a distributable
@@ -111,13 +110,14 @@ Electron Forge can be configured to create distributables in different OS-specif
:::
:::tip Creating and Adding Application Icons
:::tip Creating and adding application icons
Setting custom application icons requires a few additions to your config. Check out [Forge's icon tutorial] for more information.
Setting custom application icons requires a few additions to your config.
Check out [Forge's icon tutorial] for more information.
:::
:::note Packaging without Electron Forge
:::info Packaging without Electron Forge
If you want to manually package your code, or if you're just interested understanding the
mechanics behind packaging an Electron app, check out the full [Application Packaging]
@@ -136,64 +136,51 @@ Code signing is a security technology that you use to certify that a desktop app
created by a known source. Windows and macOS have their own OS-specific code signing
systems that will make it difficult for users to download or launch unsigned applications.
If you already have code signing certificates for Windows and macOS, you can set your
credentials in your Forge configuration. Otherwise, please refer to the full
[Code Signing] documentation to learn how to purchase a certificate and for more information
on the desktop app code signing process.
On macOS, code signing is done at the app packaging level. On Windows, distributable installers
are signed instead.
are signed instead. If you already have code signing certificates for Windows and macOS, you can set
your credentials in your Forge configuration.
:::info
For more information on code signing, check out the
[Signing macOS Apps](https://www.electronforge.io/guides/code-signing) guide in the Forge docs.
:::
<Tabs>
<TabItem value="macos" label="macOS" default>
```json title='package.json' {6-18}
{
//...
"config": {
"forge": {
//...
"packagerConfig": {
"osxSign": {
"identity": "Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)",
"hardened-runtime": true,
"entitlements": "entitlements.plist",
"entitlements-inherit": "entitlements.plist",
"signature-flags": "library"
},
"osxNotarize": {
"appleId": "felix@felix.fun",
"appleIdPassword": "this-is-a-secret"
}
}
//...
```js title='forge.config.js'
module.exports = {
packagerConfig: {
osxSign: {},
//...
osxNotarize: {
tool: 'notarytool',
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_PASSWORD,
teamId: process.env.APPLE_TEAM_ID,
}
//...
}
//...
}
```
</TabItem>
<TabItem value="windows" label="Windows">
```json title='package.json' {6-14}
{
```js title='forge.config.js'
module.exports = {
//...
"config": {
"forge": {
//...
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {
"certificateFile": "./cert.pfx",
"certificatePassword": "this-is-a-secret"
}
}
]
//...
}
}
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
certificateFile: './cert.pfx',
certificatePassword: process.env.CERTIFICATE_PASSWORD,
},
},
],
//...
}
```
@@ -214,13 +201,12 @@ information.
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
[application packaging]: ./application-distribution.md
[code signing]: ./code-signing.md
[`electron-packager`]: https://github.com/electron/electron-packager
[`electron-winstaller`]: https://github.com/electron/windows-installer
[electron forge]: https://www.electronforge.io
[electron forge cli documentation]: https://www.electronforge.io/cli#commands
[makers]: https://www.electronforge.io/config/makers
[Forge's icon tutorial]: https://www.electronforge.io/guides/create-and-add-icons
[forge's icon tutorial]: https://www.electronforge.io/guides/create-and-add-icons
<!-- Tutorial links -->

View File

@@ -78,27 +78,21 @@ Once you have it installed, you need to set it up in your Forge
configuration. A full list of options is documented in the Forge's
[`PublisherGitHubConfig`] API docs.
```json title='package.json' {6-16}
{
//...
"config": {
"forge": {
"publishers": [
{
"name": "@electron-forge/publisher-github",
"config": {
"repository": {
"owner": "github-user-name",
"name": "github-repo-name"
},
"prerelease": false,
"draft": true
}
}
]
}
}
//...
```js title='forge.config.js'
module.exports = {
publishers: [
{
name: '@electron-forge/publisher-github',
config: {
repository: {
owner: 'github-user-name',
name: 'github-repo-name',
},
prerelease: false,
draft: true,
},
},
],
}
```

View File

@@ -142,7 +142,6 @@ auto_filenames = {
"lib/common/define-properties.ts",
"lib/common/ipc-messages.ts",
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/ipc-renderer.ts",

View File

@@ -671,8 +671,6 @@ filenames = {
"shell/renderer/electron_render_frame_observer.h",
"shell/renderer/electron_renderer_client.cc",
"shell/renderer/electron_renderer_client.h",
"shell/renderer/electron_renderer_pepper_host_factory.cc",
"shell/renderer/electron_renderer_pepper_host_factory.h",
"shell/renderer/electron_sandboxed_renderer_client.cc",
"shell/renderer/electron_sandboxed_renderer_client.h",
"shell/renderer/guest_view_container.cc",

View File

@@ -176,10 +176,10 @@ WebContents.prototype.printToPDF = async function (options) {
scale: 1.0,
paperWidth: 8.5,
paperHeight: 11.0,
marginTop: 0.0,
marginBottom: 0.0,
marginLeft: 0.0,
marginRight: 0.0,
marginTop: 0.4,
marginBottom: 0.4,
marginLeft: 0.4,
marginRight: 0.4,
pageRanges: '',
preferCSSPageSize: false
};
@@ -449,12 +449,14 @@ WebContents.prototype.loadURL = function (url, options) {
const removeListeners = () => {
this.removeListener('did-finish-load', finishListener);
this.removeListener('did-fail-load', failListener);
this.removeListener('did-navigate-in-page', finishListener);
this.removeListener('did-start-navigation', navigationListener);
this.removeListener('did-stop-loading', stopLoadingListener);
this.removeListener('destroyed', stopLoadingListener);
};
this.on('did-finish-load', finishListener);
this.on('did-fail-load', failListener);
this.on('did-navigate-in-page', finishListener);
this.on('did-start-navigation', navigationListener);
this.on('did-stop-loading', stopLoadingListener);
this.on('destroyed', stopLoadingListener);

View File

@@ -186,7 +186,10 @@ export class SrcAttribute extends WebViewAttribute {
opts.userAgent = useragent;
}
(this.webViewImpl.webviewNode as Electron.WebviewTag).loadURL(this.getValue(), opts);
(this.webViewImpl.webviewNode as Electron.WebviewTag).loadURL(this.getValue(), opts)
.catch(err => {
console.error('Unexpected error while loading URL', err);
});
}
}

View File

@@ -70,29 +70,21 @@ function isInstalled () {
// unzips and makes path.txt point at the correct executable
function extractFile (zipPath) {
return new Promise((resolve, reject) => {
const distPath = process.env.ELECTRON_OVERRIDE_DIST_PATH || path.join(__dirname, 'dist');
const distPath = process.env.ELECTRON_OVERRIDE_DIST_PATH || path.join(__dirname, 'dist');
extract(zipPath, { dir: path.join(__dirname, 'dist') })
.then(() => {
// If the zip contains an "electron.d.ts" file,
// move that up
const srcTypeDefPath = path.join(distPath, 'electron.d.ts');
const targetTypeDefPath = path.join(__dirname, 'electron.d.ts');
const hasTypeDefinitions = fs.existsSync(srcTypeDefPath);
return extract(zipPath, { dir: path.join(__dirname, 'dist') }).then(() => {
// If the zip contains an "electron.d.ts" file,
// move that up
const srcTypeDefPath = path.join(distPath, 'electron.d.ts');
const targetTypeDefPath = path.join(__dirname, 'electron.d.ts');
const hasTypeDefinitions = fs.existsSync(srcTypeDefPath);
if (hasTypeDefinitions) {
try {
fs.renameSync(srcTypeDefPath, targetTypeDefPath);
} catch (err) {
reject(err);
}
}
if (hasTypeDefinitions) {
fs.renameSync(srcTypeDefPath, targetTypeDefPath);
}
// Write a "path.txt" file.
return fs.promises.writeFile(path.join(__dirname, 'path.txt'), platformPath);
})
.catch((err) => reject(err));
// Write a "path.txt" file.
return fs.promises.writeFile(path.join(__dirname, 'path.txt'), platformPath);
});
}

View File

@@ -1,12 +1,13 @@
{
"name": "electron",
"version": "21.2.0",
"version": "0.0.0-development",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
"@azure/storage-blob": "^12.9.0",
"@electron/docs-parser": "^0.12.4",
"@electron/typescript-definitions": "^8.9.5",
"@electron/asar": "^3.2.1",
"@electron/docs-parser": "^1.0.0",
"@electron/typescript-definitions": "^8.10.0",
"@octokit/auth-app": "^2.10.0",
"@octokit/rest": "^18.0.3",
"@primer/octicons": "^10.0.0",
@@ -31,7 +32,6 @@
"@types/webpack-env": "^1.16.3",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
"asar": "^3.1.0",
"aws-sdk": "^2.814.0",
"check-for-leaks": "^1.2.1",
"colors": "1.4.0",
@@ -89,7 +89,7 @@
"lint:docs-relative-links": "python3 ./script/check-relative-doc-links.py",
"lint:markdownlint": "markdownlint \"*.md\" \"docs/**/*.md\"",
"lint:js-in-markdown": "standard-markdown docs",
"create-api-json": "electron-docs-parser --dir=./",
"create-api-json": "node script/create-api-json.js",
"create-typescript-definitions": "npm run create-api-json && electron-typescript-definitions --api=electron-api.json && node spec/ts-smoke/runner.js",
"gn-typescript-definitions": "npm run create-typescript-definitions && shx cp electron.d.ts",
"pre-flight": "pre-flight",
@@ -146,4 +146,4 @@
"resolutions": {
"nan": "nodejs/nan#16fa32231e2ccd89d2804b3f765319128b20c4ac"
}
}
}

View File

@@ -125,3 +125,6 @@ fix_crash_loading_non-standard_schemes_in_iframes.patch
create_browser_v8_snapshot_file_name_fuse.patch
cherry-pick-c83640db21b5.patch
fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch
build_allow_electron_to_use_exec_script.patch
cherry-pick-67c9cbc784d6.patch
cherry-pick-933cc81c6bad.patch

View File

@@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@salesforce.com>
Date: Fri, 21 Oct 2022 16:29:06 -0700
Subject: build: allow electron to use exec_script
This is similar to the //build usecase so we're OK adding ourselves here
diff --git a/.gn b/.gn
index 7b9768eb315ef8a4d8d61bcf07d43c70f3ea9582..0857ee17a35dabc19c2a04f6a5dd7d631e4b7ee8 100644
--- a/.gn
+++ b/.gn
@@ -169,4 +169,6 @@ exec_script_whitelist =
"//tools/grit/grit_rule.gni",
"//tools/gritsettings/BUILD.gn",
+
+ "//electron/BUILD.gn"
]

View File

@@ -0,0 +1,680 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lukasz Anforowicz <lukasza@chromium.org>
Date: Tue, 30 Aug 2022 19:18:15 +0000
Subject: Validate `source_context` in ExtensionHostMsg_OpenChannelToNativeApp.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
After this CL, the Browser process will verify `source_context` in the
IPC payload of the ExtensionHostMsg_OpenChannelToNativeApp message and
avoid processing malformed or spoofed IPCs.
Change-Id: I9466dc076c4d07dbb4bec38973000dc0418565f6
Bug: 1356234
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3854987
Commit-Queue: Łukasz Anforowicz <lukasza@chromium.org>
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1041118}
diff --git a/chrome/browser/extensions/extension_security_exploit_browsertest.cc b/chrome/browser/extensions/extension_security_exploit_browsertest.cc
index 0bbdc8c38a99986d5b8f70f5582f78c7f8be3fa0..7f8ed5f1e0eef6db0504ad3e075d76738bc07243 100644
--- a/chrome/browser/extensions/extension_security_exploit_browsertest.cc
+++ b/chrome/browser/extensions/extension_security_exploit_browsertest.cc
@@ -10,6 +10,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/test/bind.h"
+#include "build/build_config.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
@@ -45,6 +46,10 @@
#include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h"
#include "url/gurl.h"
+#if !(BUILDFLAG(IS_FUCHSIA))
+#include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
+#endif
+
namespace extensions {
// ExtensionFrameHostInterceptor is a helper for:
@@ -280,6 +285,10 @@ class ExtensionSecurityExploitBrowserTest : public ExtensionBrowserTest {
InstallTestExtensions();
}
+ content::WebContents* active_web_contents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
// Asks the `extension_id` to inject `content_script` into `web_contents`.
// Returns true if the content script execution started successfully.
bool ExecuteProgrammaticContentScript(content::WebContents* web_contents,
@@ -293,36 +302,63 @@ class ExtensionSecurityExploitBrowserTest : public ExtensionBrowserTest {
browser()->profile(), extension_id, background_script);
}
+ const Extension& active_extension() { return *active_extension_; }
const ExtensionId& active_extension_id() { return active_extension_->id(); }
const ExtensionId& spoofed_extension_id() { return spoofed_extension_->id(); }
private:
+ // Installs an `active_extension` and a separate, but otherwise identical
+ // `spoofed_extension` (the only difference will be the extension id).
void InstallTestExtensions() {
- // Install an `active_extension` and a separate, but otherwise identical
- // `spoofed_extension` (the only difference will be the extension id).
- auto install_extension = [this](TestExtensionDir& dir) -> const Extension* {
+ auto install_extension =
+ [this](TestExtensionDir& dir,
+ const char* extra_manifest_bits) -> const Extension* {
const char kManifestTemplate[] = R"(
{
+ %s
"name": "ContentScriptTrackerBrowserTest - Programmatic",
"version": "1.0",
"manifest_version": 2,
- "permissions": [ "tabs", "<all_urls>", "storage" ],
+ "permissions": [
+ "tabs",
+ "<all_urls>",
+ "nativeMessaging",
+ "storage"
+ ],
"background": {"scripts": ["background_script.js"]}
} )";
- dir.WriteManifest(kManifestTemplate);
+ dir.WriteManifest(
+ base::StringPrintf(kManifestTemplate, extra_manifest_bits));
dir.WriteFile(FILE_PATH_LITERAL("background_script.js"), "");
+ dir.WriteFile(FILE_PATH_LITERAL("page.html"), "<p>page</p>");
return LoadExtension(dir.UnpackedPath());
};
- TestExtensionDir active_dir;
- TestExtensionDir spoofed_dir;
- active_extension_ = install_extension(active_dir);
- spoofed_extension_ = install_extension(spoofed_dir);
+#if !(BUILDFLAG(IS_FUCHSIA))
+ // The key below corresponds to the extension ID used by
+ // ScopedTestNativeMessagingHost::kExtensionId.
+ const char kActiveExtensionKey[] = R"(
+ "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
+ )";
+#else
+ // Native messaging is not available on Fuchsia (i.e.
+ // //chrome/browser/extensions/BUILD.gn excludes
+ // api/messaging/native_messaging_test_util.h on Fuchsia).
+ const char kActiveExtensionKey[] = "";
+#endif
+ active_extension_ = install_extension(active_dir_, kActiveExtensionKey);
+ spoofed_extension_ = install_extension(spoofed_dir_, "");
ASSERT_TRUE(active_extension_);
ASSERT_TRUE(spoofed_extension_);
+#if !(BUILDFLAG(IS_FUCHSIA))
+ ASSERT_EQ(active_extension_id(),
+ ScopedTestNativeMessagingHost::kExtensionId);
+#endif
ASSERT_NE(active_extension_id(), spoofed_extension_id());
}
+ TestExtensionDir active_dir_;
+ TestExtensionDir spoofed_dir_;
raw_ptr<const Extension> active_extension_ = nullptr;
raw_ptr<const Extension> spoofed_extension_ = nullptr;
};
@@ -333,48 +369,35 @@ class OpenChannelToExtensionExploitTest
public:
OpenChannelToExtensionExploitTest() = default;
+ using OpenChannelMessageWaiter =
+ ExtensionMessageWaiter<ExtensionHostMsg_OpenChannelToExtension>;
void SetUpOnMainThread() override {
ExtensionSecurityExploitBrowserTest::SetUpOnMainThread();
- GURL test_page_url =
- embedded_test_server()->GetURL("foo.com", "/title1.html");
- ipc_message_waiter_ = StartInterceptingIpcs(test_page_url);
- }
-
- // Waits for ExtensionHostMsg_OpenChannelToExtension IPC and returns its
- // payload.
- ExtensionHostMsg_OpenChannelToExtension::Param WaitForMessage() {
- return ipc_message_waiter_->WaitForMessage();
- }
-
- private:
- using OpenChannelMessageWaiter =
- ExtensionMessageWaiter<ExtensionHostMsg_OpenChannelToExtension>;
- std::unique_ptr<OpenChannelMessageWaiter> StartInterceptingIpcs(
- const GURL& test_page_url) {
// Start capturing IPC messages in all future/new RenderProcessHosts.
- auto ipc_message_waiter = std::make_unique<OpenChannelMessageWaiter>();
+ ipc_message_waiter_ = std::make_unique<OpenChannelMessageWaiter>();
// Navigate to an arbitrary, mostly empty test page. Make sure that a new
// RenderProcessHost is created to make sure it is covered by the
- // `ipc_message_waiter`. (A WebUI -> http navigation should swap the
+ // `ipc_message_waiter_`. (A WebUI -> http navigation should swap the
// RenderProcessHost on all platforms.)
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
+ GURL test_page_url =
+ embedded_test_server()->GetURL("foo.com", "/title1.html");
int old_process_id =
- web_contents->GetPrimaryMainFrame()->GetProcess()->GetID();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
EXPECT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL("chrome://version")));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), test_page_url));
int new_process_id =
- web_contents->GetPrimaryMainFrame()->GetProcess()->GetID();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
EXPECT_NE(old_process_id, new_process_id);
// Only intercept messages from `active_extension`'s content script running
// in the main frame's process.
ExtensionId matching_extension_id = active_extension_id();
- int matching_process_id = new_process_id;
- ipc_message_waiter->SetIpcMatcher(base::BindLambdaForTesting(
+ int matching_process_id =
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
+ ipc_message_waiter_->SetIpcMatcher(base::BindLambdaForTesting(
[matching_extension_id, matching_process_id](
int captured_render_process_id,
const ExtensionHostMsg_OpenChannelToExtension::Param& param) {
@@ -391,14 +414,16 @@ class OpenChannelToExtensionExploitTest
return true;
}));
+ }
- return ipc_message_waiter;
+ // Waits for ExtensionHostMsg_OpenChannelToExtension IPC and returns its
+ // payload.
+ ExtensionHostMsg_OpenChannelToExtension::Param WaitForMessage() {
+ return ipc_message_waiter_->WaitForMessage();
}
+ private:
std::unique_ptr<OpenChannelMessageWaiter> ipc_message_waiter_;
-
- raw_ptr<const Extension> active_extension_ = nullptr;
- raw_ptr<const Extension> spoofed_extension_ = nullptr;
};
IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
@@ -412,24 +437,22 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
// Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
// from a content script of an `active_extension_id`.
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ExecuteProgrammaticContentScript(
- web_contents, active_extension_id(),
+ active_web_contents(), active_extension_id(),
"chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
// Capture the IPC.
auto [source_context, info, channel_name, port_id] = WaitForMessage();
-
- // Mutate the IPC payload.
EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
+
+ // Mutate the IPC payload.
info.source_endpoint.extension_id = spoofed_extension_id();
// Inject the malformed/mutated IPC and verify that the renderer is terminated
// as expected.
content::RenderProcessHost* main_frame_process =
- web_contents->GetPrimaryMainFrame()->GetProcess();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess();
RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
IPC::IpcSecurityTestUtil::PwnMessageReceived(
main_frame_process->GetChannel(),
@@ -443,24 +466,22 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
FromContentScript_UnexpectedNativeAppType) {
// Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
// from a content script of an `active_extension_id`.
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ExecuteProgrammaticContentScript(
- web_contents, active_extension_id(),
+ active_web_contents(), active_extension_id(),
"chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
// Capture the IPC.
auto [source_context, info, channel_name, port_id] = WaitForMessage();
-
- // Mutate the IPC payload.
EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
+
+ // Mutate the IPC payload.
info.source_endpoint.type = MessagingEndpoint::Type::kNativeApp;
// Inject the malformed/mutated IPC and verify that the renderer is terminated
// as expected.
content::RenderProcessHost* main_frame_process =
- web_contents->GetPrimaryMainFrame()->GetProcess();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess();
RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
IPC::IpcSecurityTestUtil::PwnMessageReceived(
main_frame_process->GetChannel(),
@@ -473,24 +494,22 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
FromContentScript_UnexpectedExtensionType) {
// Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
// from a content script of an `active_extension_id`.
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ExecuteProgrammaticContentScript(
- web_contents, active_extension_id(),
+ active_web_contents(), active_extension_id(),
"chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
// Capture the IPC.
auto [source_context, info, channel_name, port_id] = WaitForMessage();
-
- // Mutate the IPC payload.
EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
+
+ // Mutate the IPC payload.
info.source_endpoint.type = MessagingEndpoint::Type::kExtension;
// Inject the malformed/mutated IPC and verify that the renderer is terminated
// as expected.
content::RenderProcessHost* main_frame_process =
- web_contents->GetPrimaryMainFrame()->GetProcess();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess();
RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
IPC::IpcSecurityTestUtil::PwnMessageReceived(
main_frame_process->GetChannel(),
@@ -504,25 +523,23 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
FromContentScript_NoExtensionIdForExtensionType) {
// Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
// from a content script of an `active_extension_id`.
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ExecuteProgrammaticContentScript(
- web_contents, active_extension_id(),
+ active_web_contents(), active_extension_id(),
"chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
// Capture the IPC.
auto [source_context, info, channel_name, port_id] = WaitForMessage();
-
- // Mutate the IPC payload.
EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
+
+ // Mutate the IPC payload.
info.source_endpoint.type = MessagingEndpoint::Type::kExtension;
info.source_endpoint.extension_id = absl::nullopt;
// Inject the malformed/mutated IPC and verify that the renderer is terminated
// as expected.
content::RenderProcessHost* main_frame_process =
- web_contents->GetPrimaryMainFrame()->GetProcess();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess();
RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
IPC::IpcSecurityTestUtil::PwnMessageReceived(
main_frame_process->GetChannel(),
@@ -536,18 +553,16 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
FromContentScript_UnexpectedWorkerContext) {
// Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
// from a content script of an `active_extension_id`.
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ExecuteProgrammaticContentScript(
- web_contents, active_extension_id(),
+ active_web_contents(), active_extension_id(),
"chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
// Capture the IPC.
auto [source_context, info, channel_name, port_id] = WaitForMessage();
-
- // Mutate the IPC payload.
EXPECT_TRUE(source_context.is_for_render_frame());
EXPECT_FALSE(source_context.is_for_service_worker());
+
+ // Mutate the IPC payload.
source_context.frame = absl::nullopt;
source_context.worker = PortContext::WorkerContext(
/* thread_id = */ 123, /* version_id = */ 456,
@@ -556,7 +571,7 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
// Inject the malformed/mutated IPC and verify that the renderer is terminated
// as expected.
content::RenderProcessHost* main_frame_process =
- web_contents->GetPrimaryMainFrame()->GetProcess();
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess();
RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
IPC::IpcSecurityTestUtil::PwnMessageReceived(
main_frame_process->GetChannel(),
@@ -566,21 +581,115 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
kill_waiter.Wait());
}
+// Native messaging is not available on Fuchsia (i.e.
+// //chrome/browser/extensions/BUILD.gn excludes
+// api/messaging/native_messaging_test_util.h on Fuchsia).
+#if !(BUILDFLAG(IS_FUCHSIA))
+
+// Test suite for covering ExtensionHostMsg_OpenChannelToNativeApp IPC.
+class OpenChannelToNativeAppExploitTest
+ : public ExtensionSecurityExploitBrowserTest {
+ public:
+ OpenChannelToNativeAppExploitTest() = default;
+
+ using OpenChannelMessageWaiter =
+ ExtensionMessageWaiter<ExtensionHostMsg_OpenChannelToNativeApp>;
+ void SetUpOnMainThread() override {
+ // Set up ExtensionMessageWaiter *before* installing the extensions (i.e.
+ // *before* the corresponding RenderProcessHost objects are created).
+ ipc_message_waiter_ = std::make_unique<OpenChannelMessageWaiter>();
+
+ // SetUpOnMainThread in the base class will install the test extensions.
+ ExtensionSecurityExploitBrowserTest::SetUpOnMainThread();
+
+ // Register a (fake, test-only) native messaging host.
+ test_native_messaging_host_.RegisterTestHost(/* user_level= */ false);
+
+ // Navigate the test tab to an extension page.
+ GURL test_page_url = active_extension().GetResourceURL("page.html");
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), test_page_url));
+
+ // Only intercept messages from the test process.
+ int matching_process_id =
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
+ ipc_message_waiter_->SetIpcMatcher(base::BindLambdaForTesting(
+ [matching_process_id](
+ int captured_render_process_id,
+ const ExtensionHostMsg_OpenChannelToNativeApp::Param& param) {
+ if (captured_render_process_id != matching_process_id)
+ return false;
+
+ return true;
+ }));
+ }
+
+ // Waits for ExtensionHostMsg_OpenChannelToNativeApp IPC and returns its
+ // payload.
+ ExtensionHostMsg_OpenChannelToNativeApp::Param WaitForMessage() {
+ return ipc_message_waiter_->WaitForMessage();
+ }
+
+ private:
+ ScopedTestNativeMessagingHost test_native_messaging_host_;
+ std::unique_ptr<OpenChannelMessageWaiter> ipc_message_waiter_;
+};
+
+IN_PROC_BROWSER_TEST_F(OpenChannelToNativeAppExploitTest,
+ SourceContextWithSpoofedExtensionId) {
+ // Trigger sending of a valid ExtensionHostMsg_OpenChannelToNativeApp IPC
+ // from a frame of an `active_extension`.
+ const char kScript[] = R"(
+ var message = {text: 'Hello!'};
+ var host = $1;
+ chrome.runtime.sendNativeMessage(host, message);
+ )";
+ ASSERT_EQ(
+ active_extension().origin(),
+ active_web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
+ ASSERT_TRUE(content::ExecuteScript(
+ active_web_contents(),
+ content::JsReplace(kScript, ScopedTestNativeMessagingHost::kHostName)));
+
+ // Capture the IPC.
+ auto [source_context, native_app_name, port_id] = WaitForMessage();
+ EXPECT_EQ(native_app_name, ScopedTestNativeMessagingHost::kHostName);
+ EXPECT_TRUE(source_context.is_for_render_frame());
+
+ // Mutate the IPC payload.
+ source_context = PortContext::ForWorker(123, // thread_id
+ 456, // version_id
+ spoofed_extension_id());
+
+ // Inject the malformed/mutated IPC and verify that the renderer is terminated
+ // as expected.
+ content::RenderProcessHost* main_frame_process =
+ active_web_contents()->GetPrimaryMainFrame()->GetProcess();
+ RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+ IPC::IpcSecurityTestUtil::PwnMessageReceived(
+ main_frame_process->GetChannel(),
+ ExtensionHostMsg_OpenChannelToNativeApp(source_context, native_app_name,
+ port_id));
+ EXPECT_EQ(bad_message::EMF_INVALID_EXTENSION_ID_FOR_WORKER_CONTEXT,
+ kill_waiter.Wait());
+}
+
+#endif // !(BUILDFLAG(IS_FUCHSIA)) - native messaging is available
+
IN_PROC_BROWSER_TEST_F(ExtensionSecurityExploitBrowserTest,
SpoofedExtensionId_ExtensionFunctionDispatcher) {
// Navigate to a test page.
GURL test_page_url =
embedded_test_server()->GetURL("foo.com", "/title1.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_page_url));
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
- content::RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame();
+ content::RenderFrameHost* main_frame =
+ active_web_contents()->GetPrimaryMainFrame();
// Verify the test setup by checking if the non-intercepted `chrome.storage`
// API call will succeed.
{
ExtensionTestMessageListener listener("Got chrome.storage response");
- ExecuteProgrammaticContentScript(web_contents, active_extension_id(), R"(
+ ExecuteProgrammaticContentScript(active_web_contents(),
+ active_extension_id(), R"(
chrome.storage.local.set(
{ test_key: 'test value'},
() => {
@@ -607,7 +716,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionSecurityExploitBrowserTest,
// terminating the misbehaving renderer process.
content::RenderProcessHostBadMojoMessageWaiter kill_waiter(
main_frame->GetProcess());
- ExecuteProgrammaticContentScript(web_contents, active_extension_id(), R"(
+ ExecuteProgrammaticContentScript(active_web_contents(), active_extension_id(),
+ R"(
chrome.storage.local.set({ test_key: 'test value2'}, () => {}); )");
EXPECT_EQ(
"Received bad user message: LocalFrameHost::Request: renderer never "
diff --git a/docs/security/compromised-renderers.md b/docs/security/compromised-renderers.md
index b7e56be454f2d42dc4ae4ac875586a01f2354d9a..2155a399e0e432fedc2792b6893440efd7fca572 100644
--- a/docs/security/compromised-renderers.md
+++ b/docs/security/compromised-renderers.md
@@ -213,14 +213,21 @@ Compromised renderers shouldnt be able to:
- Spoof the `MessageEvent.origin` seen by a recipient of a `postMessage`.
- Bypass enforcement of the `targetOrigin` argument of `postMessage`.
- Send or receive `BroadcastChannel` messages for another origin.
-- Spoof the `MessageSender.origin` seen by a recipient of a
- `chrome.runtime.sendMessage`
- (see also [MessageSender documentation](https://developers.chrome.com/extensions/runtime#type-MessageSender) and [content script security guidance](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/0ei-UCHNm34)).
+- Spoof the `MessageSender.origin`, nor `MessageSender.id` (i.e. an
+ extension id which can differ from the origin when the message is sent
+ from a content script), as seen by a recipient of a
+ `chrome.runtime.sendMessage`.
+ See also [MessageSender documentation](https://developers.chrome.com/extensions/runtime#type-MessageSender) and [content script security guidance](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/0ei-UCHNm34).
+- Spoof the id of a Chrome extension initiating
+ [native messaging](https://developer.chrome.com/docs/apps/nativeMessaging/)
+ communication.
Protection techniques:
- Using `CanAccessDataForOrigin` to verify IPCs sent by a renderer process
(e.g. in `RenderFrameProxyHost::OnRouteMessageEvent` or
`BroadcastChannelProvider::ConnectToChannel`).
+- Using `ContentScriptTracker` to check if IPCs from a given renderer process
+ can legitimately claim to act on behalf content scripts of a given extension.
**Known gaps in protection**:
- Spoofing of `MessageSender.id` object
diff --git a/extensions/browser/api/messaging/messaging_api_message_filter.cc b/extensions/browser/api/messaging/messaging_api_message_filter.cc
index 25af213a4c02d7994eddb4dcb29a37871accd807..faf61a3b2bcb531934b8522b95b64930fff463dd 100644
--- a/extensions/browser/api/messaging/messaging_api_message_filter.cc
+++ b/extensions/browser/api/messaging/messaging_api_message_filter.cc
@@ -146,6 +146,16 @@ bool IsValidSourceContext(RenderProcessHost& process,
}
}
+ // This function doesn't validate frame-flavoured `source_context`s, because
+ // PortContext::FrameContext only contains frame's `routing_id` and therefore
+ // inherently cannot spoof frames in another process (a frame is identified
+ // by its `routing_id` *and* the `process_id` of the Renderer process hosting
+ // the frame; the latter is trustworthy / doesn't come from an IPC payload).
+
+ // This function doesn't validate native app `source_context`s, because
+ // `PortContext::ForNativeHost()` is called with trustoworthy inputs (e.g. it
+ // doesn't take input from IPCs sent by a Renderer process).
+
return true;
}
@@ -217,6 +227,18 @@ void MessagingAPIMessageFilter::Shutdown() {
shutdown_notifier_subscription_ = {};
}
+content::RenderProcessHost* MessagingAPIMessageFilter::GetRenderProcessHost() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!browser_context_)
+ return nullptr;
+
+ // The IPC might race with RenderProcessHost destruction. This may only
+ // happen in scenarios that are already inherently racey, so returning nullptr
+ // (and dropping the IPC) is okay and won't lead to any additional risk of
+ // data loss.
+ return content::RenderProcessHost::FromID(render_process_id_);
+}
+
void MessagingAPIMessageFilter::OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) {
@@ -262,19 +284,14 @@ void MessagingAPIMessageFilter::OnOpenChannelToExtension(
const std::string& channel_name,
const PortId& port_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (!browser_context_)
- return;
-
- // The IPC might race with RenderProcessHost destruction. This may only
- // happen in scenarios that are already inherently racey, so dropping the IPC
- // is okay and won't lead to any additional risk of data loss.
- auto* process = content::RenderProcessHost::FromID(render_process_id_);
+ auto* process = GetRenderProcessHost();
if (!process)
return;
TRACE_EVENT("extensions", "MessageFilter::OnOpenChannelToExtension",
ChromeTrackEvent::kRenderProcessHost, *process);
ScopedExternalConnectionInfoCrashKeys info_crash_keys(info);
+ debug::ScopedPortContextCrashKeys port_context_crash_keys(source_context);
if (!IsValidMessagingSource(*process, info.source_endpoint) ||
!IsValidSourceContext(*process, source_context)) {
return;
@@ -293,7 +310,14 @@ void MessagingAPIMessageFilter::OnOpenChannelToNativeApp(
const std::string& native_app_name,
const PortId& port_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (!browser_context_)
+ auto* process = GetRenderProcessHost();
+ if (!process)
+ return;
+ TRACE_EVENT("extensions", "MessageFilter::OnOpenChannelToNativeApp",
+ ChromeTrackEvent::kRenderProcessHost, *process);
+
+ debug::ScopedPortContextCrashKeys port_context_crash_keys(source_context);
+ if (!IsValidSourceContext(*process, source_context))
return;
ChannelEndpoint source_endpoint(browser_context_, render_process_id_,
diff --git a/extensions/browser/api/messaging/messaging_api_message_filter.h b/extensions/browser/api/messaging/messaging_api_message_filter.h
index 6a0ccd698629f650d68f2b4ee168aa2b3b3a116c..3358187387cd9a5765a7bd4e522aeecfd787e06b 100644
--- a/extensions/browser/api/messaging/messaging_api_message_filter.h
+++ b/extensions/browser/api/messaging/messaging_api_message_filter.h
@@ -14,6 +14,7 @@ struct ExtensionMsg_TabTargetConnectionInfo;
namespace content {
class BrowserContext;
+class RenderProcessHost;
}
namespace extensions {
@@ -40,6 +41,11 @@ class MessagingAPIMessageFilter : public content::BrowserMessageFilter {
void Shutdown();
+ // Returns the process that the IPC came from, or `nullptr` if the IPC should
+ // be dropped (in case the IPC arrived racily after the process or its
+ // BrowserContext already got destructed).
+ content::RenderProcessHost* GetRenderProcessHost();
+
// content::BrowserMessageFilter implementation:
void OverrideThreadForMessage(const IPC::Message& message,
content::BrowserThread::ID* thread) override;
diff --git a/extensions/common/api/messaging/port_context.cc b/extensions/common/api/messaging/port_context.cc
index 6872179450d8295de7f15dc1437e9d6edefe4fde..319e2f34eca730c5eb7cf94ef8cdede0ddc3f8e1 100644
--- a/extensions/common/api/messaging/port_context.cc
+++ b/extensions/common/api/messaging/port_context.cc
@@ -40,4 +40,27 @@ PortContext PortContext::ForNativeHost() {
return PortContext();
}
+namespace debug {
+
+namespace {
+
+base::debug::CrashKeyString* GetServiceWorkerExtensionIdCrashKey() {
+ static auto* crash_key = base::debug::AllocateCrashKeyString(
+ "PortContext-worker-extension_id", base::debug::CrashKeySize::Size64);
+ return crash_key;
+}
+
+} // namespace
+
+ScopedPortContextCrashKeys::ScopedPortContextCrashKeys(
+ const PortContext& port_context) {
+ if (port_context.is_for_service_worker()) {
+ extension_id_.emplace(GetServiceWorkerExtensionIdCrashKey(),
+ port_context.worker->extension_id);
+ }
+}
+
+ScopedPortContextCrashKeys::~ScopedPortContextCrashKeys() = default;
+
+} // namespace debug
} // namespace extensions
diff --git a/extensions/common/api/messaging/port_context.h b/extensions/common/api/messaging/port_context.h
index b2e9f057b531d90dc256773959cd586953e4915c..53d94c2ad73c58d45b186a32989e2f4864e67d79 100644
--- a/extensions/common/api/messaging/port_context.h
+++ b/extensions/common/api/messaging/port_context.h
@@ -9,6 +9,7 @@
#include <string>
+#include "base/debug/crash_logging.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace extensions {
@@ -59,6 +60,19 @@ struct PortContext {
absl::optional<WorkerContext> worker;
};
+namespace debug {
+
+class ScopedPortContextCrashKeys {
+ public:
+ explicit ScopedPortContextCrashKeys(const PortContext& port_context);
+ ~ScopedPortContextCrashKeys();
+
+ private:
+ absl::optional<base::debug::ScopedCrashKeyString> extension_id_;
+};
+
+} // namespace debug
+
} // namespace extensions
#endif // EXTENSIONS_COMMON_API_MESSAGING_PORT_CONTEXT_H_

View File

@@ -0,0 +1,323 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Xiaocheng Hu <xiaochengh@chromium.org>
Date: Sat, 10 Sep 2022 05:53:49 +0000
Subject: Remove symlinks from FileChooserImpl folder upload result
FileChooserImpl is the browser-side implementation of
<input type=file>. When uploading a whole folder, it
currently uses DirectoryLister to list all the files in a
directory. The result also includes resolved symbolic links
(which may even hide deep in some subfolder), which is not a
desired behavior.
Therefore, this patch removes all symbolic links from the
result by checking each file against `base::IsLink()`. Since
the function needs blocking calls to access file data, the
job is sent to a worker pool thread.
Fixed: 1345275
Change-Id: I8ab58214c87944408c64b177e915247a7485925b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3866767
Reviewed-by: Austin Sullivan <asully@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1045491}
diff --git a/content/browser/web_contents/file_chooser_impl.cc b/content/browser/web_contents/file_chooser_impl.cc
index 7a3ea45d32c97980c141662f6a071cc517a15ad8..1aa19f7a735b444f2c33d5084edcdd14e3c2f5c5 100644
--- a/content/browser/web_contents/file_chooser_impl.cc
+++ b/content/browser/web_contents/file_chooser_impl.cc
@@ -4,8 +4,11 @@
#include "content/browser/web_contents/file_chooser_impl.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/ranges/algorithm.h"
+#include "base/task/thread_pool.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
@@ -18,6 +21,19 @@
namespace content {
+namespace {
+
+std::vector<blink::mojom::FileChooserFileInfoPtr> RemoveSymlinks(
+ std::vector<blink::mojom::FileChooserFileInfoPtr> files) {
+ auto new_end = base::ranges::remove_if(
+ files, &base::IsLink,
+ [](const auto& file) { return file->get_native_file()->file_path; });
+ files.erase(new_end, files.end());
+ return files;
+}
+
+} // namespace
+
FileChooserImpl::FileSelectListenerImpl::~FileSelectListenerImpl() {
#if DCHECK_IS_ON()
if (!was_file_select_listener_function_called_) {
@@ -51,8 +67,20 @@ void FileChooserImpl::FileSelectListenerImpl::FileSelected(
"FileSelectListener::FileSelectionCanceled()";
was_file_select_listener_function_called_ = true;
#endif
- if (owner_)
- owner_->FileSelected(std::move(files), base_dir, mode);
+ if (!owner_)
+ return;
+
+ if (mode != blink::mojom::FileChooserParams::Mode::kUploadFolder) {
+ owner_->FileSelected(base_dir, mode, std::move(files));
+ return;
+ }
+
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ base::BindOnce(&RemoveSymlinks, std::move(files)),
+ base::BindOnce(&FileChooserImpl::FileSelected, owner_->GetWeakPtr(),
+ base_dir, mode));
}
void FileChooserImpl::FileSelectListenerImpl::FileSelectionCanceled() {
@@ -162,9 +190,9 @@ void FileChooserImpl::EnumerateChosenDirectory(
}
void FileChooserImpl::FileSelected(
- std::vector<blink::mojom::FileChooserFileInfoPtr> files,
const base::FilePath& base_dir,
- blink::mojom::FileChooserParams::Mode mode) {
+ blink::mojom::FileChooserParams::Mode mode,
+ std::vector<blink::mojom::FileChooserFileInfoPtr> files) {
listener_impl_ = nullptr;
if (!render_frame_host_) {
std::move(callback_).Run(nullptr);
diff --git a/content/browser/web_contents/file_chooser_impl.h b/content/browser/web_contents/file_chooser_impl.h
index b9f11f9e6a0b548cb5ab8ca721ae823e079ce6fa..b628b29a5f84264e62bb3fa9e92550787b8342de 100644
--- a/content/browser/web_contents/file_chooser_impl.h
+++ b/content/browser/web_contents/file_chooser_impl.h
@@ -37,6 +37,8 @@ class CONTENT_EXPORT FileChooserImpl : public blink::mojom::FileChooser,
// FileSelectListener overrides:
+ // TODO(xiaochengh): Move |file| to the end of the argument list to match
+ // the argument ordering of FileChooserImpl::FileSelected().
void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
const base::FilePath& base_dir,
blink::mojom::FileChooserParams::Mode mode) override;
@@ -68,9 +70,9 @@ class CONTENT_EXPORT FileChooserImpl : public blink::mojom::FileChooser,
~FileChooserImpl() override;
- void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
- const base::FilePath& base_dir,
- blink::mojom::FileChooserParams::Mode mode);
+ void FileSelected(const base::FilePath& base_dir,
+ blink::mojom::FileChooserParams::Mode mode,
+ std::vector<blink::mojom::FileChooserFileInfoPtr> files);
void FileSelectionCanceled();
@@ -82,6 +84,10 @@ class CONTENT_EXPORT FileChooserImpl : public blink::mojom::FileChooser,
const base::FilePath& directory_path,
EnumerateChosenDirectoryCallback callback) override;
+ base::WeakPtr<FileChooserImpl> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
private:
explicit FileChooserImpl(RenderFrameHostImpl* render_frame_host);
@@ -95,6 +101,8 @@ class CONTENT_EXPORT FileChooserImpl : public blink::mojom::FileChooser,
raw_ptr<RenderFrameHostImpl> render_frame_host_;
scoped_refptr<FileSelectListenerImpl> listener_impl_;
base::OnceCallback<void(blink::mojom::FileChooserResultPtr)> callback_;
+
+ base::WeakPtrFactory<FileChooserImpl> weak_factory_{this};
};
} // namespace content
diff --git a/content/browser/web_contents/file_chooser_impl_browsertest.cc b/content/browser/web_contents/file_chooser_impl_browsertest.cc
index 5aa120cd2e9fab1492f5d0993adbb2f80a4aa732..2acd2216331bd9be56eb9705f0e9c0d3bceb9e93 100644
--- a/content/browser/web_contents/file_chooser_impl_browsertest.cc
+++ b/content/browser/web_contents/file_chooser_impl_browsertest.cc
@@ -5,14 +5,18 @@
#include "content/browser/web_contents/file_chooser_impl.h"
#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
#include "base/run_loop.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
+#include "content/test/content_browser_test_utils_internal.h"
#include "url/gurl.h"
#include "url/url_constants.h"
@@ -143,11 +147,52 @@ IN_PROC_BROWSER_TEST_F(FileChooserImplBrowserTest,
->SetListenerFunctionCalledTrueForTesting();
std::vector<blink::mojom::FileChooserFileInfoPtr> files;
files.emplace_back(blink::mojom::FileChooserFileInfoPtr(nullptr));
- chooser->FileSelected(std::move(files), base::FilePath(),
- blink::mojom::FileChooserParams::Mode::kOpen);
+ chooser->FileSelected(base::FilePath(),
+ blink::mojom::FileChooserParams::Mode::kOpen,
+ std::move(files));
// Test passes if this run_loop.Run() returns instead of timing out.
run_loop.Run();
}
+// https://crbug.com/1345275
+IN_PROC_BROWSER_TEST_F(FileChooserImplBrowserTest, UploadFolderWithSymlink) {
+ EXPECT_TRUE(NavigateToURL(
+ shell(), GetTestUrl(".", "file_input_webkitdirectory.html")));
+
+ // The folder contains a regular file and a symbolic link.
+ // When uploading the folder, the symbolic link should be excluded.
+ base::FilePath dir_test_data;
+ ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &dir_test_data));
+ base::FilePath folder_to_upload =
+ dir_test_data.AppendASCII("file_chooser").AppendASCII("dir_with_symlink");
+
+ base::FilePath text_file = folder_to_upload.AppendASCII("text_file.txt");
+ base::FilePath symlink_file = folder_to_upload.AppendASCII("symlink");
+
+ // Skip the test if symbolic links are not supported.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ if (!base::IsLink(symlink_file))
+ return;
+ }
+
+ std::unique_ptr<FileChooserDelegate> delegate(
+ new FileChooserDelegate({text_file, symlink_file}, base::OnceClosure()));
+ shell()->web_contents()->SetDelegate(delegate.get());
+ EXPECT_TRUE(ExecJs(shell(),
+ "(async () => {"
+ " let listener = new Promise("
+ " resolve => fileinput.onchange = resolve);"
+ " fileinput.click();"
+ " await listener;"
+ "})()"));
+
+ EXPECT_EQ(
+ 1, EvalJs(shell(), "document.getElementById('fileinput').files.length;"));
+ EXPECT_EQ(
+ "text_file.txt",
+ EvalJs(shell(), "document.getElementById('fileinput').files[0].name;"));
+}
+
} // namespace content
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index 8183cb7e6547f42b27afc323fe136e2157e4dd03..dbdf244571956645c6494c4cdab514dd42dbb6c2 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -447,9 +447,14 @@ Shell* OpenPopup(const ToRenderFrameHost& opener,
return new_shell_observer.GetShell();
}
+FileChooserDelegate::FileChooserDelegate(std::vector<base::FilePath> files,
+ base::OnceClosure callback)
+ : files_(std::move(files)), callback_(std::move(callback)) {}
+
FileChooserDelegate::FileChooserDelegate(const base::FilePath& file,
base::OnceClosure callback)
- : file_(file), callback_(std::move(callback)) {}
+ : FileChooserDelegate(std::vector<base::FilePath>(1, file),
+ std::move(callback)) {}
FileChooserDelegate::~FileChooserDelegate() = default;
@@ -457,16 +462,18 @@ void FileChooserDelegate::RunFileChooser(
RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) {
- // Send the selected file to the renderer process.
- auto file_info = blink::mojom::FileChooserFileInfo::NewNativeFile(
- blink::mojom::NativeFileInfo::New(file_, std::u16string()));
+ // Send the selected files to the renderer process.
std::vector<blink::mojom::FileChooserFileInfoPtr> files;
- files.push_back(std::move(file_info));
- listener->FileSelected(std::move(files), base::FilePath(),
- blink::mojom::FileChooserParams::Mode::kOpen);
+ for (const auto& file : files_) {
+ auto file_info = blink::mojom::FileChooserFileInfo::NewNativeFile(
+ blink::mojom::NativeFileInfo::New(file, std::u16string()));
+ files.push_back(std::move(file_info));
+ }
+ listener->FileSelected(std::move(files), base::FilePath(), params.mode);
params_ = params.Clone();
- std::move(callback_).Run();
+ if (callback_)
+ std::move(callback_).Run();
}
FrameTestNavigationManager::FrameTestNavigationManager(
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index 6ae5e8f81a189702d5ed9bef003696f34c525dd3..3bf1bb93e15b0a5270262837802e140ad72a9231 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -176,9 +176,11 @@ Shell* OpenPopup(const ToRenderFrameHost& opener,
class FileChooserDelegate : public WebContentsDelegate {
public:
// Constructs a WebContentsDelegate that mocks a file dialog.
- // The mocked file dialog will always reply that the user selected |file|.
- // |callback| is invoked when RunFileChooser() is called.
+ // The mocked file dialog will always reply that the user selected |file| or
+ // |files|. |callback| is invoked when RunFileChooser() is called.
FileChooserDelegate(const base::FilePath& file, base::OnceClosure callback);
+ FileChooserDelegate(std::vector<base::FilePath> files,
+ base::OnceClosure callback);
~FileChooserDelegate() override;
// Implementation of WebContentsDelegate::RunFileChooser.
@@ -190,7 +192,7 @@ class FileChooserDelegate : public WebContentsDelegate {
const blink::mojom::FileChooserParams& params() const { return *params_; }
private:
- base::FilePath file_;
+ std::vector<base::FilePath> files_;
base::OnceClosure callback_;
blink::mojom::FileChooserParamsPtr params_;
};
diff --git a/content/test/data/file_chooser/dir_with_symlink/symlink b/content/test/data/file_chooser/dir_with_symlink/symlink
new file mode 120000
index 0000000000000000000000000000000000000000..7857c689f7043265b4e6d4dcdf6d40d0be2d3d60
--- /dev/null
+++ b/content/test/data/file_chooser/dir_with_symlink/symlink
@@ -0,0 +1 @@
+../linked_text_file.txt
\ No newline at end of file
diff --git a/content/test/data/file_chooser/dir_with_symlink/text_file.txt b/content/test/data/file_chooser/dir_with_symlink/text_file.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8e27be7d6154a1f68ea9160ef0e18691d20560dc
--- /dev/null
+++ b/content/test/data/file_chooser/dir_with_symlink/text_file.txt
@@ -0,0 +1 @@
+text
diff --git a/content/test/data/file_chooser/linked_text_file.txt b/content/test/data/file_chooser/linked_text_file.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9a1f4bc60917c014eac1464ad664a0271c288b84
--- /dev/null
+++ b/content/test/data/file_chooser/linked_text_file.txt
@@ -0,0 +1 @@
+linked text file
diff --git a/content/test/data/file_input_webkitdirectory.html b/content/test/data/file_input_webkitdirectory.html
new file mode 100644
index 0000000000000000000000000000000000000000..5b7bb501f7eb5d9f28751f36380e4ad01d2da0c7
--- /dev/null
+++ b/content/test/data/file_input_webkitdirectory.html
@@ -0,0 +1 @@
+<input type="file" id="fileinput" webkitdirectory />

View File

@@ -67,12 +67,12 @@ index ad34b6230f57ad906bae515aa73ea2aaf1bfeede..b5cff1fee2d14c02f2d67cf012b7ba24
const WebSecurityOrigin& script_origin) {
return false;
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index 20d674166c78e316751bfa3ec7aed74656eea9b4..d289cfca8d8a9b093d8d30427e594c46b2f56c79 100644
index 156d8d8184c4a69a9d8b8603027d7b398e2790bf..98fa13b35b0328eb436f2d146a8cda87438e6519 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -744,6 +744,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() {
nested_runner_->QuitNow();
@@ -746,6 +746,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() {
}
pause_handle_.reset();
+ {
+ v8::HandleScope handle_scope(GetIsolate());

View File

@@ -50,3 +50,4 @@ buffer_fix_atob_input_validation.patch
v8_api_advance_api_deprecation.patch
enable_-wunqualified-std-cast-call.patch
fix_expose_the_built-in_electron_module_via_the_esm_loader.patch
chore_enable_c_17_for_native_modules.patch

View File

@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Wed, 16 Nov 2022 13:18:23 +0900
Subject: chore: enable c++17 for native modules
V8 headers shipped since 10.4 use C++17 featuers, update the compile flags
correspondinly for native addons. C++ version in this file should be updated
following the version bump in upstream.
Next update: crbug.com/1284275
diff --git a/common.gypi b/common.gypi
index c68cf1eb0f9cdd2a8fe314237c1e790fe3690272..b5f33411c68d56ae2a582b0defaa55bf10a830e0 100644
--- a/common.gypi
+++ b/common.gypi
@@ -308,7 +308,10 @@
],
'msvs_settings': {
'VCCLCompilerTool': {
- 'AdditionalOptions': ['/Zc:__cplusplus'],
+ 'AdditionalOptions': [
+ '/Zc:__cplusplus',
+ '-std:c++17',
+ ],
'BufferSecurityCheck': 'true',
'DebugInformationFormat': 1, # /Z7 embed info in .obj files
'ExceptionHandling': 0, # /EHsc
@@ -440,7 +443,7 @@
}],
[ 'OS in "linux freebsd openbsd solaris android aix cloudabi"', {
'cflags': [ '-Wall', '-Wextra', '-Wno-unused-parameter', ],
- 'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++14' ],
+ 'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++17' ],
'defines': [ '__STDC_FORMAT_MACROS' ],
'ldflags': [ '-rdynamic' ],
'target_conditions': [
@@ -580,7 +583,7 @@
['clang==1', {
'xcode_settings': {
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
- 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++14', # -std=gnu++14
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++17', # -std=gnu++17
'CLANG_CXX_LIBRARY': 'libc++',
},
}],
@@ -653,7 +656,7 @@
'-qASM',
],
'cflags_cc': [
- '-qxclang=-std=c++14',
+ '-qxclang=-std=c++17',
],
'ldflags': [
'-q64',

View File

@@ -9,7 +9,7 @@ necessary for native modules to load.
Also, some fixes relating to mksnapshot on ARM.
diff --git a/BUILD.gn b/BUILD.gn
index 83082271b8b76179b41e943190303e31fa639a95..0a23171a391606bb3408dcf8a488d075a148443a 100644
index 7b41a7500e55a5dbbef512dd8bfcc7dbc65e982c..8039c91f8800b516a64b84a1d7a9701932decae2 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -663,7 +663,7 @@ config("internal_config") {
@@ -21,7 +21,7 @@ index 83082271b8b76179b41e943190303e31fa639a95..0a23171a391606bb3408dcf8a488d075
defines += [ "BUILDING_V8_SHARED" ]
}
@@ -6166,7 +6166,7 @@ if (current_toolchain == v8_generator_toolchain) {
@@ -6169,7 +6169,7 @@ if (current_toolchain == v8_generator_toolchain) {
"src/interpreter/bytecodes.h",
]

View File

@@ -12,7 +12,7 @@ This patch can be safely removed if, when it is removed, `node.lib` does not
contain any standard C++ library exports (e.g. `std::ostringstream`).
diff --git a/BUILD.gn b/BUILD.gn
index b0c6d08bf50a3443e2589f09b6f1f5b7734c000c..7728123805054064d5acecacf6cbf8d1fe4a56c1 100644
index 8b98f871341fed2186d0bc2de42912e76bbc04e1..6d3b542d781ad1a71c4b966aa05d5f51b1b3d45e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -663,6 +663,10 @@ config("internal_config") {

View File

@@ -6,10 +6,10 @@ Subject: expose_mksnapshot.patch
Needed in order to target mksnapshot for mksnapshot zip.
diff --git a/BUILD.gn b/BUILD.gn
index 0a23171a391606bb3408dcf8a488d075a148443a..b0c6d08bf50a3443e2589f09b6f1f5b7734c000c 100644
index 8039c91f8800b516a64b84a1d7a9701932decae2..8b98f871341fed2186d0bc2de42912e76bbc04e1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -6178,7 +6178,6 @@ if (current_toolchain == v8_generator_toolchain) {
@@ -6181,7 +6181,6 @@ if (current_toolchain == v8_generator_toolchain) {
if (current_toolchain == v8_snapshot_toolchain) {
v8_executable("mksnapshot") {

17
script/create-api-json.js Normal file
View File

@@ -0,0 +1,17 @@
const { parseDocs } = require('@electron/docs-parser');
const fs = require('fs');
const path = require('path');
const { getElectronVersion } = require('./lib/get-version');
parseDocs({
baseDirectory: path.resolve(__dirname, '..'),
packageMode: 'single',
useReadme: false,
moduleVersion: getElectronVersion()
}).then((api) => {
return fs.promises.writeFile(path.resolve(__dirname, '..', 'electron-api.json'), JSON.stringify(api, null, 2));
}).catch((err) => {
console.error(err);
process.exit(1);
});

View File

@@ -3,8 +3,7 @@ const path = require('path');
const semver = require('semver');
const outputPath = process.argv[2];
const currentVersion = fs.readFileSync(path.resolve(__dirname, '../ELECTRON_VERSION'), 'utf8').trim();
const currentVersion = process.argv[3];
const parsed = semver.parse(currentVersion);
@@ -20,9 +19,11 @@ const {
} = parsed;
fs.writeFileSync(outputPath, JSON.stringify({
full_version: currentVersion,
major,
minor,
patch,
prerelease,
prerelease_number: prerelease ? parsed.prerelease[parsed.prerelease.length - 1] : '0',
has_prerelease: prerelease === '' ? 0 : 1
}, null, 2));

View File

@@ -1,4 +1,4 @@
const asar = require('asar');
const asar = require('@electron/asar');
const crypto = require('crypto');
const fs = require('fs');

View File

@@ -1,4 +1,4 @@
const asar = require('asar');
const asar = require('@electron/asar');
const assert = require('assert');
const fs = require('fs-extra');
const os = require('os');

22
script/lib/get-version.js Normal file
View File

@@ -0,0 +1,22 @@
const { spawnSync } = require('child_process');
const path = require('path');
module.exports.getElectronVersion = () => {
// Find the nearest tag to the current HEAD
// This is equivilant to our old logic of "use a value in package.json" for the following reasons
//
// 1. Whenever we updated the package.json we ALSO pushed a tag with the same version
// 2. Whenever we _reverted_ a bump all we actually did was push a commit that deleted the tag and changed the version number back
//
// The only difference in the "git describe" technique is that technically a commit can "change" it's version
// number if a tag is created / removed retroactively. i.e. the first time a commit is pushed it will be 1.2.3
// and after the tag is made rebuilding the same commit will result in it being 1.2.4
const output = spawnSync('git', ['describe', '--tags', '--abbrev=0'], {
cwd: path.resolve(__dirname, '..', '..')
});
if (output.status !== 0) {
console.error(output.stderr);
throw new Error('Failed to get current electron version');
}
return output.stdout.toString().trim().replace(/^v/g, '');
};

View File

@@ -15,7 +15,9 @@ except ImportError:
from urllib2 import urlopen
import zipfile
from lib.config import is_verbose_mode
# from lib.config import is_verbose_mode
def is_verbose_mode():
return False
ELECTRON_DIR = os.path.abspath(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
@@ -149,11 +151,17 @@ def get_electron_branding():
with open(branding_file_path) as f:
return json.load(f)
cached_electron_version = None
def get_electron_version():
SOURCE_ROOT = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
version_file = os.path.join(SOURCE_ROOT, 'ELECTRON_VERSION')
with open(version_file) as f:
return 'v' + f.read().strip()
global cached_electron_version
if cached_electron_version is None:
cached_electron_version = str.strip(execute([
'node',
'-p',
'require("./script/lib/get-version").getElectronVersion()'
], cwd=ELECTRON_DIR).decode())
return cached_electron_version
def store_artifact(prefix, key_prefix, files):
# Azure Storage

View File

@@ -67,6 +67,8 @@ async function main () {
console.error(`Found ${missing.length} missing disabled specs: \n${missing.join('\n')}`);
process.exit(1);
}
process.exit(0);
}
const options = args.default ? defaultOptions : getCustomOptions();

3
script/print-version.py Normal file
View File

@@ -0,0 +1,3 @@
from lib.util import get_electron_version
print(get_electron_version())

View File

@@ -6,6 +6,7 @@ const got = require('got');
const semver = require('semver');
const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils');
const { getElectronVersion } = require('../lib/get-version');
const rootPackageJson = require('../../package.json');
const { Octokit } = require('@octokit/rest');
@@ -34,7 +35,6 @@ const files = [
const jsonFields = [
'name',
'version',
'repository',
'description',
'license',
@@ -44,6 +44,9 @@ const jsonFields = [
let npmTag = '';
const currentElectronVersion = getElectronVersion();
const isNightlyElectronVersion = currentElectronVersion.includes('nightly');
new Promise((resolve, reject) => {
temp.mkdir('electron-npm', (err, dirPath) => {
if (err) {
@@ -68,6 +71,7 @@ new Promise((resolve, reject) => {
jsonFields.forEach((fieldName) => {
packageJson[fieldName] = rootPackageJson[fieldName];
});
packageJson.version = currentElectronVersion;
fs.writeFileSync(
path.join(tempDir, 'package.json'),
JSON.stringify(packageJson, null, 2)
@@ -75,27 +79,27 @@ new Promise((resolve, reject) => {
return octokit.repos.listReleases({
owner: 'electron',
repo: rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
repo: isNightlyElectronVersion ? 'nightlies' : 'electron'
});
})
.then((releases) => {
// download electron.d.ts from release
const release = releases.data.find(
(release) => release.tag_name === `v${rootPackageJson.version}`
(release) => release.tag_name === `v${currentElectronVersion}`
);
if (!release) {
throw new Error(`cannot find release with tag v${rootPackageJson.version}`);
throw new Error(`cannot find release with tag v${currentElectronVersion}`);
}
return release;
})
.then(async (release) => {
const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts');
if (!tsdAsset) {
throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`);
throw new Error(`cannot find electron.d.ts from v${currentElectronVersion} release assets`);
}
const typingsContent = await getAssetContents(
rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron',
isNightlyElectronVersion ? 'nightlies' : 'electron',
tsdAsset.id
);
@@ -106,11 +110,11 @@ new Promise((resolve, reject) => {
.then(async (release) => {
const checksumsAsset = release.assets.find((asset) => asset.name === 'SHASUMS256.txt');
if (!checksumsAsset) {
throw new Error(`cannot find SHASUMS256.txt from v${rootPackageJson.version} release assets`);
throw new Error(`cannot find SHASUMS256.txt from v${currentElectronVersion} release assets`);
}
const checksumsContent = await getAssetContents(
rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron',
isNightlyElectronVersion ? 'nightlies' : 'electron',
checksumsAsset.id
);
@@ -127,7 +131,7 @@ new Promise((resolve, reject) => {
.then(async (release) => {
const currentBranch = await getCurrentBranch();
if (release.tag_name.indexOf('nightly') > 0) {
if (isNightlyElectronVersion) {
// TODO(main-migration): Simplify once main branch is renamed.
if (currentBranch === 'master' || currentBranch === 'main') {
// Nightlies get published to their own module, so they should be tagged as latest
@@ -164,7 +168,7 @@ new Promise((resolve, reject) => {
.then(() => childProcess.execSync('npm pack', { cwd: tempDir }))
.then(() => {
// test that the package can install electron prebuilt from github release
const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`);
const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${currentElectronVersion}.tgz`);
return new Promise((resolve, reject) => {
const result = childProcess.spawnSync('npm', ['install', tarballPath, '--force', '--silent'], {
env: { ...process.env, electron_config_cache: tempDir },
@@ -190,7 +194,7 @@ new Promise((resolve, reject) => {
});
})
.then((tarballPath) => {
const existingVersionJSON = childProcess.execSync(`npm view electron@${rootPackageJson.version} --json`).toString('utf-8');
const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8');
// It's possible this is a re-run and we already have published the package, if not we just publish like normal
if (!existingVersionJSON) {
childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`);
@@ -198,22 +202,21 @@ new Promise((resolve, reject) => {
})
.then(() => {
const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString());
const localVersion = rootPackageJson.version;
const parsedLocalVersion = semver.parse(localVersion);
const parsedLocalVersion = semver.parse(currentElectronVersion);
if (rootPackageJson.name === 'electron') {
// We should only customly add dist tags for non-nightly releases where the package name is still
// "electron"
if (parsedLocalVersion.prerelease.length === 0 &&
semver.gt(localVersion, currentTags.latest)) {
childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`);
semver.gt(currentElectronVersion, currentTags.latest)) {
childProcess.execSync(`npm dist-tag add electron@${currentElectronVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`);
}
if (parsedLocalVersion.prerelease[0] === 'beta' &&
semver.gt(localVersion, currentTags.beta)) {
childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`);
semver.gt(currentElectronVersion, currentTags.beta)) {
childProcess.execSync(`npm dist-tag add electron@${currentElectronVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`);
}
if (parsedLocalVersion.prerelease[0] === 'alpha' &&
semver.gt(localVersion, currentTags.alpha)) {
childProcess.execSync(`npm dist-tag add electron@${localVersion} alpha --otp=${process.env.ELECTRON_NPM_OTP}`);
semver.gt(currentElectronVersion, currentTags.alpha)) {
childProcess.execSync(`npm dist-tag add electron@${currentElectronVersion} alpha --otp=${process.env.ELECTRON_NPM_OTP}`);
}
}
})

View File

@@ -18,26 +18,6 @@ require('colors');
const pass = '✓'.green;
const fail = '✗'.red;
function getLastBumpCommit (tag) {
const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString();
return JSON.parse(data);
}
async function revertBumpCommit (tag) {
const branch = await getCurrentBranch();
const commitToRevert = getLastBumpCommit(tag).hash;
await GitProcess.exec(['pull', '--rebase']);
await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR);
const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR);
if (pushDetails.exitCode === 0) {
console.log(`${pass} successfully reverted release commit.`);
} else {
const error = GitProcess.parseError(pushDetails.stderr);
console.error(`${fail} could not push release commit: `, error);
process.exit(1);
}
}
async function deleteDraft (releaseId, targetRepo) {
try {
const result = await octokit.repos.getRelease({
@@ -80,9 +60,6 @@ async function cleanReleaseArtifacts () {
const releaseId = args.releaseID.length > 0 ? args.releaseID : null;
const isNightly = args.tag.includes('nightly');
// try to revert commit regardless of tag and draft deletion status
await revertBumpCommit(args.tag);
if (releaseId) {
if (isNightly) {
await deleteDraft(releaseId, 'nightlies');

View File

@@ -12,8 +12,6 @@ const args = require('minimist')(process.argv.slice(2), {
const fs = require('fs');
const { execSync } = require('child_process');
const got = require('got');
const pkg = require('../../package.json');
const pkgVersion = `v${pkg.version}`;
const path = require('path');
const temp = require('temp').track();
const { URL } = require('url');
@@ -25,8 +23,11 @@ const pass = '✓'.green;
const fail = '✗'.red;
const { ELECTRON_DIR } = require('../lib/utils');
const { getElectronVersion } = require('../lib/get-version');
const getUrlHash = require('./get-url-hash');
const pkgVersion = `v${getElectronVersion()}`;
const octokit = new Octokit({
auth: process.env.ELECTRON_GITHUB_TOKEN
});

View File

@@ -23,7 +23,7 @@ from lib.util import get_electron_branding, execute, get_electron_version, \
SRC_DIR, ELECTRON_DIR, TS_NODE
ELECTRON_VERSION = get_electron_version()
ELECTRON_VERSION = 'v' + get_electron_version()
PROJECT_NAME = get_electron_branding()['project_name']
PRODUCT_NAME = get_electron_branding()['product_name']

View File

@@ -1,17 +1,10 @@
#!/usr/bin/env node
const { GitProcess } = require('dugite');
const { promises: fs } = require('fs');
const semver = require('semver');
const path = require('path');
const minimist = require('minimist');
const { ELECTRON_DIR } = require('../lib/utils');
const { getElectronVersion } = require('../lib/get-version');
const versionUtils = require('./version-utils');
const supported = path.resolve(ELECTRON_DIR, 'docs', 'tutorial', 'support.md');
const writeFile = fs.writeFile;
const readFile = fs.readFile;
function parseCommandLine () {
let help;
@@ -37,7 +30,7 @@ function parseCommandLine () {
// run the script
async function main () {
const opts = parseCommandLine();
const currentVersion = await versionUtils.getElectronVersion();
const currentVersion = getElectronVersion();
const version = await nextVersion(opts.bump, currentVersion);
const parsed = semver.parse(version);
@@ -54,20 +47,6 @@ async function main () {
return 0;
}
if (shouldUpdateSupported(opts.bump, currentVersion, version)) {
await updateSupported(version, supported);
}
// update all version-related files
await Promise.all([
updateVersion(version),
updatePackageJSON(version),
updateWinRC(components)
]);
// commit all updated version-related files
await commitVersionBump(version);
console.log(`Bumped to version: ${version}`);
}
@@ -118,10 +97,6 @@ async function nextVersion (bumpType, version) {
return version;
}
function shouldUpdateSupported (bump, current, version) {
return isMajorStable(bump, current) || isMajorNightly(version, current);
}
function isMajorStable (bump, currentVersion) {
if (versionUtils.isBeta(currentVersion) && (bump === 'stable')) return true;
return false;
@@ -134,59 +109,6 @@ function isMajorNightly (version, currentVersion) {
return false;
}
// update VERSION file with latest release info
async function updateVersion (version) {
const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION');
await writeFile(versionPath, version, 'utf8');
}
// update package metadata files with new version
async function updatePackageJSON (version) {
const filePath = path.resolve(ELECTRON_DIR, 'package.json');
const file = require(filePath);
file.version = version;
await writeFile(filePath, JSON.stringify(file, null, 2));
}
// push bump commit to release branch
async function commitVersionBump (version) {
const gitArgs = ['commit', '-a', '-m', `Bump v${version}`, '-n'];
await GitProcess.exec(gitArgs, ELECTRON_DIR);
}
// updates electron.rc file with new semver values
async function updateWinRC (components) {
const filePath = path.resolve(ELECTRON_DIR, 'shell', 'browser', 'resources', 'win', 'electron.rc');
const data = await readFile(filePath, 'utf8');
const arr = data.split('\n');
arr.forEach((line, idx) => {
if (line.includes('FILEVERSION')) {
arr[idx] = ` FILEVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`;
arr[idx + 1] = ` PRODUCTVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`;
} else if (line.includes('FileVersion')) {
arr[idx] = ` VALUE "FileVersion", "${versionUtils.makeVersion(components, '.')}"`;
arr[idx + 5] = ` VALUE "ProductVersion", "${versionUtils.makeVersion(components, '.')}"`;
}
});
await writeFile(filePath, arr.join('\n'));
}
// updates support.md file with new semver values (stable only)
async function updateSupported (version, filePath) {
const v = parseInt(version);
const newVersions = [`* ${v}.x.y`, `* ${v - 1}.x.y`, `* ${v - 2}.x.y`, `* ${v - 3}.x.y`];
const contents = await readFile(filePath, 'utf8');
const previousVersions = contents.split('\n').filter((elem) => {
return (/[^\n]*\.x\.y[^\n]*/).test(elem);
}, []);
const newContents = previousVersions.reduce((contents, current, i) => {
return contents.replace(current, newVersions[i]);
}, contents);
await writeFile(filePath, newContents, 'utf8');
}
if (process.mainModule === module) {
main().catch((error) => {
console.error(error);
@@ -194,4 +116,4 @@ if (process.mainModule === module) {
});
}
module.exports = { nextVersion, shouldUpdateSupported, updateSupported };
module.exports = { nextVersion };

View File

@@ -68,12 +68,6 @@ async function nextBeta (v) {
return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease');
}
async function getElectronVersion () {
const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION');
const version = await readFile(versionPath, 'utf8');
return version.trim();
}
async function nextNightly (v) {
let next = semver.valid(semver.coerce(v));
const pre = `nightly.${getCurrentDate()}`;
@@ -114,7 +108,6 @@ module.exports = {
nextAlpha,
nextBeta,
makeVersion,
getElectronVersion,
nextNightly,
preType
};

View File

@@ -66,6 +66,7 @@
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/language_util.h"
#include "shell/common/node_includes.h"
#include "shell/common/options_switches.h"
#include "shell/common/platform_util.h"
@@ -1790,6 +1791,7 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
.SetMethod("setAppLogsPath", &App::SetAppLogsPath)
.SetMethod("setDesktopName", &App::SetDesktopName)
.SetMethod("getLocale", &App::GetLocale)
.SetMethod("getPreferredSystemLanguages", &GetPreferredLanguages)
.SetMethod("getSystemLocale", &App::GetSystemLocale)
.SetMethod("getLocaleCountryCode", &App::GetLocaleCountryCode)
#if BUILDFLAG(USE_NSS_CERTS)

View File

@@ -447,6 +447,13 @@ void Browser::DockSetIcon(v8::Isolate* isolate, v8::Local<v8::Value> icon) {
image = native_image->image();
}
// This is needed when this fn is called before the browser
// process is ready, since supported scales are normally set
// by ui::ResourceBundle::InitSharedInstance
// during browser process startup.
if (!is_ready())
gfx::ImageSkia::SetSupportedScales({1.0f});
[[AtomApplication sharedApplication]
setApplicationIconImage:image.AsNSImage()];
}

View File

@@ -124,6 +124,7 @@ void PrintViewManagerElectron::PrintToPdf(
printing_rfh_ = rfh;
print_pages_params->pages = absl::get<printing::PageRanges>(parsed_ranges);
headless_jobs_.emplace_back(print_pages_params->params->document_cookie);
callback_ = std::move(callback);
// There is no need for a weak pointer here since the mojo proxy is held
@@ -147,7 +148,9 @@ void PrintViewManagerElectron::OnDidPrintWithParams(
}
}
auto& content = *result->get_params()->content;
printing::mojom::DidPrintDocumentParamsPtr& params = result->get_params();
auto& content = *params->content;
if (!content.metafile_data_region.IsValid()) {
FailJob(kInvalidMemoryHandle);
return;
@@ -163,7 +166,7 @@ void PrintViewManagerElectron::OnDidPrintWithParams(
std::string(static_cast<const char*>(map.memory()), map.size());
std::move(callback_).Run(kPrintSuccess,
base::RefCountedString::TakeString(&data));
base::Erase(headless_jobs_, params->document_cookie);
Reset();
}

View File

@@ -4,14 +4,31 @@
#include "shell/common/language_util.h"
#include "ui/base/l10n/l10n_util.h"
#include <glib.h>
#include "base/check.h"
#include "base/i18n/rtl.h"
namespace electron {
std::vector<std::string> GetPreferredLanguages() {
// Return empty as there's no API to use. You may be able to use
// GetApplicationLocale() of a browser process.
return std::vector<std::string>{};
std::vector<std::string> preferredLanguages;
// Based on
// https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5329.0:ui/base/l10n/l10n_util.cc;l=543-554
// GLib implements correct environment variable parsing with
// the precedence order: LANGUAGE, LC_ALL, LC_MESSAGES and LANG.
const char* const* languages = g_get_language_names();
DCHECK(languages); // A valid pointer is guaranteed.
DCHECK(*languages); // At least one entry, "C", is guaranteed.
for (; *languages; ++languages) {
if (strcmp(*languages, "C") != 0) {
preferredLanguages.push_back(base::i18n::GetCanonicalLocale(*languages));
}
}
return preferredLanguages;
}
} // namespace electron

View File

@@ -124,6 +124,19 @@ describe('app module', () => {
});
});
describe('app.getPreferredSystemLanguages()', () => {
ifit(process.platform !== 'linux')('should not be empty', () => {
expect(app.getPreferredSystemLanguages().length).to.not.equal(0);
});
ifit(process.platform === 'linux')('should be empty or contain C entry', () => {
const languages = app.getPreferredSystemLanguages();
if (languages.length) {
expect(languages).to.not.include('C');
}
});
});
describe('app.getLocaleCountryCode()', () => {
it('should be empty or have length of two', () => {
const localeCountryCode = app.getLocaleCountryCode();

View File

@@ -567,6 +567,10 @@ describe('BrowserWindow module', () => {
targetId: iframeTarget.targetId,
flatten: true
});
let willNavigateEmitted = false;
w.webContents.on('will-navigate', () => {
willNavigateEmitted = true;
});
await w.webContents.debugger.sendCommand('Input.dispatchMouseEvent', {
type: 'mousePressed',
x: 10,
@@ -581,10 +585,6 @@ describe('BrowserWindow module', () => {
clickCount: 1,
button: 'left'
}, sessionId);
let willNavigateEmitted = false;
w.webContents.on('will-navigate', () => {
willNavigateEmitted = true;
});
await emittedOnce(w.webContents, 'did-navigate');
expect(willNavigateEmitted).to.be.true();
});

View File

@@ -362,6 +362,12 @@ describe('webContents module', () => {
await expect(w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html'))).to.eventually.be.fulfilled();
});
it('resolves when navigating within the page', async () => {
await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html'));
await new Promise(resolve => setTimeout(resolve));
await expect(w.loadURL(w.getURL() + '#foo')).to.eventually.be.fulfilled();
});
it('rejects when failing to load a file URL', async () => {
await expect(w.loadURL('file:non-existent')).to.eventually.be.rejected()
.and.have.property('code', 'ERR_FILE_NOT_FOUND');

View File

@@ -375,6 +375,7 @@ describe('command line switches', () => {
describe('--lang switch', () => {
const currentLocale = app.getLocale();
const currentSystemLocale = app.getSystemLocale();
const currentPreferredLanguages = JSON.stringify(app.getPreferredSystemLanguages());
const testLocale = async (locale: string, result: string, printEnv: boolean = false) => {
const appPath = path.join(fixturesPath, 'api', 'locale-check');
const args = [appPath, `--set-lang=${locale}`];
@@ -397,9 +398,9 @@ describe('command line switches', () => {
expect(output).to.equal(result);
};
it('should set the locale', async () => testLocale('fr', `fr|${currentSystemLocale}`));
it('should set the locale with country code', async () => testLocale('zh-CN', `zh-CN|${currentSystemLocale}`));
it('should not set an invalid locale', async () => testLocale('asdfkl', `${currentLocale}|${currentSystemLocale}`));
it('should set the locale', async () => testLocale('fr', `fr|${currentSystemLocale}|${currentPreferredLanguages}`));
it('should set the locale with country code', async () => testLocale('zh-CN', `zh-CN|${currentSystemLocale}|${currentPreferredLanguages}`));
it('should not set an invalid locale', async () => testLocale('asdfkl', `${currentLocale}|${currentSystemLocale}|${currentPreferredLanguages}`));
const lcAll = String(process.env.LC_ALL);
ifit(process.platform === 'linux')('current process has a valid LC_ALL env', async () => {
@@ -2323,9 +2324,7 @@ describe('navigator.hid', () => {
} else {
expect(device).to.equal('');
}
if (process.arch === 'arm64' || process.arch === 'arm') {
// arm CI returns HID devices - this block may need to change if CI hardware changes.
expect(haveDevices).to.be.true();
if (haveDevices) {
// Verify that navigation will clear device permissions
const grantedDevices = await w.webContents.executeJavaScript('navigator.hid.getDevices()');
expect(grantedDevices).to.not.be.empty();

View File

@@ -31,6 +31,22 @@ const runFixtureAndEnsureCleanExit = (args: string[]) => {
});
};
const shouldRunCase = (crashCase: string) => {
switch (crashCase) {
// TODO(jkleinsc) fix this flaky test on Windows 32-bit
case 'quit-on-crashed-event': {
return (process.platform !== 'win32' || process.arch !== 'ia32');
}
// TODO(jkleinsc) fix this test on Linux on arm/arm64
case 'js-execute-iframe': {
return (process.platform !== 'linux' || (process.arch !== 'arm64' && process.arch !== 'arm'));
}
default: {
return true;
}
}
};
describe('crash cases', () => {
afterEach(() => {
for (const child of children) {
@@ -42,8 +58,7 @@ describe('crash cases', () => {
const cases = fs.readdirSync(fixturePath);
for (const crashCase of cases) {
// TODO(jkleinsc) fix this flaky test on Windows 32-bit
ifit(process.platform !== 'win32' || process.arch !== 'ia32' || crashCase !== 'quit-on-crashed-event')(`the "${crashCase}" case should not crash`, () => {
ifit(shouldRunCase(crashCase))(`the "${crashCase}" case should not crash`, () => {
const fixture = path.resolve(fixturePath, crashCase);
const argsFile = path.resolve(fixture, 'electron.args');
const args = [fixture];

View File

@@ -1,15 +1,9 @@
import { expect } from 'chai';
import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite';
import { nextVersion, shouldUpdateSupported, updateSupported } from '../script/release/version-bumper';
import { nextVersion } from '../script/release/version-bumper';
import * as utils from '../script/release/version-utils';
import * as sinon from 'sinon';
import { ifdescribe } from './spec-helpers';
const { promises: fs } = require('fs');
const path = require('path');
const fixtureDir = path.resolve(__dirname, 'fixtures', 'version-bumper', 'fixture_support.md');
const readFile = fs.readFile;
const writeFile = fs.writeFile;
class GitFake {
branches: {
@@ -96,94 +90,6 @@ describe('version-bumper', () => {
});
});
describe('updateSupported', () => {
let restore: any;
before(async () => {
restore = await readFile(fixtureDir, 'utf8');
});
afterEach(async () => {
await writeFile(fixtureDir, restore, 'utf8');
});
it('updates correctly when a new stable version is promoted from beta', async () => {
const version = '4.0.0';
const currentVersion = '4.0.0-beta.29';
if (shouldUpdateSupported('stable', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('4.x.y\n* 3.x.y\n* 2.x.y');
});
it('should not update when a new stable patch version is promoted', async () => {
const version = '3.0.1';
const currentVersion = '3.0.0';
if (shouldUpdateSupported('stable', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should not update when a new stable minor version is promoted', async () => {
const version = '3.1.0';
const currentVersion = '3.0.0';
if (shouldUpdateSupported('minor', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should not update when a new beta.1 version is promoted', async () => {
const version = '5.0.0-beta.1';
const currentVersion = '4.0.0-beta.29';
if (shouldUpdateSupported('beta', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should not update when a new beta.12 version is promoted', async () => {
const version = '4.0.0-beta.12';
const currentVersion = '4.0.0-beta.11';
if (shouldUpdateSupported('beta', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should update when a new major nightly version is promoted', async () => {
const version = '4.0.0-nightly.19950901';
const currentVersion = '3.0.0-nightly.19950828';
if (shouldUpdateSupported('nightly', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('4.x.y\n* 3.x.y\n* 2.x.y');
});
it('should not update when a new nightly version is promoted', async () => {
const version = '3.0.0-nightly.19950901';
const currentVersion = '3.0.0-nightly.19950828';
if (shouldUpdateSupported('nightly', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
});
// On macOS Circle CI we don't have a real git environment due to running
// gclient sync on a linux machine. These tests therefore don't run as expected.
ifdescribe(!(process.platform === 'linux' && process.arch.indexOf('arm') === 0) && process.platform !== 'darwin')('nextVersion', () => {

View File

@@ -9,7 +9,7 @@ app.whenReady().then(() => {
if (process.argv[3] === '--print-env') {
process.stdout.write(String(process.env.LC_ALL));
} else {
process.stdout.write(`${app.getLocale()}|${app.getSystemLocale()}`);
process.stdout.write(`${app.getLocale()}|${app.getSystemLocale()}|${JSON.stringify(app.getPreferredSystemLanguages())}`);
}
process.stdout.end();

View File

@@ -1,7 +1,7 @@
// Use this script to regenerate these fixture files
// using a new version of the asar package
const asar = require('asar');
const asar = require('@electron/asar');
const fs = require('fs');
const os = require('os');
const path = require('path');

View File

@@ -106,10 +106,22 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
"@electron/docs-parser@^0.12.4":
version "0.12.4"
resolved "https://registry.yarnpkg.com/@electron/docs-parser/-/docs-parser-0.12.4.tgz#cca403c8c2200181339c3115cdd25f3fbfc7dea3"
integrity sha512-vdkjcvkI7zTd2v1A8qsl5+HY+9AQCrW5Eh60I9rhPtUPoxo2V1pQwogTW6kzc3XZ54crTa7R3KxwkZpSbcGCug==
"@electron/asar@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.1.tgz#c4143896f3dd43b59a80a9c9068d76f77efb62ea"
integrity sha512-hE2cQMZ5+4o7+6T2lUaVbxIzrOjZZfX7dB02xuapyYFJZEAiWTelq6J3mMoxzd0iONDvYLPVKecB5tyjIoVDVA==
dependencies:
chromium-pickle-js "^0.2.0"
commander "^5.0.0"
glob "^7.1.6"
minimatch "^3.0.4"
optionalDependencies:
"@types/glob" "^7.1.1"
"@electron/docs-parser@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@electron/docs-parser/-/docs-parser-1.0.0.tgz#1844ed2e18528ea56aaef0ace1cfa0633a6fa1b1"
integrity sha512-nIqEO8Ga6LavdaY2aJMPfq2vSOPVlgOvNv7jpiyaoqsAz5vYnWNUnxeCyaalCaDyFiKhVeHbKwP8Kt2TENwneg==
dependencies:
"@types/markdown-it" "^10.0.0"
chai "^4.2.0"
@@ -121,10 +133,10 @@
ora "^4.0.3"
pretty-ms "^5.1.0"
"@electron/typescript-definitions@^8.9.5":
version "8.9.5"
resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.9.5.tgz#e6cb08e0e7c9656e178b892eab50866a8a80bf7a"
integrity sha512-xDLFl6joGpA8c9cGSPWC3DFHyIGf9+OWZmDrPbGJW1URt6C1ukdQWKSmjb1Rttb94QQxBrGuUlSyz27IQgLFsw==
"@electron/typescript-definitions@^8.10.0":
version "8.10.0"
resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.10.0.tgz#e9cf2b329ec4b0b76947ef751725383a6cf8994d"
integrity sha512-FVc2y0GUfxFZDoma0scYiMxkoalle19Fq332fNFGWoCJ9rCj5OUvriewSjPtGBsRuHv2xaMS5MhBuy2/pRuFuQ==
dependencies:
"@types/node" "^11.13.7"
chalk "^2.4.2"
@@ -1208,18 +1220,6 @@ arrify@^1.0.0:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
asar@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/asar/-/asar-3.1.0.tgz#70b0509449fe3daccc63beb4d3c7d2e24d3c6473"
integrity sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ==
dependencies:
chromium-pickle-js "^0.2.0"
commander "^5.0.0"
glob "^7.1.6"
minimatch "^3.0.4"
optionalDependencies:
"@types/glob" "^7.1.1"
asn1.js@^4.0.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"