mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
102 Commits
fix-ensure
...
gh-macos-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acb41e76aa | ||
|
|
96120a6f4d | ||
|
|
65bd2db0f5 | ||
|
|
d2c390983b | ||
|
|
ff6d7c2564 | ||
|
|
7f2ba4ba80 | ||
|
|
2a7a61dcf5 | ||
|
|
c001d8b130 | ||
|
|
1e376d2aa7 | ||
|
|
84b0beaa47 | ||
|
|
0e297a3903 | ||
|
|
af90e37cae | ||
|
|
636ed1a0c5 | ||
|
|
238f815c5c | ||
|
|
a7ff541de3 | ||
|
|
ec4d5a715f | ||
|
|
62a46d46e1 | ||
|
|
674242f4ef | ||
|
|
7e31c0f7a2 | ||
|
|
60a458c455 | ||
|
|
2dd31e1682 | ||
|
|
2a48ecca1d | ||
|
|
7fd77bbb50 | ||
|
|
4813397cc4 | ||
|
|
f79037cfcc | ||
|
|
43d29911d1 | ||
|
|
a501abb69c | ||
|
|
b221c14d6f | ||
|
|
451c419fed | ||
|
|
2ea4e696d0 | ||
|
|
5031384ec4 | ||
|
|
3b43ec9f63 | ||
|
|
05b902fc93 | ||
|
|
0004836b2b | ||
|
|
ca329e167b | ||
|
|
bc36c67fa0 | ||
|
|
a320436ead | ||
|
|
64389c34d6 | ||
|
|
a2e042a278 | ||
|
|
9c283e7acd | ||
|
|
f3cfa779d4 | ||
|
|
0d91db25ec | ||
|
|
0a64d90670 | ||
|
|
c0145cb127 | ||
|
|
8962cde240 | ||
|
|
f904225b02 | ||
|
|
0fd69734d0 | ||
|
|
eb806b2f8f | ||
|
|
7085243476 | ||
|
|
ebfde8007e | ||
|
|
82739863be | ||
|
|
eb6423a713 | ||
|
|
cbdbef48c5 | ||
|
|
e2e4636deb | ||
|
|
fd399bee29 | ||
|
|
b2178f6eae | ||
|
|
3e016b2381 | ||
|
|
8226449304 | ||
|
|
e725c30686 | ||
|
|
75f4c272c9 | ||
|
|
e61370b29e | ||
|
|
244d1a0e80 | ||
|
|
fbc0668fcd | ||
|
|
86665a509a | ||
|
|
b02af910c4 | ||
|
|
4d53d5b6d9 | ||
|
|
07df2170b0 | ||
|
|
b70ac5d2b9 | ||
|
|
dce0376527 | ||
|
|
652943a5ae | ||
|
|
1411101adc | ||
|
|
f0313adced | ||
|
|
2688204d08 | ||
|
|
6a3b713201 | ||
|
|
29558326d0 | ||
|
|
a2de07ad8a | ||
|
|
541fbc7264 | ||
|
|
2c9e55af1b | ||
|
|
2571bf10f3 | ||
|
|
d22bd05d68 | ||
|
|
3d8e477522 | ||
|
|
9a3b05de7b | ||
|
|
8720664dac | ||
|
|
83a0ead9ee | ||
|
|
ba3b647fd7 | ||
|
|
76f7bbb0a8 | ||
|
|
22c149812c | ||
|
|
42164d7081 | ||
|
|
3eb94b72ba | ||
|
|
752f2eb124 | ||
|
|
beafbfd511 | ||
|
|
d54645e554 | ||
|
|
0bf53a3876 | ||
|
|
62d4b21819 | ||
|
|
61457c9498 | ||
|
|
c6102b9278 | ||
|
|
72c2b9e862 | ||
|
|
08241669bc | ||
|
|
6e36153799 | ||
|
|
4f76fff978 | ||
|
|
c82ec0c72b | ||
|
|
c57ce31e84 |
@@ -49,28 +49,30 @@ jobs:
|
||||
docker:
|
||||
- image: cimg/node:16.14
|
||||
steps:
|
||||
- checkout
|
||||
- path-filtering/set-parameters:
|
||||
base-revision: main
|
||||
mapping: |
|
||||
^((?!docs/).)*$ run-build-mac true
|
||||
^((?!docs/).)*$ run-build-linux true
|
||||
docs/.* run-docs-only true
|
||||
^((?!docs/).)*$ run-docs-only false
|
||||
- run:
|
||||
command: |
|
||||
cd .circleci/config
|
||||
yarn
|
||||
export CIRCLECI_BINARY="$HOME/circleci"
|
||||
curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | DESTDIR=$CIRCLECI_BINARY bash
|
||||
node build.js
|
||||
name: Pack config.yml
|
||||
- run:
|
||||
name: Set params
|
||||
command: node .circleci/config/params.js
|
||||
- continuation/continue:
|
||||
configuration_path: .circleci/config-staging/built.yml
|
||||
parameters: /tmp/pipeline-parameters.json
|
||||
command: echo "CircleCI disabled."
|
||||
# - checkout
|
||||
# - path-filtering/set-parameters:
|
||||
# base-revision: main
|
||||
# mapping: |
|
||||
# ^((?!docs/).)*$ run-build-mac true
|
||||
# ^((?!docs/).)*$ run-build-linux true
|
||||
# docs/.* run-docs-only true
|
||||
# ^((?!docs/).)*$ run-docs-only false
|
||||
# - run:
|
||||
# command: |
|
||||
# cd .circleci/config
|
||||
# yarn
|
||||
# export CIRCLECI_BINARY="$HOME/circleci"
|
||||
# curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | DESTDIR=$CIRCLECI_BINARY bash
|
||||
# node build.js
|
||||
# name: Pack config.yml
|
||||
# - run:
|
||||
# name: Set params
|
||||
# command: node .circleci/config/params.js
|
||||
# - continuation/continue:
|
||||
# configuration_path: .circleci/config-staging/built.yml
|
||||
# parameters: /tmp/pipeline-parameters.json
|
||||
|
||||
# Initial setup workflow
|
||||
workflows:
|
||||
|
||||
14
.github/workflows/config/gclient.diff
vendored
Normal file
14
.github/workflows/config/gclient.diff
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
diff --git a/gclient.py b/gclient.py
|
||||
index 59e2b4c5197928bdba1ef69bdbe637d7dfe471c1..b4bae5e48c83c84bd867187afaf40eed16e69851 100755
|
||||
--- a/gclient.py
|
||||
+++ b/gclient.py
|
||||
@@ -739,7 +739,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
|
||||
|
||||
if dep_type == 'cipd':
|
||||
cipd_root = self.GetCipdRoot()
|
||||
- for package in dep_value.get('packages', []):
|
||||
+ packages = dep_value.get('packages', [])
|
||||
+ for package in (x for x in packages if "infra/3pp/tools/swift-format" not in x.get('package')):
|
||||
deps_to_add.append(
|
||||
CipdDependency(parent=self,
|
||||
name=name,
|
||||
4
.github/workflows/issue-opened.yml
vendored
4
.github/workflows/issue-opened.yml
vendored
@@ -38,6 +38,8 @@ jobs:
|
||||
- run: npm install mdast-util-from-markdown@2.0.0 unist-util-select@5.1.0
|
||||
- name: Add labels
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
env:
|
||||
ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
@@ -47,7 +49,7 @@ jobs:
|
||||
const [ owner, repo ] = '${{ github.repository }}'.split('/');
|
||||
const issue_number = ${{ github.event.issue.number }};
|
||||
|
||||
const tree = fromMarkdown(`${{ github.event.issue.body }}`);
|
||||
const tree = fromMarkdown(process.env.ISSUE_BODY);
|
||||
|
||||
const labels = [];
|
||||
|
||||
|
||||
623
.github/workflows/macos-build.yml
vendored
Normal file
623
.github/workflows/macos-build.yml
vendored
Normal file
@@ -0,0 +1,623 @@
|
||||
name: Build MacOS
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
IS_RELEASE:
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
GN_CONFIG:
|
||||
required: false
|
||||
type: string
|
||||
default: false//electron/build/args/testing.gn
|
||||
GN_BUILD_TYPE:
|
||||
required: false
|
||||
type: string
|
||||
default: testing
|
||||
GENERATE_SYMBOLS:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
UPLOAD_TO_STORAGE:
|
||||
required: false
|
||||
type: string
|
||||
default: '0'
|
||||
|
||||
|
||||
env:
|
||||
AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }}
|
||||
AZURE_STORAGE_KEY: ${{ secrets.AZURE_STORAGE_KEY }}
|
||||
AZURE_STORAGE_CONTAINER_NAME: ${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}
|
||||
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
||||
# Disable pre-compiled headers to reduce out size - only useful for rebuilds
|
||||
GN_BUILDFLAG_ARGS: 'enable_precompiled_headers = false'
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac --custom-var=host_cpu=arm64'
|
||||
CHECK_DIST_MANIFEST: '1'
|
||||
|
||||
jobs:
|
||||
checkout:
|
||||
runs-on: LargeLinuxRunner
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src/electron
|
||||
- name: Install Azure CLI
|
||||
run: |
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||
- name: Set GIT_CACHE_PATH to make gclient to use the cache
|
||||
run: |
|
||||
echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV
|
||||
- name: Setup Node.js/npm
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
cache: yarn
|
||||
cache-dependency-path: src/electron/yarn.lock
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
# remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems
|
||||
sed -i '' '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
else
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
# Remove swift-format dep from cipd on macOS until we send a patch upstream.
|
||||
cd depot_tools
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
fi
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
- name: Generate DEPS Hash
|
||||
run: |
|
||||
node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
echo "DEPSHASH=v1-src-cache-$(shasum src/electron/.depshash | cut -f1 -d' ')" >> $GITHUB_ENV
|
||||
- name: Check If Cache Exists
|
||||
id: check-cache
|
||||
run: |
|
||||
exists_json=$(az storage blob exists \
|
||||
--account-name $AZURE_STORAGE_ACCOUNT \
|
||||
--account-key $AZURE_STORAGE_KEY \
|
||||
--container-name $AZURE_STORAGE_CONTAINER_NAME \
|
||||
--name $DEPSHASH)
|
||||
|
||||
cache_exists=$(echo $exists_json | jq -r '.exists')
|
||||
echo "cache_exists=$cache_exists" >> $GITHUB_OUTPUT
|
||||
|
||||
if (test "$cache_exists" = "true"); then
|
||||
echo "Cache Exists for $DEPSHASH"
|
||||
else
|
||||
echo "Cache Does Not Exist for $DEPSHASH"
|
||||
fi
|
||||
- name: Gclient Sync
|
||||
# If there is no existing src cache, we need to do a full gclient sync.
|
||||
# TODO(codebytere): Add stale patch handling for non-release builds.
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
run: |
|
||||
gclient config \
|
||||
--name "src/electron" \
|
||||
--unmanaged \
|
||||
${GCLIENT_EXTRA_ARGS} \
|
||||
"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
|
||||
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 gclient sync --with_branch_heads --with_tags -vvvvv
|
||||
# delete all .git directories under src/ except for
|
||||
# third_party/angle/ and third_party/dawn/ because of build time generation of files
|
||||
# gen/angle/commit.h depends on third_party/angle/.git/HEAD
|
||||
# https://chromium-review.googlesource.com/c/angle/angle/+/2074924
|
||||
# and dawn/common/Version_autogen.h depends on third_party/dawn/.git/HEAD
|
||||
# https://dawn-review.googlesource.com/c/dawn/+/83901
|
||||
# TODO: maybe better to always leave out */.git/HEAD file for all targets ?
|
||||
- name: Delete .git directories under src to free space
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
run: |
|
||||
cd src
|
||||
( find . -type d -name ".git" -not -path "./third_party/angle/*" -not -path "./third_party/dawn/*" -not -path "./electron/*" ) | xargs rm -rf
|
||||
- name: Minimize Cache Size for Upload
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
run: |
|
||||
rm -rf src/android_webview
|
||||
rm -rf src/ios/chrome
|
||||
rm -rf src/third_party/blink/web_tests
|
||||
rm -rf src/third_party/blink/perf_tests
|
||||
rm -rf src/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
|
||||
rm -rf src/electron
|
||||
- name: Compress Src Directory
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
run: |
|
||||
echo "Uncompressed src size: $(du -sh src | cut -f1 -d' ')"
|
||||
tar -cvf $DEPSHASH.tar src
|
||||
echo "Compressed src to $(du -sh $DEPSHASH.tar | cut -f1 -d' ')"
|
||||
- name: Upload Compressed Src Cache to Azure
|
||||
if: steps.check-cache.outputs.cache_exists == 'false'
|
||||
run: |
|
||||
az storage blob upload \
|
||||
--account-name $AZURE_STORAGE_ACCOUNT \
|
||||
--account-key $AZURE_STORAGE_KEY \
|
||||
--container-name $AZURE_STORAGE_CONTAINER_NAME \
|
||||
--file $DEPSHASH.tar \
|
||||
--name $DEPSHASH \
|
||||
--debug
|
||||
build:
|
||||
runs-on: macos-13-xlarge
|
||||
needs: checkout
|
||||
env:
|
||||
AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }}
|
||||
AZURE_STORAGE_KEY: ${{ secrets.AZURE_STORAGE_KEY }}
|
||||
AZURE_STORAGE_CONTAINER_NAME: ${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}
|
||||
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
||||
steps:
|
||||
- name: Load Build Tools
|
||||
run: |
|
||||
npm i -g @electron/build-tools
|
||||
e init --root=$(pwd) --out=Default ${{ inputs.GN_BUILD_TYPE }}
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src/electron
|
||||
- name: Install Azure CLI
|
||||
run: |
|
||||
brew update && brew install azure-cli
|
||||
- name: Setup Node.js/npm
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
cache: yarn
|
||||
cache-dependency-path: src/electron/yarn.lock
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
# remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems
|
||||
sed -i '' '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
else
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
# Remove swift-format dep from cipd on macOS until we send a patch upstream.
|
||||
cd depot_tools
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
fi
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
- name: Generate DEPS Hash
|
||||
run: |
|
||||
node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
echo "DEPSHASH=v1-src-cache-$(shasum src/electron/.depshash | cut -f1 -d' ')" >> $GITHUB_ENV
|
||||
- name: Download Src Cache
|
||||
# The cache will always exist here as a result of the checkout job
|
||||
# Either it was uploaded to Azure in the checkout job for this commit
|
||||
# or it was uploaded in the checkout job for a previous commit.
|
||||
run: |
|
||||
az storage blob download \
|
||||
--account-name $AZURE_STORAGE_ACCOUNT \
|
||||
--account-key $AZURE_STORAGE_KEY \
|
||||
--container-name $AZURE_STORAGE_CONTAINER_NAME \
|
||||
--name $DEPSHASH \
|
||||
--file $DEPSHASH.tar \
|
||||
- name: Unzip and Ensure Src Cache
|
||||
run: |
|
||||
echo "Downloaded cache is $(du -sh $DEPSHASH.tar | cut -f1)"
|
||||
mkdir temp-cache
|
||||
tar -xvf $DEPSHASH.tar -C temp-cache
|
||||
echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)"
|
||||
|
||||
if [ -d "temp-cache/src" ]; then
|
||||
echo "Relocating Cache"
|
||||
rm -rf src
|
||||
mv temp-cache/src src
|
||||
|
||||
echo "Deleting zip file"
|
||||
rm -rf $DEPSHASH.tar
|
||||
fi
|
||||
|
||||
if [ ! -d "src/third_party/blink" ]; then
|
||||
echo "Cache was not correctly restored - exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Wiping Electron Directory"
|
||||
rm -rf src/electron
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src/electron
|
||||
- name: Run Electron Only Hooks
|
||||
run: |
|
||||
echo "Running Electron Only Hooks"
|
||||
gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
- name: Regenerate DEPS Hash
|
||||
run: |
|
||||
(cd src/electron && git checkout .) && node src/electron/script/generate-deps-hash.js && cat src/electron/.depshash-target
|
||||
echo "DEPSHASH=$(shasum src/electron/.depshash | cut -f1 -d' ')" >> $GITHUB_ENV
|
||||
- name: Add CHROMIUM_BUILDTOOLS_PATH to env
|
||||
run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV
|
||||
- name: Fix Sync
|
||||
# This step is required to correct for differences between "gclient sync"
|
||||
# on Linux and the expected state on macOS. This requires:
|
||||
# 1. Fixing Clang Install (wrong binary)
|
||||
# 2. Fixing esbuild (wrong binary)
|
||||
# 3. Fixing rustc (wrong binary)
|
||||
# 4. Fixing gn (wrong binary)
|
||||
# 5. Fix reclient (wrong binary)
|
||||
# 6. Fixing dsymutil (wrong binary)
|
||||
# 7. Ensuring we are using the correct ninja and adding it to PATH
|
||||
# 8. Fixing angle (wrong remote)
|
||||
run : |
|
||||
SEDOPTION="-i ''"
|
||||
rm -rf src/third_party/llvm-build
|
||||
python3 src/tools/clang/scripts/update.py
|
||||
|
||||
echo 'infra/3pp/tools/esbuild/${platform}' `gclient getdep --deps-file=src/third_party/devtools-frontend/src/DEPS -r 'third_party/esbuild:infra/3pp/tools/esbuild/${platform}'` > esbuild_ensure_file
|
||||
# Remove extra output from calling gclient getdep which always calls update_depot_tools
|
||||
sed -i '' "s/Updating depot_tools... //g" esbuild_ensure_file
|
||||
cipd ensure --root src/third_party/devtools-frontend/src/third_party/esbuild -ensure-file esbuild_ensure_file
|
||||
|
||||
rm -rf src/third_party/rust-toolchain
|
||||
python3 src/tools/rust/update_rust.py
|
||||
|
||||
# Prevent calling gclient getdep which always calls update_depot_tools
|
||||
echo 'gn/gn/mac-${arch}' `gclient getdep --deps-file=src/DEPS -r 'src/buildtools/mac:gn/gn/mac-${arch}'` > gn_ensure_file
|
||||
sed -i '' "s/Updating depot_tools... //g" gn_ensure_file
|
||||
cipd ensure --root src/buildtools/mac -ensure-file gn_ensure_file
|
||||
|
||||
# Prevent calling gclient getdep which always calls update_depot_tools
|
||||
echo 'infra/rbe/client/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/buildtools/reclient:infra/rbe/client/${platform}'` > gn_ensure_file
|
||||
sed -i '' "s/Updating depot_tools... //g" gn_ensure_file
|
||||
cipd ensure --root src/buildtools/reclient -ensure-file gn_ensure_file
|
||||
python3 src/buildtools/reclient_cfgs/configure_reclient_cfgs.py --rbe_instance "projects/rbe-chrome-untrusted/instances/default_instance" --reproxy_cfg_template reproxy.cfg.template --rewrapper_cfg_project "" --skip_remoteexec_cfg_fetch
|
||||
|
||||
DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1
|
||||
python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang -s $DSYM_SHA_FILE -o src/tools/clang/dsymutil/bin/dsymutil
|
||||
|
||||
echo 'infra/3pp/tools/ninja/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/third_party/ninja:infra/3pp/tools/ninja/${platform}'` > ninja_ensure_file
|
||||
sed $SEDOPTION "s/Updating depot_tools... //g" ninja_ensure_file
|
||||
cipd ensure --root src/third_party/ninja -ensure-file ninja_ensure_file
|
||||
|
||||
echo "$(pwd)/src/third_party/ninja" >> $GITHUB_PATH
|
||||
|
||||
cd src/third_party/angle
|
||||
rm -f .git/objects/info/alternates
|
||||
git remote set-url origin https://chromium.googlesource.com/angle/angle.git
|
||||
cp .git/config .git/config.backup
|
||||
git remote remove origin
|
||||
mv .git/config.backup .git/config
|
||||
git fetch
|
||||
- name: Install build-tools & Setup RBE
|
||||
run: |
|
||||
echo "NUMBER_OF_NINJA_PROCESSES=200" >> $GITHUB_ENV
|
||||
cd ~/.electron_build_tools
|
||||
npx yarn --ignore-engines
|
||||
# Pull down credential helper and print status
|
||||
node -e "require('./src/utils/reclient.js').downloadAndPrepare({})"
|
||||
HELPER=$(node -p "require('./src/utils/reclient.js').helperPath({})")
|
||||
$HELPER login
|
||||
echo 'RBE_service='`node -e "console.log(require('./src/utils/reclient.js').serviceAddress)"` >> $GITHUB_ENV
|
||||
echo 'RBE_experimental_credentials_helper='`node -e "console.log(require('./src/utils/reclient.js').helperPath({}))"` >> $GITHUB_ENV
|
||||
echo 'RBE_experimental_credentials_helper_args=print' >> $GITHUB_ENV
|
||||
- name: Build Electron (darwin)
|
||||
run: |
|
||||
cd src/electron
|
||||
# TODO(codebytere): remove this once we figure out why .git/packed-refs is initially missing
|
||||
git pack-refs
|
||||
cd ..
|
||||
|
||||
ulimit -n 10000
|
||||
sudo launchctl limit maxfiles 65536 200000
|
||||
NINJA_SUMMARIZE_BUILD=1 e build -j $NUMBER_OF_NINJA_PROCESSES
|
||||
cp out/Default/.ninja_log out/electron_ninja_log
|
||||
node electron/script/check-symlinks.js
|
||||
# TODO(vertdinde): this step doesn't happen on MAS
|
||||
# - name: Build Electron Node Headers (darwin)
|
||||
# run: |
|
||||
# cd src
|
||||
# autoninja -C out/Default third_party/electron_node:overlapped-checker -j $NUMBER_OF_NINJA_PROCESSES
|
||||
# Additional Targets: shell_browser_ui_unittests third_party/electron_node:headers third_party/electron_node:overlapped-checker electron:hunspell_dictionaries_zip
|
||||
- name: Build Electron dist.zip (darwin)
|
||||
run: |
|
||||
cd src
|
||||
e build electron:electron_dist_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
if [ "$CHECK_DIST_MANIFEST" == "1" ]; then
|
||||
target_os=mac
|
||||
target_cpu=arm64
|
||||
electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.$target_os.$target_cpu.manifest
|
||||
fi
|
||||
- name: Build Mksnapshot (darwin)
|
||||
run: |
|
||||
cd src
|
||||
e build electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
|
||||
gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
|
||||
# Remove unused args from mksnapshot_args
|
||||
SEDOPTION="-i ''"
|
||||
sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args
|
||||
sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args
|
||||
sed $SEDOPTION '/The gn arg use_goma=true .*/d' out/Default/mksnapshot_args
|
||||
e build electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
|
||||
- name: Build Chromedriver (darwin)
|
||||
run: |
|
||||
cd src
|
||||
e build electron:electron_chromedriver -j $NUMBER_OF_NINJA_PROCESSES
|
||||
e build electron:electron_chromedriver_zip
|
||||
# NOTE (vertedinde): We strip binaries/symbols on the Linux job, not the Mac job
|
||||
- name: Generate & Zip Symbols (darwin)
|
||||
run: |
|
||||
# Generate breakpad symbols on release builds
|
||||
if [ ${{ inputs.GENERATE_SYMBOLS }} == "true" ]; then
|
||||
e build electron:electron_symbols
|
||||
fi
|
||||
cd src
|
||||
export BUILD_PATH="$(pwd)/out/Default"
|
||||
e build electron:licenses
|
||||
e build electron:electron_version_file
|
||||
if [ ${{ inputs.IS_RELEASE }} == "true" ]; then
|
||||
DELETE_DSYMS_AFTER_ZIP=1 electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
else
|
||||
electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
fi
|
||||
# Only generate ffmpeg, hunspell, and typescript definitions for release builds
|
||||
- name: Generate ffmpeg
|
||||
run: |
|
||||
if [ ${{ inputs.IS_RELEASE }} == "true" ]; then
|
||||
cd src
|
||||
gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true $GN_EXTRA_ARGS"
|
||||
autoninja -C out/ffmpeg electron:electron_ffmpeg_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
fi
|
||||
- name: Generate Hunspell Dictionaries
|
||||
run: |
|
||||
if [ ${{ inputs.IS_RELEASE }} == "true" ]; then
|
||||
cd src
|
||||
autoninja -C out/Default electron:hunspell_dictionaries_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
fi
|
||||
- name: Generate TypeScript Definitions
|
||||
run: |
|
||||
if [ ${{ inputs.IS_RELEASE }} == "true" ]; then
|
||||
cd src/electron
|
||||
node script/yarn create-typescript-definitions
|
||||
fi
|
||||
# TODO(vertedinde): These uploads currently point to a different Azure bucket & GitHub Repo
|
||||
- name: Publish Electron Dist
|
||||
run: |
|
||||
rm -rf src/out/Default/obj
|
||||
cd src/electron
|
||||
if [ ${{ inputs.UPLOAD_TO_STORAGE }} == "1" ]; then
|
||||
echo 'Uploading Electron release distribution to Azure'
|
||||
# script/release/uploaders/upload.py --verbose --upload_to_storage
|
||||
else
|
||||
echo 'Uploading Electron release distribution to GitHub releases'
|
||||
# script/release/uploaders/upload.py --verbose
|
||||
fi
|
||||
# The current generated_artifacts_<< artifact.key >> name was taken from CircleCI
|
||||
# to ensure we don't break anything, but we may be able to improve that.
|
||||
- name: Move all Generated Artifacts to Upload Folder
|
||||
run: ./src/electron/script/actions/move-artifacts.sh
|
||||
- name: Upload Generated Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: generated_artifacts_darwin
|
||||
path: ./generated_artifacts_darwin
|
||||
- name: Persist Build Artifacts
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
src/out/Default/gen/node_headers
|
||||
src/out/Default/overlapped-checker
|
||||
src/electron
|
||||
src/third_party/electron_node
|
||||
src/third_party/nan
|
||||
src/cross-arch-snapshots
|
||||
src/third_party/llvm-build
|
||||
src/build/linux
|
||||
src/buildtools/mac
|
||||
src/buildtools/third_party/libc++
|
||||
src/buildtools/third_party/libc++abi
|
||||
src/third_party/libc++
|
||||
src/third_party/libc++abi
|
||||
src/out/Default/obj/buildtools/third_party
|
||||
src/v8/tools/builtins-pgo
|
||||
key: ${{ runner.os }}-build-artifacts-darwin-${{ github.sha }}
|
||||
- name: Create MAS Config
|
||||
run: |
|
||||
mv src/electron/script/actions/evm.mas.json $HOME/.electron_build_tools/configs/evm.mas.json
|
||||
echo "MAS_BUILD=true" >> $GITHUB_ENV
|
||||
e use mas
|
||||
- name: Build Electron (mas)
|
||||
run: |
|
||||
rm -rf "src/out/Default/Electron Framework.framework"
|
||||
rm -rf src/out/Default/Electron*.app
|
||||
|
||||
cd src/electron
|
||||
# TODO(codebytere): remove this once we figure out why .git/packed-refs is initially missing
|
||||
git pack-refs
|
||||
cd ..
|
||||
|
||||
ulimit -n 10000
|
||||
sudo launchctl limit maxfiles 65536 200000
|
||||
NINJA_SUMMARIZE_BUILD=1 e build -j $NUMBER_OF_NINJA_PROCESSES
|
||||
cp out/Default/.ninja_log out/electron_ninja_log
|
||||
node electron/script/check-symlinks.js
|
||||
# Additional Targets: shell_browser_ui_unittests third_party/electron_node:headers third_party/electron_node:overlapped-checker electron:hunspell_dictionaries_zip
|
||||
- name: Build Electron dist.zip (mas)
|
||||
run: |
|
||||
cd src
|
||||
e build electron:electron_dist_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
if [ "$CHECK_DIST_MANIFEST" == "1" ]; then
|
||||
target_os=mac_mas
|
||||
target_cpu=arm64
|
||||
electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.$target_os.$target_cpu.manifest
|
||||
fi
|
||||
- name: Build Mksnapshot (mas)
|
||||
run: |
|
||||
cd src
|
||||
e build electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
|
||||
gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
|
||||
# Remove unused args from mksnapshot_args
|
||||
SEDOPTION="-i ''"
|
||||
sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args
|
||||
sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args
|
||||
sed $SEDOPTION '/The gn arg use_goma=true .*/d' out/Default/mksnapshot_args
|
||||
e build electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
|
||||
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
|
||||
- name: Build Chromedriver (mas)
|
||||
run: |
|
||||
cd src
|
||||
e build electron:electron_chromedriver -j $NUMBER_OF_NINJA_PROCESSES
|
||||
e build electron:electron_chromedriver_zip
|
||||
- name: Build Node Headers
|
||||
run: |
|
||||
cd src
|
||||
e build electron:node_headers
|
||||
- name: Generate & Zip Symbols (mas)
|
||||
run: |
|
||||
if [ ${{ inputs.GENERATE_SYMBOLS }} == "true" ]; then
|
||||
e build electron:electron_symbols
|
||||
fi
|
||||
cd src
|
||||
export BUILD_PATH="$(pwd)/out/Default"
|
||||
e build electron:licenses
|
||||
e build electron:electron_version_file
|
||||
if [ ${{ inputs.IS_RELEASE }} == "true" ]; then
|
||||
DELETE_DSYMS_AFTER_ZIP=1 electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
else
|
||||
electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
fi
|
||||
- name: Move all Generated Artifacts to Upload Folder (mas)
|
||||
run: ./src/electron/script/actions/move-artifacts.sh
|
||||
- name: Upload Generated Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: generated_artifacts_mas
|
||||
path: ./generated_artifacts_mas
|
||||
- name: Persist Build Artifacts
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
src/out/Default/gen/node_headers
|
||||
src/out/Default/overlapped-checker
|
||||
src/electron
|
||||
src/third_party/electron_node
|
||||
src/third_party/nan
|
||||
src/cross-arch-snapshots
|
||||
src/third_party/llvm-build
|
||||
src/build/linux
|
||||
src/buildtools/mac
|
||||
src/buildtools/third_party/libc++
|
||||
src/buildtools/third_party/libc++abi
|
||||
src/third_party/libc++
|
||||
src/third_party/libc++abi
|
||||
src/out/Default/obj/buildtools/third_party
|
||||
src/v8/tools/builtins-pgo
|
||||
key: ${{ runner.os }}-build-artifacts-mas-${{ github.sha }}
|
||||
test:
|
||||
if: ${{ inputs.IS_RELEASE == false }}
|
||||
runs-on: macos-13-xlarge
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-type: [darwin, mas]
|
||||
env:
|
||||
BUILD_TYPE: ${{ matrix.build-type }}
|
||||
steps:
|
||||
- name: Load Build Tools
|
||||
run: |
|
||||
npm i -g @electron/build-tools
|
||||
e init --root=$(pwd) --out=Default ${{ inputs.GN_BUILD_TYPE }}
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src/electron
|
||||
- name: Setup Node.js/npm
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20.11.x
|
||||
cache: yarn
|
||||
cache-dependency-path: src/electron/yarn.lock
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install
|
||||
- name: Get Depot Tools
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
# remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems
|
||||
sed -i '' '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
else
|
||||
sed -i '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja
|
||||
# Remove swift-format dep from cipd on macOS until we send a patch upstream.
|
||||
cd depot_tools
|
||||
git apply --3way ../src/electron/.github/workflows/config/gclient.diff
|
||||
fi
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
touch .disable_auto_update
|
||||
- name: Add Depot Tools to PATH
|
||||
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
||||
- name: Download Generated Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: generated_artifacts_${{ matrix.build-type }}
|
||||
path: ./generated_artifacts_${{ matrix.build-type }}
|
||||
- name: Restore Persisted Build Artifacts
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
src/out/Default/gen/node_headers
|
||||
src/out/Default/overlapped-checker
|
||||
src/electron
|
||||
src/third_party/electron_node
|
||||
src/third_party/nan
|
||||
src/cross-arch-snapshots
|
||||
src/third_party/llvm-build
|
||||
src/build/linux
|
||||
src/buildtools/mac
|
||||
src/buildtools/third_party/libc++
|
||||
src/buildtools/third_party/libc++abi
|
||||
src/third_party/libc++
|
||||
src/third_party/libc++abi
|
||||
src/out/Default/obj/buildtools/third_party
|
||||
src/v8/tools/builtins-pgo
|
||||
key: ${{ runner.os }}-build-artifacts-${{ matrix.build-type }}-${{ github.sha }}
|
||||
- name: Restore Generated Artifacts
|
||||
run: ./src/electron/script/actions/restore-artifacts.sh
|
||||
- name: Unzip Dist, Mksnapshot & Chromedriver
|
||||
run: |
|
||||
cd src/out/Default
|
||||
unzip -:o dist.zip
|
||||
unzip -:o chromedriver.zip
|
||||
unzip -:o mksnapshot.zip
|
||||
# - name: Import & Trust Self-Signed Codesigning Cert on MacOS
|
||||
# run: |
|
||||
# sudo security authorizationdb write com.apple.trust-settings.admin allow
|
||||
# cd src/electron
|
||||
# ./script/codesign/generate-identity.sh
|
||||
- name: Run Electron Tests
|
||||
env:
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
ELECTRON_SKIP_NATIVE_MODULE_TESTS: true
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn test --runners=main --trace-uncaught --enable-logging
|
||||
30
.github/workflows/macos-publish.yml
vendored
Normal file
30
.github/workflows/macos-publish.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Publish MacOS
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
macos-publish-arch-limit:
|
||||
description: 'The allowed arches for macos-publish'
|
||||
type: choice
|
||||
default: all
|
||||
options: ["all", "osx-x64", "osx-arm64", "mas-x64", "mas-arm64"]
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
required: false
|
||||
default: '1'
|
||||
type: string
|
||||
run-macos-publish:
|
||||
description: 'Run the publish jobs vs just the build jobs'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
uses: electron/electron/.github/workflows/macos-build.yml@gh-macos-publish
|
||||
with:
|
||||
IS_RELEASE: true
|
||||
GN_CONFIG: //electron/build/args/release.gn
|
||||
GN_BUILD_TYPE: release
|
||||
GENERATE_SYMBOLS: true
|
||||
UPLOAD_TO_STORAGE: ${{ inputs.upload-to-storage }}
|
||||
secrets: inherit
|
||||
@@ -1,3 +1,17 @@
|
||||
{
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json"
|
||||
"extends": "@electron/lint-roller/configs/markdownlint.json",
|
||||
"no-angle-brackets": true,
|
||||
"no-inline-html": {
|
||||
"allowed_elements": [
|
||||
"br",
|
||||
"details",
|
||||
"img",
|
||||
"li",
|
||||
"summary",
|
||||
"ul",
|
||||
"unknown",
|
||||
"Tabs",
|
||||
"TabItem",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
1
BUILD.gn
1
BUILD.gn
@@ -475,6 +475,7 @@ source_set("electron_lib") {
|
||||
"//net:extras",
|
||||
"//net:net_resources",
|
||||
"//printing/buildflags",
|
||||
"//services/device/public/cpp/bluetooth:bluetooth",
|
||||
"//services/device/public/cpp/geolocation",
|
||||
"//services/device/public/cpp/hid",
|
||||
"//services/device/public/mojom",
|
||||
|
||||
@@ -17,6 +17,8 @@ following properties:
|
||||
method.
|
||||
* `url` string (optional) - The request URL. Must be provided in the absolute
|
||||
form with the protocol scheme specified as http or https.
|
||||
* `headers` Record\<string, string | string[]\> (optional) - Headers to be sent
|
||||
with the request.
|
||||
* `session` Session (optional) - The [`Session`](session.md) instance with
|
||||
which the request is associated.
|
||||
* `partition` string (optional) - The name of the [`partition`](session.md)
|
||||
|
||||
@@ -814,6 +814,8 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
* `keyboardLock` - Request capture of keypresses for any or all of the keys on the physical keyboard via the [Keyboard Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/lock). These requests always appear to originate from the main frame.
|
||||
* `openExternal` - Request to open links in external applications.
|
||||
* `speaker-selection` - Request to enumerate and select audio output devices via the [speaker-selection permissions policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/speaker-selection).
|
||||
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `window-management` - Request access to enumerate screens using the [`getScreenDetails`](https://developer.chrome.com/en/articles/multi-screen-window-placement/) API.
|
||||
* `unknown` - An unrecognized permission request.
|
||||
* `callback` Function
|
||||
@@ -862,6 +864,8 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
|
||||
* `openExternal` - Open links in external applications.
|
||||
* `pointerLock` - Directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
|
||||
* `serial` - Read from and write to serial devices with the [Web Serial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API).
|
||||
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
|
||||
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
|
||||
* `requestingOrigin` string - The origin URL of the permission check
|
||||
* `details` Object - Some properties are only available on certain permission types.
|
||||
@@ -1424,23 +1428,34 @@ is emitted.
|
||||
Returns `string | null` - The absolute file system path where data for this
|
||||
session is persisted on disk. For in memory sessions this returns `null`.
|
||||
|
||||
#### `ses.clearData()`
|
||||
#### `ses.clearData([options])`
|
||||
|
||||
* `options` Object (optional)
|
||||
* `dataTypes` String[] (optional) - The types of data to clear. By default, this will clear all types of data.
|
||||
* `backgroundFetch` - Background Fetch
|
||||
* `cache` - Cache
|
||||
* `cookies` - Cookies
|
||||
* `downloads` - Downloads
|
||||
* `fileSystems` - File Systems
|
||||
* `indexedDB` - IndexedDB
|
||||
* `localStorage` - Local Storage
|
||||
* `serviceWorkers` - Service Workers
|
||||
* `webSQL` - WebSQL
|
||||
* `origins` String[] (optional) - Clear data for only these origins. Cannot be used with `excludeOrigins`.
|
||||
* `excludeOrigins` String[] (optional) - Clear data for all origins except these ones. Cannot be used with `origins`.
|
||||
* `avoidClosingConnections` boolean (optional) - Skips deleting cookies that would close current network connections. (Default: `false`)
|
||||
* `originMatchingMode` String (optional) - The behavior for matching data to origins.
|
||||
* `third-parties-included` (default) - Storage is matched on origin in first-party contexts and top-level-site in third-party contexts.
|
||||
* `origin-in-all-contexts` - Storage is matched on origin only in all contexts.
|
||||
|
||||
Returns `Promise<void>` - resolves when all data has been cleared.
|
||||
|
||||
This method clears many different types of data, inlcuding:
|
||||
|
||||
* Cache
|
||||
* Cookies
|
||||
* Downloads
|
||||
* IndexedDB
|
||||
* Local Storage
|
||||
* Service Workers
|
||||
* And more...
|
||||
Clears various different types of data.
|
||||
|
||||
This method clears more types of data and is more thourough than the
|
||||
`clearStorageData` method, however it is currently less configurable than that
|
||||
method.
|
||||
`clearStorageData` method.
|
||||
|
||||
**Note:** Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`.
|
||||
|
||||
For more information, refer to Chromium's [`BrowsingDataRemover` interface](https://source.chromium.org/chromium/chromium/src/+/main:content/public/browser/browsing_data_remover.h).
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRende
|
||||
<details><summary>Typed import aliases</summary>
|
||||
|
||||
For better type checking when writing TypeScript code, you can choose to import
|
||||
main process modules from <code>electron/main</code>.
|
||||
main process modules from `electron/main`.
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow } = require('electron/main')
|
||||
|
||||
@@ -209,6 +209,22 @@ type ExtraURLLoaderOptions = {
|
||||
headers: Record<string, { name: string, value: string | string[] }>;
|
||||
allowNonHttpProtocols: boolean;
|
||||
}
|
||||
|
||||
function validateHeader (name: any, value: any): void {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & ExtraURLLoaderOptions {
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
|
||||
@@ -275,12 +291,7 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||
};
|
||||
const headers: Record<string, string | string[]> = options.headers || {};
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
validateHeader(name, value);
|
||||
const key = name.toLowerCase();
|
||||
urlLoaderOptions.headers[key] = { name, value };
|
||||
}
|
||||
@@ -351,21 +362,10 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
}
|
||||
|
||||
setHeader (name: string, value: string) {
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('`name` should be a string in setHeader(name, value)');
|
||||
}
|
||||
if (value == null) {
|
||||
throw new Error('`value` required in setHeader("' + name + '", value)');
|
||||
}
|
||||
if (this._started || this._firstWrite) {
|
||||
throw new Error('Can\'t set headers after they are sent');
|
||||
}
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw new Error(`Invalid header name: '${name}'`);
|
||||
}
|
||||
if (!isValidHeaderValue(value.toString())) {
|
||||
throw new Error(`Invalid value for header '${name}': '${value}'`);
|
||||
}
|
||||
validateHeader(name, value);
|
||||
|
||||
const key = name.toLowerCase();
|
||||
this._urlLoaderOptions.headers[key] = { name, value };
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@electron/docs-parser": "^1.2.0",
|
||||
"@electron/fiddle-core": "^1.0.4",
|
||||
"@electron/github-app-auth": "^2.0.0",
|
||||
"@electron/lint-roller": "^1.9.0",
|
||||
"@electron/lint-roller": "^1.12.1",
|
||||
"@electron/typescript-definitions": "^8.15.2",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@primer/octicons": "^10.0.0",
|
||||
@@ -49,7 +49,7 @@
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-unicorn": "^46.0.1",
|
||||
"events": "^3.2.0",
|
||||
"express": "^4.16.4",
|
||||
"express": "^4.19.2",
|
||||
"folder-hash": "^2.1.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"got": "^11.8.5",
|
||||
|
||||
@@ -33,7 +33,6 @@ fix_assert_module_in_the_renderer_process.patch
|
||||
fix_add_trusted_space_and_trusted_lo_space_to_the_v8_heap.patch
|
||||
win_process_avoid_assert_after_spawning_store_app_4152.patch
|
||||
chore_remove_use_of_deprecated_kmaxlength.patch
|
||||
fix_missing_include_for_node_extern.patch
|
||||
feat_optionally_prevent_calling_v8_enablewebassemblytraphandler.patch
|
||||
build_only_create_cppgc_heap_on_non-32_bit_platforms.patch
|
||||
fix_-wshadow_error_in_uvwasi_c.patch
|
||||
|
||||
@@ -87,10 +87,18 @@ index 895ff3a5948add3513700ecc2f32fce4c2fbe4eb..3182a5e4aad2ba0be2b6769edb696b81
|
||||
|
||||
MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
|
||||
diff --git a/src/module_wrap.h b/src/module_wrap.h
|
||||
index e17048357feca2419087621ed280de30882a90bc..061117dc3182d63e35d7e99ffd95801f96356322 100644
|
||||
index e17048357feca2419087621ed280de30882a90bc..63682be31ce00a3bf7b9be909cac4b7f9ec02a8e 100644
|
||||
--- a/src/module_wrap.h
|
||||
+++ b/src/module_wrap.h
|
||||
@@ -31,7 +31,14 @@ enum HostDefinedOptions : int {
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "base_object.h"
|
||||
+#include "node.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
@@ -31,7 +32,14 @@ enum HostDefinedOptions : int {
|
||||
kLength = 9,
|
||||
};
|
||||
|
||||
@@ -106,7 +114,7 @@ index e17048357feca2419087621ed280de30882a90bc..061117dc3182d63e35d7e99ffd95801f
|
||||
public:
|
||||
enum InternalFields {
|
||||
kModuleSlot = BaseObject::kInternalFieldCount,
|
||||
@@ -68,6 +75,8 @@ class ModuleWrap : public BaseObject {
|
||||
@@ -68,6 +76,8 @@ class ModuleWrap : public BaseObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,7 +123,7 @@ index e17048357feca2419087621ed280de30882a90bc..061117dc3182d63e35d7e99ffd95801f
|
||||
private:
|
||||
ModuleWrap(Realm* realm,
|
||||
v8::Local<v8::Object> object,
|
||||
@@ -102,7 +111,6 @@ class ModuleWrap : public BaseObject {
|
||||
@@ -102,7 +112,6 @@ class ModuleWrap : public BaseObject {
|
||||
v8::Local<v8::String> specifier,
|
||||
v8::Local<v8::FixedArray> import_attributes,
|
||||
v8::Local<v8::Module> referrer);
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Wed, 15 Nov 2023 12:25:39 +0100
|
||||
Subject: fix: missing include for NODE_EXTERN
|
||||
|
||||
At some point it seems that node.h was removed from the include chain,
|
||||
causing the following error:
|
||||
|
||||
../../third_party/electron_node/src/module_wrap.h:33:1: error: unknown type name 'NODE_EXTERN'
|
||||
33 | NODE_EXTERN v8::MaybeLocal<v8::Promise> ImportModuleDynamically(
|
||||
| ^
|
||||
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/src/module_wrap.h b/src/module_wrap.h
|
||||
index 061117dc3182d63e35d7e99ffd95801f96356322..63682be31ce00a3bf7b9be909cac4b7f9ec02a8e 100644
|
||||
--- a/src/module_wrap.h
|
||||
+++ b/src/module_wrap.h
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "base_object.h"
|
||||
+#include "node.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
26
script/actions/evm.mas.json
Normal file
26
script/actions/evm.mas.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"root": "/Users/runner/work/electron/electron/",
|
||||
"remotes": {
|
||||
"electron": {
|
||||
"origin": "https://github.com/electron/electron.git"
|
||||
}
|
||||
},
|
||||
"gen": {
|
||||
"args": [
|
||||
"import(\"//electron/build/args/testing.gn\")",
|
||||
"use_remoteexec = true",
|
||||
"target_cpu = \"arm64\"",
|
||||
"is_mas_build = true"
|
||||
],
|
||||
"out": "Default"
|
||||
},
|
||||
"env": {
|
||||
"CHROMIUM_BUILDTOOLS_PATH": "/Users/runner/work/electron/electron/src/buildtools",
|
||||
"GIT_CACHE_PATH": "/Users/runner/work/electron/electron/.git-cache"
|
||||
},
|
||||
"$schema": "file:///home/builduser/.electron_build_tools/evm-config.schema.json",
|
||||
"configValidationLevel": "strict",
|
||||
"reclient": "remote_exec",
|
||||
"goma": "none",
|
||||
"preserveXcode": 5
|
||||
}
|
||||
40
script/actions/move-artifacts.sh
Executable file
40
script/actions/move-artifacts.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
if [ -z "$MAS_BUILD" ]; then
|
||||
BUILD_TYPE="darwin"
|
||||
else
|
||||
BUILD_TYPE="mas"
|
||||
fi
|
||||
|
||||
rm -rf generated_artifacts_${BUILD_TYPE}
|
||||
mkdir generated_artifacts_${BUILD_TYPE}
|
||||
|
||||
mv_if_exist() {
|
||||
if [ -f "$1" ] || [ -d "$1" ]; then
|
||||
echo Storing $1
|
||||
mv $1 generated_artifacts_${BUILD_TYPE}
|
||||
else
|
||||
echo Skipping $1 - It is not present on disk
|
||||
fi
|
||||
}
|
||||
cp_if_exist() {
|
||||
if [ -f "$1" ] || [ -d "$1" ]; then
|
||||
echo Storing $1
|
||||
cp $1 generated_artifacts_${BUILD_TYPE}
|
||||
else
|
||||
echo Skipping $1 - It is not present on disk
|
||||
fi
|
||||
}
|
||||
|
||||
mv_if_exist src/out/Default/dist.zip
|
||||
mv_if_exist src/out/Default/gen/node_headers.tar.gz
|
||||
mv_if_exist src/out/Default/symbols.zip
|
||||
mv_if_exist src/out/Default/mksnapshot.zip
|
||||
mv_if_exist src/out/Default/chromedriver.zip
|
||||
mv_if_exist src/out/ffmpeg/ffmpeg.zip
|
||||
mv_if_exist src/out/Default/hunspell_dictionaries.zip
|
||||
mv_if_exist src/cross-arch-snapshots
|
||||
cp_if_exist src/out/electron_ninja_log
|
||||
cp_if_exist src/out/Default/.ninja_log
|
||||
22
script/actions/restore-artifacts.sh
Executable file
22
script/actions/restore-artifacts.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
mv_if_exist() {
|
||||
if [ -f "generated_artifacts_${BUILD_TYPE}/$1" ] || [ -d "generated_artifacts_${BUILD_TYPE}/$1" ]; then
|
||||
echo Restoring $1 to $2
|
||||
mkdir -p $2
|
||||
mv generated_artifacts_${BUILD_TYPE}/$1 $2
|
||||
else
|
||||
echo Skipping $1 - It is not present on disk
|
||||
fi
|
||||
}
|
||||
|
||||
mv_if_exist dist.zip src/out/Default
|
||||
mv_if_exist node_headers.tar.gz src/out/Default/gen
|
||||
mv_if_exist symbols.zip src/out/Default
|
||||
mv_if_exist mksnapshot.zip src/out/Default
|
||||
mv_if_exist chromedriver.zip src/out/Default
|
||||
mv_if_exist ffmpeg.zip src/out/ffmpeg
|
||||
mv_if_exist hunspell_dictionaries.zip src/out/Default
|
||||
mv_if_exist cross-arch-snapshots src
|
||||
@@ -2,6 +2,8 @@
|
||||
const { BlobServiceClient } = require('@azure/storage-blob');
|
||||
const path = require('node:path');
|
||||
|
||||
// TODO(vertedinde): This variable is a test variable in GHA, sending test artifacts to a test account
|
||||
// Change this to the real electron artifacts storage account when ready
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(process.env.ELECTRON_ARTIFACTS_BLOB_STORAGE);
|
||||
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
|
||||
@@ -3,9 +3,18 @@ if (!process.env.CI) require('dotenv-safe').load();
|
||||
const assert = require('node:assert');
|
||||
const got = require('got');
|
||||
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.ELECTRON_GITHUB_TOKEN
|
||||
});
|
||||
|
||||
const BUILD_APPVEYOR_URL = 'https://ci.appveyor.com/api/builds';
|
||||
const CIRCLECI_PIPELINE_URL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline';
|
||||
const GH_ACTIONS_PIPELINE_URL = 'https://github.com/electron/electron/actions';
|
||||
const GH_ACTIONS_API_URL = '/repos/electron/electron/actions';
|
||||
|
||||
const CIRCLECI_WAIT_TIME = process.env.CIRCLECI_WAIT_TIME || 30000;
|
||||
const GH_ACTIONS_WAIT_TIME = process.env.GH_ACTIONS_WAIT_TIME || 30000;
|
||||
|
||||
const appVeyorJobs = {
|
||||
'electron-x64': 'electron-x64-release',
|
||||
@@ -23,6 +32,14 @@ const circleCIPublishIndividualArches = {
|
||||
'linux-publish': ['arm', 'arm64', 'x64']
|
||||
};
|
||||
|
||||
const ghActionsPublishWorkflows = [
|
||||
'macos-publish'
|
||||
];
|
||||
|
||||
const ghActionsPublishIndividualArches = {
|
||||
'macos-publish': ['osx-x64', 'mas-x64', 'osx-arm64', 'mas-arm64']
|
||||
};
|
||||
|
||||
let jobRequestedCount = 0;
|
||||
|
||||
async function makeRequest ({ auth, username, password, url, headers, body, method }) {
|
||||
@@ -53,6 +70,65 @@ async function makeRequest ({ auth, username, password, url, headers, body, meth
|
||||
return JSON.parse(response.body);
|
||||
}
|
||||
|
||||
async function githubActionsCall (targetBranch, workflowName, options) {
|
||||
console.log(`Triggering GitHub Actions to run build job: ${workflowName} on branch: ${targetBranch} with release flag.`);
|
||||
const buildRequest = {
|
||||
branch: targetBranch,
|
||||
parameters: {}
|
||||
};
|
||||
if (options.ghRelease) {
|
||||
buildRequest.parameters['upload-to-storage'] = '0';
|
||||
} else {
|
||||
buildRequest.parameters['upload-to-storage'] = '1';
|
||||
}
|
||||
buildRequest.parameters[`run-${workflowName}`] = true;
|
||||
if (options.arch) {
|
||||
const validArches = ghActionsPublishIndividualArches[workflowName];
|
||||
assert(validArches.includes(options.arch), `Unknown GitHub Actions architecture "${options.arch}". Valid values are ${JSON.stringify(validArches)}`);
|
||||
buildRequest.parameters['macos-publish-arch-limit'] = options.arch;
|
||||
}
|
||||
|
||||
jobRequestedCount++;
|
||||
try {
|
||||
const commits = await octokit.repos.listCommits({
|
||||
owner: 'electron',
|
||||
repo: 'electron',
|
||||
sha: targetBranch,
|
||||
per_page: 5
|
||||
});
|
||||
if (!commits.data.length) {
|
||||
console.error('Could not fetch most recent commits for GitHub Actions, returning early');
|
||||
}
|
||||
|
||||
await octokit.request(`POST ${GH_ACTIONS_API_URL}/workflows/${workflowName}.yml/dispatches`, {
|
||||
ref: buildRequest.branch,
|
||||
inputs: {
|
||||
...buildRequest.parameters
|
||||
},
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
|
||||
const runNumber = await getGitHubActionsRun(workflowName, commits.data[0].sha);
|
||||
if (runNumber === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`GitHub Actions release build pipeline ${runNumber} for ${workflowName} triggered.`);
|
||||
const runUrl = `${GH_ACTIONS_PIPELINE_URL}/runs/${runNumber}`;
|
||||
|
||||
if (options.runningPublishWorkflows) {
|
||||
console.log(`GitHub Actions release workflow request for ${workflowName} successful. Check ${runUrl} for status.`);
|
||||
} else {
|
||||
console.log(`GitHub Actions release build workflow running at ${GH_ACTIONS_PIPELINE_URL}/runs/${runNumber} for ${workflowName}.`);
|
||||
console.log(`GitHub Actions release build request for ${workflowName} successful. Check ${runUrl} for status.`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error calling GitHub Actions: ', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function circleCIcall (targetBranch, workflowName, options) {
|
||||
console.log(`Triggering CircleCI to run build job: ${workflowName} on branch: ${targetBranch} with release flag.`);
|
||||
const buildRequest = {
|
||||
@@ -167,6 +243,59 @@ async function getCircleCIJobNumber (workflowId) {
|
||||
return jobNumber;
|
||||
}
|
||||
|
||||
async function getGitHubActionsRun (workflowId, headCommit) {
|
||||
let runNumber = 0;
|
||||
let actionRun;
|
||||
while (runNumber === 0) {
|
||||
const actionsRuns = await octokit.request(`GET ${GH_ACTIONS_API_URL}/workflows/${workflowId}.yml/runs`, {
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
if (!actionsRuns.data.workflow_runs.length) {
|
||||
console.log(`No current workflow_runs found for ${workflowId}, response was: ${actionsRuns.data.workflow_runs}`);
|
||||
runNumber = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const run of actionsRuns.data.workflow_runs) {
|
||||
if (run.head_sha === headCommit) {
|
||||
console.log(`GitHub Actions run ${run.html_url} found for ${headCommit}, waiting on status.`);
|
||||
actionRun = run;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (actionRun) {
|
||||
switch (actionRun.status) {
|
||||
case 'in_progress':
|
||||
case 'pending':
|
||||
case 'queued':
|
||||
case 'requested':
|
||||
case 'waiting': {
|
||||
if (actionRun.id && !isNaN(actionRun.id)) {
|
||||
console.log(`GitHub Actions run ${actionRun.status} for ${actionRun.html_url}.`);
|
||||
runNumber = actionRun.id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'action_required':
|
||||
case 'cancelled':
|
||||
case 'failure':
|
||||
case 'skipped':
|
||||
case 'timed_out':
|
||||
case 'failed': {
|
||||
console.log(`Error workflow run returned a status of ${actionRun.status} for ${actionRun.html_url}`);
|
||||
runNumber = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, GH_ACTIONS_WAIT_TIME));
|
||||
}
|
||||
}
|
||||
return runNumber;
|
||||
}
|
||||
|
||||
async function circleCIRequest (url, method, requestBody) {
|
||||
const requestOpts = {
|
||||
username: process.env.CIRCLE_TOKEN,
|
||||
@@ -194,18 +323,6 @@ async function circleCIRequest (url, method, requestBody) {
|
||||
});
|
||||
}
|
||||
|
||||
function buildAppVeyor (targetBranch, options) {
|
||||
const validJobs = Object.keys(appVeyorJobs);
|
||||
if (options.job) {
|
||||
assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`);
|
||||
callAppVeyor(targetBranch, options.job, options);
|
||||
} else {
|
||||
for (const job of validJobs) {
|
||||
callAppVeyor(targetBranch, job, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function callAppVeyor (targetBranch, job, options) {
|
||||
console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`);
|
||||
const environmentVariables = {
|
||||
@@ -252,6 +369,18 @@ async function callAppVeyor (targetBranch, job, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildAppVeyor (targetBranch, options) {
|
||||
const validJobs = Object.keys(appVeyorJobs);
|
||||
if (options.job) {
|
||||
assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`);
|
||||
callAppVeyor(targetBranch, options.job, options);
|
||||
} else {
|
||||
for (const job of validJobs) {
|
||||
callAppVeyor(targetBranch, job, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildCircleCI (targetBranch, options) {
|
||||
if (options.job) {
|
||||
assert(circleCIPublishWorkflows.includes(options.job), `Unknown CircleCI workflow name: ${options.job}. Valid values are: ${circleCIPublishWorkflows}.`);
|
||||
@@ -265,6 +394,19 @@ function buildCircleCI (targetBranch, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildGHActions (targetBranch, options) {
|
||||
if (options.job) {
|
||||
assert(ghActionsPublishWorkflows.includes(options.job), `Unknown GitHub Actions workflow name: ${options.job}. Valid values are: ${ghActionsPublishWorkflows}.`);
|
||||
githubActionsCall(targetBranch, options.job, options);
|
||||
} else {
|
||||
assert(!options.arch, 'Cannot provide a single architecture while building all workflows, please specify a single workflow via --workflow');
|
||||
options.runningPublishWorkflows = true;
|
||||
for (const job of ghActionsPublishWorkflows) {
|
||||
githubActionsCall(targetBranch, job, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runRelease (targetBranch, options) {
|
||||
if (options.ci) {
|
||||
switch (options.ci) {
|
||||
@@ -272,6 +414,10 @@ function runRelease (targetBranch, options) {
|
||||
buildCircleCI(targetBranch, options);
|
||||
break;
|
||||
}
|
||||
case 'GitHubActions': {
|
||||
buildGHActions(targetBranch, options);
|
||||
break;
|
||||
}
|
||||
case 'AppVeyor': {
|
||||
buildAppVeyor(targetBranch, options);
|
||||
break;
|
||||
@@ -284,6 +430,8 @@ function runRelease (targetBranch, options) {
|
||||
} else {
|
||||
buildCircleCI(targetBranch, options);
|
||||
buildAppVeyor(targetBranch, options);
|
||||
// TODO(vertedinde): Enable GH Actions in defaults when ready
|
||||
// buildGHActions(targetBranch, options);
|
||||
}
|
||||
console.log(`${jobRequestedCount} jobs were requested.`);
|
||||
}
|
||||
@@ -297,7 +445,7 @@ if (require.main === module) {
|
||||
const targetBranch = args._[0];
|
||||
if (args._.length < 1) {
|
||||
console.log(`Trigger CI to build release builds of electron.
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--arch=INDIVIDUAL_ARCH] [--ci=CircleCI|AppVeyor]
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--arch=INDIVIDUAL_ARCH] [--ci=CircleCI|AppVeyor|GitHubActions]
|
||||
[--ghRelease] [--circleBuildNum=xxx] [--appveyorJobId=xxx] [--commit=sha] TARGET_BRANCH
|
||||
`);
|
||||
process.exit(0);
|
||||
|
||||
@@ -17,6 +17,7 @@ const filePath = process.argv[2];
|
||||
const fileName = process.argv[3];
|
||||
const releaseId = parseInt(process.argv[4], 10);
|
||||
const releaseVersion = process.argv[5];
|
||||
const isGHActions = process.argv[6];
|
||||
|
||||
if (isNaN(releaseId)) {
|
||||
throw new TypeError('Provided release ID was not a valid integer');
|
||||
@@ -44,7 +45,9 @@ const getHeaders = (filePath: string, fileName: string) => {
|
||||
};
|
||||
|
||||
const targetRepo = releaseVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron';
|
||||
const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}`;
|
||||
const uploadDefaultUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}`;
|
||||
const uploadGHActionsTestUrl = `https://uploads.github.com/repos/electron/test-releases/releases/${releaseId}/assets{?name,label}`;
|
||||
const uploadUrl = isGHActions ? uploadGHActionsTestUrl : uploadDefaultUrl;
|
||||
let retry = 0;
|
||||
|
||||
function uploadToGitHub () {
|
||||
|
||||
@@ -181,6 +181,9 @@ def parse_args():
|
||||
parser.add_argument('--verbose',
|
||||
action='store_true',
|
||||
help='Mooooorreee logs')
|
||||
parser.add_argument('-g', '--gh_actions',
|
||||
help='Sets needed variables for GitHub Actions test runs',
|
||||
action='store_true')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -340,6 +343,10 @@ def upload_electron(release, file_path, args):
|
||||
except NonZipFileError:
|
||||
pass
|
||||
|
||||
is_gh_actions = False
|
||||
if args.gh_actions:
|
||||
is_gh_actions = True
|
||||
|
||||
# if upload_to_storage is set, skip github upload.
|
||||
# todo (vertedinde): migrate this variable to upload_to_storage
|
||||
if args.upload_to_storage:
|
||||
@@ -349,18 +356,18 @@ def upload_electron(release, file_path, args):
|
||||
return
|
||||
|
||||
# Upload the file.
|
||||
upload_io_to_github(release, filename, file_path, args.version)
|
||||
upload_io_to_github(release, filename, file_path, args.version, is_gh_actions)
|
||||
|
||||
# Upload the checksum file.
|
||||
upload_sha256_checksum(args.version, file_path)
|
||||
|
||||
|
||||
def upload_io_to_github(release, filename, filepath, version):
|
||||
def upload_io_to_github(release, filename, filepath, version, is_gh_actions):
|
||||
print(f'Uploading {filename} to GitHub')
|
||||
script_path = os.path.join(
|
||||
ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-to-github.ts')
|
||||
with subprocess.Popen([TS_NODE, script_path, filepath,
|
||||
filename, str(release['id']), version],
|
||||
filename, str(release['id']), version, is_gh_actions],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT) as upload_process:
|
||||
if is_verbose_mode():
|
||||
|
||||
@@ -77,6 +77,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||
WebContentsView::Create(isolate, web_preferences);
|
||||
DCHECK(web_contents_view.get());
|
||||
window_->AddDraggableRegionProvider(web_contents_view.get());
|
||||
web_contents_view_.Reset(isolate, web_contents_view.ToV8());
|
||||
|
||||
// Save a reference of the WebContents.
|
||||
gin::Handle<WebContents> web_contents =
|
||||
@@ -92,7 +93,14 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||
InitWithArgs(args);
|
||||
|
||||
// Install the content view after BaseWindow's JS code is initialized.
|
||||
SetContentView(gin::CreateHandle<View>(isolate, web_contents_view.get()));
|
||||
// The WebContentsView is added a sibling of BaseWindow's contentView (before
|
||||
// it in the paint order) so that any views added to BrowserWindow's
|
||||
// contentView will be painted on top of the BrowserWindow's WebContentsView.
|
||||
// See https://github.com/electron/electron/pull/41256.
|
||||
// Note that |GetContentsView|, confusingly, does not refer to the same thing
|
||||
// as |BaseWindow::GetContentView|.
|
||||
window()->GetContentsView()->AddChildViewAt(web_contents_view->view(), 0);
|
||||
window()->GetContentsView()->DeprecatedLayoutImmediately();
|
||||
|
||||
// Init window after everything has been setup.
|
||||
window()->InitFromOptions(options);
|
||||
|
||||
@@ -95,6 +95,7 @@ class BrowserWindow : public BaseWindow,
|
||||
base::CancelableRepeatingClosure window_unresponsive_closure_;
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
v8::Global<v8::Value> web_contents_view_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
|
||||
base::WeakPtrFactory<BrowserWindow> weak_factory_{this};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/fixed_flat_map.h"
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
@@ -33,12 +34,14 @@
|
||||
#include "content/browser/code_cache/generated_code_cache_context.h" // nogncheck
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/browsing_data_filter_builder.h"
|
||||
#include "content/public/browser/browsing_data_remover.h"
|
||||
#include "content/public/browser/download_item_utils.h"
|
||||
#include "content/public/browser/download_manager_delegate.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "gin/converter.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
||||
#include "net/base/completion_repeating_callback.h"
|
||||
@@ -86,6 +89,7 @@
|
||||
#include "third_party/blink/public/common/storage_key/storage_key.h"
|
||||
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
@@ -106,6 +110,8 @@
|
||||
#endif
|
||||
|
||||
using content::BrowserThread;
|
||||
using content::BrowsingDataFilterBuilder;
|
||||
using content::BrowsingDataRemover;
|
||||
using content::StoragePartition;
|
||||
|
||||
namespace {
|
||||
@@ -117,25 +123,23 @@ struct ClearStorageDataOptions {
|
||||
};
|
||||
|
||||
uint32_t GetStorageMask(const std::vector<std::string>& storage_types) {
|
||||
static constexpr auto Lookup =
|
||||
base::MakeFixedFlatMap<std::string_view, uint32_t>(
|
||||
{{"cookies", StoragePartition::REMOVE_DATA_MASK_COOKIES},
|
||||
{"filesystem", StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS},
|
||||
{"indexdb", StoragePartition::REMOVE_DATA_MASK_INDEXEDDB},
|
||||
{"localstorage", StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE},
|
||||
{"shadercache", StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE},
|
||||
{"websql", StoragePartition::REMOVE_DATA_MASK_WEBSQL},
|
||||
{"serviceworkers",
|
||||
StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS},
|
||||
{"cachestorage", StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE}});
|
||||
|
||||
uint32_t storage_mask = 0;
|
||||
for (const auto& it : storage_types) {
|
||||
auto type = base::ToLowerASCII(it);
|
||||
if (type == "cookies")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
|
||||
else if (type == "filesystem")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
|
||||
else if (type == "indexdb")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
|
||||
else if (type == "localstorage")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
|
||||
else if (type == "shadercache")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
|
||||
else if (type == "websql")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
|
||||
else if (type == "serviceworkers")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
|
||||
else if (type == "cachestorage")
|
||||
storage_mask |= StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE;
|
||||
if (Lookup.contains(type))
|
||||
storage_mask |= Lookup.at(type);
|
||||
}
|
||||
return storage_mask;
|
||||
}
|
||||
@@ -152,38 +156,202 @@ uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
|
||||
return quota_mask;
|
||||
}
|
||||
|
||||
constexpr content::BrowsingDataRemover::DataType kClearDataTypeAll = ~0ULL;
|
||||
constexpr content::BrowsingDataRemover::OriginType kClearOriginTypeAll =
|
||||
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
|
||||
content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
|
||||
constexpr BrowsingDataRemover::DataType kClearDataTypeAll = ~0ULL;
|
||||
constexpr BrowsingDataRemover::OriginType kClearOriginTypeAll =
|
||||
BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
|
||||
BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
|
||||
|
||||
// Observes the BrowsingDataRemover that backs the `clearData` method and
|
||||
// fulfills that API's promise once it's done. This type manages its own
|
||||
// lifetime, deleting itself once it's done.
|
||||
class ClearDataObserver : public content::BrowsingDataRemover::Observer {
|
||||
public:
|
||||
ClearDataObserver(gin_helper::Promise<void> promise,
|
||||
content::BrowsingDataRemover* remover)
|
||||
: promise_(std::move(promise)) {
|
||||
observation_.Observe(remover);
|
||||
}
|
||||
constexpr auto kDataTypeLookup =
|
||||
base::MakeFixedFlatMap<std::string_view, BrowsingDataRemover::DataType>({
|
||||
{"backgroundFetch", BrowsingDataRemover::DATA_TYPE_BACKGROUND_FETCH},
|
||||
{"cache", BrowsingDataRemover::DATA_TYPE_CACHE |
|
||||
BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE},
|
||||
{"cookies", BrowsingDataRemover::DATA_TYPE_COOKIES},
|
||||
{"downloads", BrowsingDataRemover::DATA_TYPE_DOWNLOADS},
|
||||
{"fileSystems", BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS},
|
||||
{"indexedDB", BrowsingDataRemover::DATA_TYPE_INDEXED_DB},
|
||||
{"localStorage", BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE},
|
||||
{"serviceWorkers", BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS},
|
||||
{"webSQL", BrowsingDataRemover::DATA_TYPE_WEB_SQL},
|
||||
});
|
||||
|
||||
void OnBrowsingDataRemoverDone(
|
||||
content::BrowsingDataRemover::DataType failed_data_types) override {
|
||||
if (failed_data_types == 0ULL) {
|
||||
promise_.Resolve();
|
||||
} else {
|
||||
promise_.RejectWithErrorMessage(base::StringPrintf(
|
||||
"Failed to clear browsing data (%" PRIu64 ")", failed_data_types));
|
||||
BrowsingDataRemover::DataType GetDataTypeMask(
|
||||
const std::vector<std::string>& data_types) {
|
||||
BrowsingDataRemover::DataType mask = 0u;
|
||||
for (const auto& type : data_types) {
|
||||
if (kDataTypeLookup.contains(type)) {
|
||||
mask |= kDataTypeLookup.at(type);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetDataTypesFromMask(
|
||||
BrowsingDataRemover::DataType mask) {
|
||||
std::vector<std::string> results;
|
||||
for (const auto [type, flag] : kDataTypeLookup) {
|
||||
if (mask & flag) {
|
||||
results.emplace_back(type);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// Represents a task to clear browsing data for the `clearData` API method.
|
||||
//
|
||||
// This type manages its own lifetime, deleting itself once the task finishes
|
||||
// completely.
|
||||
class ClearDataTask {
|
||||
public:
|
||||
// Starts running a task. This function will return before the task is
|
||||
// finished, but will resolve or reject the |promise| when it finishes.
|
||||
static void Run(
|
||||
BrowsingDataRemover* remover,
|
||||
gin_helper::Promise<void> promise,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::vector<url::Origin> origins,
|
||||
BrowsingDataFilterBuilder::Mode filter_mode,
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode) {
|
||||
std::shared_ptr<ClearDataTask> task(new ClearDataTask(std::move(promise)));
|
||||
|
||||
// This method counts as an operation. This is important so we can call
|
||||
// `OnOperationFinished` at the end of this method as a fallback if all the
|
||||
// other operations finished while this method was still executing
|
||||
task->operations_running_ = 1;
|
||||
|
||||
// Cookies are scoped more broadly than other types of data, so if we are
|
||||
// filtering then we need to do it at the registrable domain level
|
||||
if (!origins.empty() &&
|
||||
data_type_mask & BrowsingDataRemover::DATA_TYPE_COOKIES) {
|
||||
data_type_mask &= ~BrowsingDataRemover::DATA_TYPE_COOKIES;
|
||||
|
||||
auto cookies_filter_builder =
|
||||
BrowsingDataFilterBuilder::Create(filter_mode);
|
||||
|
||||
for (const url::Origin& origin : origins) {
|
||||
std::string domain = GetDomainAndRegistry(
|
||||
origin,
|
||||
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
|
||||
if (domain.empty()) {
|
||||
domain = origin.host();
|
||||
}
|
||||
cookies_filter_builder->AddRegisterableDomain(domain);
|
||||
}
|
||||
|
||||
StartOperation(task, remover, BrowsingDataRemover::DATA_TYPE_COOKIES,
|
||||
std::move(cookies_filter_builder));
|
||||
}
|
||||
|
||||
// If cookies aren't the only data type and weren't handled above, then we
|
||||
// can start an operation that is scoped to origins
|
||||
if (data_type_mask) {
|
||||
auto filter_builder =
|
||||
BrowsingDataFilterBuilder::Create(filter_mode, origin_matching_mode);
|
||||
|
||||
for (auto const& origin : origins) {
|
||||
filter_builder->AddOrigin(origin);
|
||||
}
|
||||
|
||||
StartOperation(task, remover, data_type_mask, std::move(filter_builder));
|
||||
}
|
||||
|
||||
// This static method counts as an operation.
|
||||
task->OnOperationFinished(std::nullopt);
|
||||
}
|
||||
|
||||
private:
|
||||
// An individiual |content::BrowsingDataRemover::Remove...| operation as part
|
||||
// of a full |ClearDataTask|. This class manages its own lifetime, cleaning
|
||||
// itself up after the operation completes and notifies the task of the
|
||||
// result.
|
||||
class ClearDataOperation : public BrowsingDataRemover::Observer {
|
||||
public:
|
||||
static void Run(std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
auto* operation = new ClearDataOperation(task, remover);
|
||||
|
||||
remover->RemoveWithFilterAndReply(base::Time::Min(), base::Time::Max(),
|
||||
data_type_mask, kClearOriginTypeAll,
|
||||
std::move(filter_builder), operation);
|
||||
}
|
||||
|
||||
// BrowsingDataRemover::Observer:
|
||||
void OnBrowsingDataRemoverDone(
|
||||
BrowsingDataRemover::DataType failed_data_types) override {
|
||||
task_->OnOperationFinished(failed_data_types);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
ClearDataOperation(std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover)
|
||||
: task_(task) {
|
||||
observation_.Observe(remover);
|
||||
}
|
||||
|
||||
std::shared_ptr<ClearDataTask> task_;
|
||||
base::ScopedObservation<BrowsingDataRemover, BrowsingDataRemover::Observer>
|
||||
observation_{this};
|
||||
};
|
||||
|
||||
explicit ClearDataTask(gin_helper::Promise<void> promise)
|
||||
: promise_(std::move(promise)) {}
|
||||
|
||||
static void StartOperation(
|
||||
std::shared_ptr<ClearDataTask> task,
|
||||
BrowsingDataRemover* remover,
|
||||
BrowsingDataRemover::DataType data_type_mask,
|
||||
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
|
||||
// Track this operation
|
||||
task->operations_running_ += 1;
|
||||
|
||||
ClearDataOperation::Run(task, remover, data_type_mask,
|
||||
std::move(filter_builder));
|
||||
}
|
||||
|
||||
void OnOperationFinished(
|
||||
std::optional<BrowsingDataRemover::DataType> failed_data_types) {
|
||||
DCHECK_GT(operations_running_, 0);
|
||||
operations_running_ -= 1;
|
||||
|
||||
if (failed_data_types.has_value()) {
|
||||
failed_data_types_ |= failed_data_types.value();
|
||||
}
|
||||
|
||||
// If this is the last operation, then the task is finished
|
||||
if (operations_running_ == 0) {
|
||||
OnTaskFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void OnTaskFinished() {
|
||||
if (failed_data_types_ == 0ULL) {
|
||||
promise_.Resolve();
|
||||
} else {
|
||||
v8::Isolate* isolate = promise_.isolate();
|
||||
|
||||
v8::Local<v8::Value> failed_data_types_array =
|
||||
gin::ConvertToV8(isolate, GetDataTypesFromMask(failed_data_types_));
|
||||
|
||||
// Create a rich error object with extra detail about what data types
|
||||
// failed
|
||||
auto error = v8::Exception::Error(
|
||||
gin::StringToV8(isolate, "Failed to clear data"));
|
||||
error.As<v8::Object>()
|
||||
->Set(promise_.GetContext(),
|
||||
gin::StringToV8(isolate, "failedDataTypes"),
|
||||
failed_data_types_array)
|
||||
.Check();
|
||||
|
||||
promise_.Reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
int operations_running_ = 0;
|
||||
BrowsingDataRemover::DataType failed_data_types_ = 0ULL;
|
||||
gin_helper::Promise<void> promise_;
|
||||
base::ScopedObservation<content::BrowsingDataRemover,
|
||||
content::BrowsingDataRemover::Observer>
|
||||
observation_{this};
|
||||
};
|
||||
|
||||
base::Value::Dict createProxyConfig(ProxyPrefs::ProxyMode proxy_mode,
|
||||
@@ -1138,17 +1306,85 @@ v8::Local<v8::Promise> Session::ClearCodeCaches(
|
||||
return handle;
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> Session::ClearData(gin::Arguments* args) {
|
||||
v8::Local<v8::Value> Session::ClearData(gin_helper::ErrorThrower thrower,
|
||||
gin::Arguments* args) {
|
||||
auto* isolate = JavascriptEnvironment::GetIsolate();
|
||||
|
||||
BrowsingDataRemover::DataType data_type_mask = kClearDataTypeAll;
|
||||
std::vector<url::Origin> origins;
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode =
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode::kThirdPartiesIncluded;
|
||||
BrowsingDataFilterBuilder::Mode filter_mode =
|
||||
BrowsingDataFilterBuilder::Mode::kPreserve;
|
||||
|
||||
if (gin_helper::Dictionary options; args->GetNext(&options)) {
|
||||
if (std::vector<std::string> data_types;
|
||||
options.Get("dataTypes", &data_types)) {
|
||||
data_type_mask = GetDataTypeMask(data_types);
|
||||
}
|
||||
|
||||
if (bool avoid_closing_connections;
|
||||
options.Get("avoidClosingConnections", &avoid_closing_connections) &&
|
||||
avoid_closing_connections) {
|
||||
data_type_mask |=
|
||||
BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
|
||||
}
|
||||
|
||||
std::vector<GURL> origin_urls;
|
||||
{
|
||||
bool has_origins_key = options.Get("origins", &origin_urls);
|
||||
std::vector<GURL> exclude_origin_urls;
|
||||
bool has_exclude_origins_key =
|
||||
options.Get("excludeOrigins", &exclude_origin_urls);
|
||||
|
||||
if (has_origins_key && has_exclude_origins_key) {
|
||||
thrower.ThrowError(
|
||||
"Cannot provide both 'origins' and 'excludeOrigins'");
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
|
||||
if (has_origins_key) {
|
||||
filter_mode = BrowsingDataFilterBuilder::Mode::kDelete;
|
||||
} else if (has_exclude_origins_key) {
|
||||
origin_urls = std::move(exclude_origin_urls);
|
||||
}
|
||||
}
|
||||
|
||||
if (!origin_urls.empty()) {
|
||||
origins.reserve(origin_urls.size());
|
||||
for (const GURL& origin_url : origin_urls) {
|
||||
auto origin = url::Origin::Create(origin_url);
|
||||
|
||||
// Opaque origins cannot be used with this API
|
||||
if (origin.opaque()) {
|
||||
thrower.ThrowError(
|
||||
base::StringPrintf("Invalid origin: '%s'",
|
||||
origin_url.possibly_invalid_spec().c_str()));
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
|
||||
origins.push_back(std::move(origin));
|
||||
}
|
||||
}
|
||||
|
||||
if (std::string origin_matching_mode_string;
|
||||
options.Get("originMatchingMode", &origin_matching_mode_string)) {
|
||||
if (origin_matching_mode_string == "third-parties-included") {
|
||||
origin_matching_mode = BrowsingDataFilterBuilder::OriginMatchingMode::
|
||||
kThirdPartiesIncluded;
|
||||
} else if (origin_matching_mode_string == "origin-in-all-contexts") {
|
||||
origin_matching_mode =
|
||||
BrowsingDataFilterBuilder::OriginMatchingMode::kOriginInAllContexts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> promise_handle = promise.GetHandle();
|
||||
|
||||
content::BrowsingDataRemover* remover =
|
||||
browser_context_->GetBrowsingDataRemover();
|
||||
|
||||
auto* observer = new ClearDataObserver(std::move(promise), remover);
|
||||
remover->RemoveAndReply(base::Time::Min(), base::Time::Max(),
|
||||
kClearDataTypeAll, kClearOriginTypeAll, observer);
|
||||
BrowsingDataRemover* remover = browser_context_->GetBrowsingDataRemover();
|
||||
ClearDataTask::Run(remover, std::move(promise), data_type_mask,
|
||||
std::move(origins), filter_mode, origin_matching_mode);
|
||||
|
||||
return promise_handle;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,8 @@ class Session : public gin::Wrappable<Session>,
|
||||
v8::Local<v8::Value> GetPath(v8::Isolate* isolate);
|
||||
void SetCodeCachePath(gin::Arguments* args);
|
||||
v8::Local<v8::Promise> ClearCodeCaches(const gin_helper::Dictionary& options);
|
||||
v8::Local<v8::Promise> ClearData(gin::Arguments* args);
|
||||
v8::Local<v8::Value> ClearData(gin_helper::ErrorThrower thrower,
|
||||
gin::Arguments* args);
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
base::Value GetSpellCheckerLanguages();
|
||||
void SetSpellCheckerLanguages(gin_helper::ErrorThrower thrower,
|
||||
|
||||
@@ -42,6 +42,7 @@ void FrameSubscriber::AttachToHost(content::RenderWidgetHost* host) {
|
||||
|
||||
// Create and configure the video capturer.
|
||||
gfx::Size size = GetRenderViewSize();
|
||||
DCHECK(!size.IsEmpty());
|
||||
video_capturer_ = host_->GetView()->CreateVideoCapturer();
|
||||
video_capturer_->SetResolutionConstraints(size, size, true);
|
||||
video_capturer_->SetAutoThrottlingEnabled(false);
|
||||
|
||||
@@ -469,12 +469,12 @@ void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) {
|
||||
|
||||
void NativeWindowViews::SetContentView(views::View* view) {
|
||||
if (content_view()) {
|
||||
root_view_.RemoveChildView(content_view());
|
||||
root_view_.GetMainView()->RemoveChildView(content_view());
|
||||
}
|
||||
set_content_view(view);
|
||||
focused_view_ = view;
|
||||
root_view_.AddChildView(content_view());
|
||||
root_view_.DeprecatedLayoutImmediately();
|
||||
root_view_.GetMainView()->AddChildView(content_view());
|
||||
root_view_.GetMainView()->DeprecatedLayoutImmediately();
|
||||
}
|
||||
|
||||
void NativeWindowViews::Close() {
|
||||
@@ -1664,7 +1664,7 @@ std::u16string NativeWindowViews::GetWindowTitle() const {
|
||||
}
|
||||
|
||||
views::View* NativeWindowViews::GetContentsView() {
|
||||
return &root_view_;
|
||||
return root_view_.GetMainView();
|
||||
}
|
||||
|
||||
bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
|
||||
@@ -1674,7 +1674,7 @@ bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
|
||||
}
|
||||
|
||||
views::ClientView* NativeWindowViews::CreateClientView(views::Widget* widget) {
|
||||
return new NativeWindowClientView{widget, GetContentsView(), this};
|
||||
return new NativeWindowClientView{widget, &root_view_, this};
|
||||
}
|
||||
|
||||
std::unique_ptr<views::NonClientFrameView>
|
||||
|
||||
@@ -159,21 +159,21 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
|
||||
|
||||
void LibnotifyNotification::Dismiss() {
|
||||
if (!notification_) {
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
on_dismissing_ = true;
|
||||
libnotify_loader_.notify_notification_close(notification_, &error);
|
||||
if (error) {
|
||||
log_and_clear_error(error, "notify_notification_close");
|
||||
Destroy();
|
||||
}
|
||||
on_dismissing_ = false;
|
||||
}
|
||||
|
||||
void LibnotifyNotification::OnNotificationClosed(
|
||||
NotifyNotification* notification) {
|
||||
NotificationDismissed();
|
||||
NotificationDismissed(!on_dismissing_);
|
||||
}
|
||||
|
||||
void LibnotifyNotification::OnNotificationView(NotifyNotification* notification,
|
||||
|
||||
@@ -33,6 +33,7 @@ class LibnotifyNotification : public Notification {
|
||||
RAW_PTR_EXCLUSION NotifyNotification* notification_ = nullptr;
|
||||
|
||||
ScopedGSignal signal_;
|
||||
bool on_dismissing_ = false;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "content/browser/renderer_host/render_widget_host_delegate.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck
|
||||
#include "content/common/input/synthetic_gesture.h" // nogncheck
|
||||
#include "content/common/input/synthetic_gesture_target.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
|
||||
@@ -35,7 +35,9 @@ std::unique_ptr<content::SerialChooser> ElectronSerialDelegate::RunChooser(
|
||||
if (controller) {
|
||||
DeleteControllerForFrame(frame);
|
||||
}
|
||||
AddControllerForFrame(frame, std::move(filters), std::move(callback));
|
||||
AddControllerForFrame(frame, std::move(filters),
|
||||
std::move(allowed_bluetooth_service_class_ids),
|
||||
std::move(callback));
|
||||
|
||||
// Return a nullptr because the return value isn't used for anything, eg
|
||||
// there is no mechanism to cancel navigator.serial.requestPort(). The return
|
||||
@@ -106,12 +108,14 @@ SerialChooserController* ElectronSerialDelegate::ControllerForFrame(
|
||||
SerialChooserController* ElectronSerialDelegate::AddControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback) {
|
||||
auto* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
auto controller = std::make_unique<SerialChooserController>(
|
||||
render_frame_host, std::move(filters), std::move(callback), web_contents,
|
||||
weak_factory_.GetWeakPtr());
|
||||
render_frame_host, std::move(filters),
|
||||
std::move(allowed_bluetooth_service_class_ids), std::move(callback),
|
||||
web_contents, weak_factory_.GetWeakPtr());
|
||||
controller_map_.insert(
|
||||
std::make_pair(render_frame_host, std::move(controller)));
|
||||
return ControllerForFrame(render_frame_host);
|
||||
|
||||
@@ -67,6 +67,7 @@ class ElectronSerialDelegate : public content::SerialDelegate,
|
||||
SerialChooserController* AddControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback);
|
||||
|
||||
base::ScopedObservation<SerialChooserContext,
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
|
||||
#include "services/device/public/cpp/bluetooth/bluetooth_utils.h"
|
||||
#include "services/device/public/mojom/serial.mojom.h"
|
||||
#include "shell/browser/api/electron_api_session.h"
|
||||
#include "shell/browser/serial/serial_chooser_context.h"
|
||||
#include "shell/browser/serial/serial_chooser_context_factory.h"
|
||||
@@ -58,14 +61,57 @@ struct Converter<device::mojom::SerialPortInfoPtr> {
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::device::mojom::SerialPortType;
|
||||
|
||||
bool FilterMatchesPort(const blink::mojom::SerialPortFilter& filter,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (filter.bluetooth_service_class_id) {
|
||||
if (!port.bluetooth_service_class_id) {
|
||||
return false;
|
||||
}
|
||||
return device::BluetoothUUID(*port.bluetooth_service_class_id) ==
|
||||
device::BluetoothUUID(*filter.bluetooth_service_class_id);
|
||||
}
|
||||
if (!filter.has_vendor_id) {
|
||||
return true;
|
||||
}
|
||||
if (!port.has_vendor_id || port.vendor_id != filter.vendor_id) {
|
||||
return false;
|
||||
}
|
||||
if (!filter.has_product_id) {
|
||||
return true;
|
||||
}
|
||||
return port.has_product_id && port.product_id == filter.product_id;
|
||||
}
|
||||
|
||||
bool BluetoothPortIsAllowed(
|
||||
const std::vector<::device::BluetoothUUID>& allowed_ids,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (!port.bluetooth_service_class_id) {
|
||||
return true;
|
||||
}
|
||||
// Serial Port Profile is allowed by default.
|
||||
if (*port.bluetooth_service_class_id == device::GetSerialPortProfileUUID()) {
|
||||
return true;
|
||||
}
|
||||
return base::Contains(allowed_ids, port.bluetooth_service_class_id.value());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SerialChooserController::SerialChooserController(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronSerialDelegate> serial_delegate)
|
||||
: WebContentsObserver(web_contents),
|
||||
filters_(std::move(filters)),
|
||||
allowed_bluetooth_service_class_ids_(
|
||||
std::move(allowed_bluetooth_service_class_ids)),
|
||||
callback_(std::move(callback)),
|
||||
serial_delegate_(serial_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
@@ -93,10 +139,11 @@ api::Session* SerialChooserController::GetSession() {
|
||||
|
||||
void SerialChooserController::OnPortAdded(
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
if (!FilterMatchesAny(port))
|
||||
if (!DisplayDevice(port))
|
||||
return;
|
||||
|
||||
ports_.push_back(port.Clone());
|
||||
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
session->Emit("serial-port-added", port.Clone(), web_contents());
|
||||
@@ -105,9 +152,8 @@ void SerialChooserController::OnPortAdded(
|
||||
|
||||
void SerialChooserController::OnPortRemoved(
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
const auto it = std::find_if(
|
||||
ports_.begin(), ports_.end(),
|
||||
[&port](const auto& ptr) { return ptr->token == port.token; });
|
||||
const auto it = base::ranges::find(ports_, port.token,
|
||||
&device::mojom::SerialPortInfo::token);
|
||||
if (it != ports_.end()) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
@@ -152,7 +198,7 @@ void SerialChooserController::OnGetDevices(
|
||||
});
|
||||
|
||||
for (auto& port : ports) {
|
||||
if (FilterMatchesAny(*port))
|
||||
if (DisplayDevice(*port))
|
||||
ports_.push_back(std::move(port));
|
||||
}
|
||||
|
||||
@@ -169,21 +215,17 @@ void SerialChooserController::OnGetDevices(
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialChooserController::FilterMatchesAny(
|
||||
bool SerialChooserController::DisplayDevice(
|
||||
const device::mojom::SerialPortInfo& port) const {
|
||||
if (filters_.empty())
|
||||
return true;
|
||||
if (filters_.empty()) {
|
||||
return BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port);
|
||||
}
|
||||
|
||||
for (const auto& filter : filters_) {
|
||||
if (filter->has_vendor_id &&
|
||||
(!port.has_vendor_id || filter->vendor_id != port.vendor_id)) {
|
||||
continue;
|
||||
if (FilterMatchesPort(*filter, port) &&
|
||||
BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port)) {
|
||||
return true;
|
||||
}
|
||||
if (filter->has_product_id &&
|
||||
(!port.has_product_id || filter->product_id != port.product_id)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -34,6 +34,7 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
|
||||
SerialChooserController(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters,
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids,
|
||||
content::SerialChooser::Callback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronSerialDelegate> serial_delegate);
|
||||
@@ -55,11 +56,12 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
|
||||
private:
|
||||
api::Session* GetSession();
|
||||
void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports);
|
||||
bool FilterMatchesAny(const device::mojom::SerialPortInfo& port) const;
|
||||
bool DisplayDevice(const device::mojom::SerialPortInfo& port) const;
|
||||
void RunCallback(device::mojom::SerialPortInfoPtr port);
|
||||
void OnDeviceChosen(const std::string& port_id);
|
||||
|
||||
std::vector<blink::mojom::SerialPortFilterPtr> filters_;
|
||||
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids_;
|
||||
content::SerialChooser::Callback callback_;
|
||||
url::Origin origin_;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/socket/tcp_server_socket.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/common/electron_paths.h"
|
||||
#include "third_party/inspector_protocol/crdtp/dispatch.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
@@ -139,4 +140,8 @@ bool DevToolsManagerDelegate::HasBundledFrontendResources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
content::BrowserContext* DevToolsManagerDelegate::GetDefaultBrowserContext() {
|
||||
return ElectronBrowserContext::From("", false);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "base/compiler_specific.h"
|
||||
#include "content/public/browser/devtools_manager_delegate.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
|
||||
class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||
@@ -33,6 +37,7 @@ class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||
TargetType target_type) override;
|
||||
std::string GetDiscoveryPageHTML() override;
|
||||
bool HasBundledFrontendResources() override;
|
||||
content::BrowserContext* GetDefaultBrowserContext() override;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -9,18 +9,12 @@
|
||||
#include "content/public/common/input/native_web_keyboard_event.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/ui/views/menu_bar.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
// The menu bar height in pixels.
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
const int kMenuBarHeight = 20;
|
||||
#else
|
||||
const int kMenuBarHeight = 25;
|
||||
#endif
|
||||
|
||||
bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
|
||||
return event.windows_key_code == ui::VKEY_MENU;
|
||||
}
|
||||
@@ -41,6 +35,12 @@ RootView::RootView(NativeWindow* window)
|
||||
: window_(window),
|
||||
last_focused_view_tracker_(std::make_unique<views::ViewTracker>()) {
|
||||
set_owned_by_client();
|
||||
views::BoxLayout* layout =
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical));
|
||||
main_view_ = AddChildView(std::make_unique<views::View>());
|
||||
main_view_->SetUseDefaultFillLayout(true);
|
||||
layout->SetFlexForView(main_view_, 1);
|
||||
}
|
||||
|
||||
RootView::~RootView() = default;
|
||||
@@ -77,7 +77,7 @@ bool RootView::HasMenu() const {
|
||||
}
|
||||
|
||||
int RootView::GetMenuBarHeight() const {
|
||||
return kMenuBarHeight;
|
||||
return menu_bar_ ? menu_bar_->GetPreferredSize().height() : 0;
|
||||
}
|
||||
|
||||
void RootView::SetAutoHideMenuBar(bool auto_hide) {
|
||||
@@ -90,10 +90,8 @@ void RootView::SetMenuBarVisibility(bool visible) {
|
||||
|
||||
menu_bar_visible_ = visible;
|
||||
if (visible) {
|
||||
DCHECK_EQ(children().size(), 1ul);
|
||||
AddChildView(menu_bar_.get());
|
||||
AddChildViewAt(menu_bar_.get(), 0);
|
||||
} else {
|
||||
DCHECK_EQ(children().size(), 2ul);
|
||||
RemoveChildView(menu_bar_.get());
|
||||
}
|
||||
|
||||
@@ -166,21 +164,6 @@ void RootView::ResetAltState() {
|
||||
menu_bar_alt_pressed_ = false;
|
||||
}
|
||||
|
||||
void RootView::Layout(PassKey) {
|
||||
if (!window_->content_view()) // Not ready yet.
|
||||
return;
|
||||
|
||||
const auto menu_bar_bounds =
|
||||
menu_bar_visible_ ? gfx::Rect(0, 0, size().width(), kMenuBarHeight)
|
||||
: gfx::Rect();
|
||||
if (menu_bar_)
|
||||
menu_bar_->SetBoundsRect(menu_bar_bounds);
|
||||
|
||||
window_->content_view()->SetBoundsRect(
|
||||
gfx::Rect(0, menu_bar_visible_ ? menu_bar_bounds.bottom() : 0,
|
||||
size().width(), size().height() - menu_bar_bounds.height()));
|
||||
}
|
||||
|
||||
gfx::Size RootView::GetMinimumSize() const {
|
||||
return window_->GetMinimumSize();
|
||||
}
|
||||
|
||||
@@ -46,8 +46,9 @@ class RootView : public views::View {
|
||||
void RegisterAcceleratorsWithFocusManager(ElectronMenuModel* menu_model);
|
||||
void UnregisterAcceleratorsWithFocusManager();
|
||||
|
||||
views::View* GetMainView() { return main_view_; }
|
||||
|
||||
// views::View:
|
||||
void Layout(PassKey) override;
|
||||
gfx::Size GetMinimumSize() const override;
|
||||
gfx::Size GetMaximumSize() const override;
|
||||
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
|
||||
@@ -62,6 +63,9 @@ class RootView : public views::View {
|
||||
bool menu_bar_visible_ = false;
|
||||
bool menu_bar_alt_pressed_ = false;
|
||||
|
||||
// Main view area.
|
||||
raw_ptr<views::View> main_view_;
|
||||
|
||||
// Map from accelerator to menu item's command id.
|
||||
accelerator_util::AcceleratorTable accelerator_table_;
|
||||
|
||||
|
||||
@@ -156,36 +156,26 @@ void WinCaptionButtonContainer::UpdateBackground() {
|
||||
}
|
||||
|
||||
void WinCaptionButtonContainer::UpdateButtons() {
|
||||
const bool is_maximized = frame_view_->frame()->IsMaximized();
|
||||
restore_button_->SetVisible(is_maximized);
|
||||
maximize_button_->SetVisible(!is_maximized);
|
||||
|
||||
const bool minimizable = frame_view_->window()->IsMinimizable();
|
||||
minimize_button_->SetEnabled(minimizable);
|
||||
minimize_button_->SetVisible(minimizable);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled.
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
const bool is_maximized = frame_view_->frame()->IsMaximized();
|
||||
const bool maximizable = frame_view_->window()->IsMaximizable();
|
||||
restore_button_->SetVisible(is_maximized && maximizable);
|
||||
maximize_button_->SetVisible(!is_maximized && maximizable);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled, unless the window is not
|
||||
// maximized.
|
||||
const bool maximizable = frame_view_->window()->IsMaximizable();
|
||||
maximize_button_->SetEnabled(!(is_touch && is_maximized) && maximizable);
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
maximize_button_->SetEnabled(!is_touch || !is_maximized);
|
||||
|
||||
// If the window isn't closable, the close button should be disabled.
|
||||
const bool closable = frame_view_->window()->IsClosable();
|
||||
close_button_->SetEnabled(closable);
|
||||
|
||||
// If all three of closable, maximizable, and minimizable are disabled,
|
||||
// Windows natively only shows the disabled closable button. Copy that
|
||||
// behavior here.
|
||||
if (!maximizable && !closable && !minimizable) {
|
||||
minimize_button_->SetVisible(false);
|
||||
maximize_button_->SetVisible(false);
|
||||
}
|
||||
|
||||
InvalidateLayout();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ const isScaleFactorRounding = () => {
|
||||
|
||||
const expectBoundsEqual = (actual: any, expected: any) => {
|
||||
if (!isScaleFactorRounding()) {
|
||||
expect(expected).to.deep.equal(actual);
|
||||
expect(actual).to.deep.equal(expected);
|
||||
} else if (Array.isArray(actual)) {
|
||||
expect(actual[0]).to.be.closeTo(expected[0], 1);
|
||||
expect(actual[1]).to.be.closeTo(expected[1], 1);
|
||||
|
||||
@@ -177,6 +177,38 @@ describe('debugger module', () => {
|
||||
await loadingFinished;
|
||||
});
|
||||
|
||||
it('can get and set cookies using the Storage API', async () => {
|
||||
await w.webContents.loadURL('about:blank');
|
||||
w.webContents.debugger.attach('1.1');
|
||||
|
||||
await w.webContents.debugger.sendCommand('Storage.clearCookies', {});
|
||||
await w.webContents.debugger.sendCommand('Storage.setCookies', {
|
||||
cookies: [
|
||||
{
|
||||
name: 'cookieOne',
|
||||
value: 'cookieValueOne',
|
||||
url: 'https://cookieone.com'
|
||||
},
|
||||
{
|
||||
name: 'cookieTwo',
|
||||
value: 'cookieValueTwo',
|
||||
url: 'https://cookietwo.com'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { cookies } = await w.webContents.debugger.sendCommand('Storage.getCookies', {});
|
||||
expect(cookies).to.have.lengthOf(2);
|
||||
|
||||
const cookieOne = cookies.find((cookie: any) => cookie.name === 'cookieOne');
|
||||
expect(cookieOne.domain).to.equal('cookieone.com');
|
||||
expect(cookieOne.value).to.equal('cookieValueOne');
|
||||
|
||||
const cookieTwo = cookies.find((cookie: any) => cookie.name === 'cookieTwo');
|
||||
expect(cookieTwo.domain).to.equal('cookietwo.com');
|
||||
expect(cookieTwo.value).to.equal('cookieValueTwo');
|
||||
});
|
||||
|
||||
it('uses empty sessionId by default', async () => {
|
||||
w.webContents.loadURL('about:blank');
|
||||
w.webContents.debugger.attach();
|
||||
|
||||
@@ -616,6 +616,25 @@ describe('net module (session)', () => {
|
||||
});
|
||||
}).to.throw('`partition` should be a string');
|
||||
});
|
||||
|
||||
it('should throw if given a header value that is empty(null/undefined)', () => {
|
||||
const emptyHeaderValues = [null, undefined];
|
||||
const errorMsg = '`value` required in setHeader("foo", value)';
|
||||
|
||||
for (const emptyValue of emptyHeaderValues) {
|
||||
expect(() => {
|
||||
net.request({
|
||||
url: 'https://foo',
|
||||
headers: { foo: emptyValue as any }
|
||||
} as any);
|
||||
}).to.throw(errorMsg);
|
||||
|
||||
const request = net.request({ url: 'https://foo' });
|
||||
expect(() => {
|
||||
request.setHeader('foo', emptyValue as any);
|
||||
}).to.throw(errorMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('net.fetch', () => {
|
||||
|
||||
@@ -935,12 +935,11 @@ describe('net module', () => {
|
||||
response.end();
|
||||
});
|
||||
const serverUrl = url.parse(serverUrlUnparsed);
|
||||
const options = {
|
||||
const urlRequest = net.request({
|
||||
port: serverUrl.port ? parseInt(serverUrl.port, 10) : undefined,
|
||||
hostname: '127.0.0.1',
|
||||
headers: { [customHeaderName]: customHeaderValue }
|
||||
};
|
||||
const urlRequest = net.request(options);
|
||||
});
|
||||
const response = await getResponse(urlRequest);
|
||||
expect(response.statusCode).to.be.equal(200);
|
||||
await collectStreamBody(response);
|
||||
|
||||
@@ -1613,7 +1613,7 @@ describe('session module', () => {
|
||||
|
||||
// NOTE: This API clears more than localStorage, but localStorage is a
|
||||
// convenient test target for this API
|
||||
it('clears localstorage data', async () => {
|
||||
it('clears all data when no options supplied', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
|
||||
await w.loadFile(path.join(fixtures, 'api', 'localstorage.html'));
|
||||
|
||||
@@ -1623,7 +1623,8 @@ describe('session module', () => {
|
||||
|
||||
expect(await w.webContents.executeJavaScript('localStorage.length')).to.equal(0);
|
||||
});
|
||||
it('clears localstorage data when called twice in parallel', async () => {
|
||||
|
||||
it('clears all data when no options supplied, called twice in parallel', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
|
||||
await w.loadFile(path.join(fixtures, 'api', 'localstorage.html'));
|
||||
|
||||
@@ -1638,5 +1639,92 @@ describe('session module', () => {
|
||||
// Await the first promise so it doesn't creep into another test
|
||||
await clearDataPromise;
|
||||
});
|
||||
|
||||
it('only clears specified data categories', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: { nodeIntegration: true, contextIsolation: false }
|
||||
});
|
||||
await w.loadFile(
|
||||
path.join(fixtures, 'api', 'localstorage-and-indexeddb.html')
|
||||
);
|
||||
|
||||
const { webContents } = w;
|
||||
const { session } = webContents;
|
||||
|
||||
await once(ipcMain, 'indexeddb-ready');
|
||||
|
||||
async function queryData (channel: string): Promise<string> {
|
||||
const event = once(ipcMain, `result-${channel}`);
|
||||
webContents.send(`get-${channel}`);
|
||||
return (await event)[1];
|
||||
}
|
||||
|
||||
// Data is in localStorage
|
||||
await expect(queryData('localstorage')).to.eventually.equal('hello localstorage');
|
||||
// Data is in indexedDB
|
||||
await expect(queryData('indexeddb')).to.eventually.equal('hello indexeddb');
|
||||
|
||||
// Clear only indexedDB, not localStorage
|
||||
await session.clearData({ dataTypes: ['indexedDB'] });
|
||||
|
||||
// The localStorage data should still be there
|
||||
await expect(queryData('localstorage')).to.eventually.equal('hello localstorage');
|
||||
|
||||
// The indexedDB data should be gone
|
||||
await expect(queryData('indexeddb')).to.eventually.be.undefined();
|
||||
});
|
||||
|
||||
it('only clears the specified origins', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const { session } = w.webContents;
|
||||
const { cookies } = session;
|
||||
|
||||
await Promise.all([
|
||||
cookies.set({
|
||||
url: 'https://example.com/',
|
||||
name: 'testdotcom',
|
||||
value: 'testdotcom'
|
||||
}),
|
||||
cookies.set({
|
||||
url: 'https://example.org/',
|
||||
name: 'testdotorg',
|
||||
value: 'testdotorg'
|
||||
})
|
||||
]);
|
||||
|
||||
await session.clearData({ origins: ['https://example.com'] });
|
||||
|
||||
expect((await cookies.get({ url: 'https://example.com/', name: 'testdotcom' })).length).to.equal(0);
|
||||
expect((await cookies.get({ url: 'https://example.org/', name: 'testdotorg' })).length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('clears all except the specified origins', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const { session } = w.webContents;
|
||||
const { cookies } = session;
|
||||
|
||||
await Promise.all([
|
||||
cookies.set({
|
||||
url: 'https://example.com/',
|
||||
name: 'testdotcom',
|
||||
value: 'testdotcom'
|
||||
}),
|
||||
cookies.set({
|
||||
url: 'https://example.org/',
|
||||
name: 'testdotorg',
|
||||
value: 'testdotorg'
|
||||
})
|
||||
]);
|
||||
|
||||
await session.clearData({ excludeOrigins: ['https://example.com'] });
|
||||
|
||||
expect((await cookies.get({ url: 'https://example.com/', name: 'testdotcom' })).length).to.be.greaterThan(0);
|
||||
expect((await cookies.get({ url: 'https://example.org/', name: 'testdotorg' })).length).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -548,6 +548,9 @@ describe('webContents module', () => {
|
||||
|
||||
describe('navigationHistory', () => {
|
||||
let w: BrowserWindow;
|
||||
const urlPage1 = 'data:text/html,<html><head><script>document.title = "Page 1";</script></head><body></body></html>';
|
||||
const urlPage2 = 'data:text/html,<html><head><script>document.title = "Page 2";</script></head><body></body></html>';
|
||||
const urlPage3 = 'data:text/html,<html><head><script>document.title = "Page 3";</script></head><body></body></html>';
|
||||
|
||||
beforeEach(async () => {
|
||||
w = new BrowserWindow({ show: false });
|
||||
@@ -559,44 +562,32 @@ describe('webContents module', () => {
|
||||
expect(result).to.deep.equal({ url: '', title: '' });
|
||||
});
|
||||
it('should fetch navigation entry given a valid index', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: 'https://www.google.com/', title: 'Google' });
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: urlPage1, title: 'Page 1' });
|
||||
});
|
||||
it('should return null given an invalid index larger than history length', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(5);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(5);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
it('should return null given an invalid negative index', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(-1);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(-1);
|
||||
expect(result).to.be.null();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigationHistory.getActiveIndex() API', () => {
|
||||
it('should return valid active index after a single page visit', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
|
||||
});
|
||||
|
||||
it('should return valid active index after a multiple page visits', async () => {
|
||||
const loadPromise = once(w.webContents, 'did-finish-load');
|
||||
await w.loadURL('https://www.github.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('https://www.google.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('about:blank');
|
||||
await loadPromise;
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(2);
|
||||
});
|
||||
@@ -608,20 +599,14 @@ describe('webContents module', () => {
|
||||
|
||||
describe('navigationHistory.length() API', () => {
|
||||
it('should return valid history length after a single page visit', async () => {
|
||||
await w.loadURL('https://www.google.com');
|
||||
w.webContents.on('did-finish-load', async () => {
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
});
|
||||
await w.loadURL(urlPage1);
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||
});
|
||||
|
||||
it('should return valid history length after a multiple page visits', async () => {
|
||||
const loadPromise = once(w.webContents, 'did-finish-load');
|
||||
await w.loadURL('https://www.github.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('https://www.google.com');
|
||||
await loadPromise;
|
||||
await w.loadURL('about:blank');
|
||||
await loadPromise;
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
expect(w.webContents.navigationHistory.length()).to.equal(3);
|
||||
});
|
||||
|
||||
@@ -1383,6 +1383,138 @@ describe('chromium features', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Storage Access API', () => {
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => {
|
||||
session.defaultSession.setPermissionCheckHandler(null);
|
||||
session.defaultSession.setPermissionRequestHandler(null);
|
||||
});
|
||||
|
||||
it('can determine if a permission is granted for "storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission === 'storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({ name: 'storage-access' })
|
||||
.then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can determine if a permission is denied for "storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission !== 'storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({ name: 'storage-access' })
|
||||
.then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
|
||||
it('can determine if a permission is granted for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission === 'top-level-storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({
|
||||
name: 'top-level-storage-access',
|
||||
requestedOrigin: "https://www.example.com",
|
||||
}).then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can determine if a permission is denied for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionCheckHandler(
|
||||
(_wc, permission) => permission !== 'top-level-storage-access'
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'a.html'));
|
||||
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
navigator.permissions.query({
|
||||
name: 'top-level-storage-access',
|
||||
requestedOrigin: "https://www.example.com",
|
||||
}).then(permission => permission.state).catch(err => err.message);
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
|
||||
it('can grant a permission request for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionRequestHandler(
|
||||
(_wc, permission, callback) => {
|
||||
callback(permission === 'top-level-storage-access');
|
||||
}
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'button.html'));
|
||||
|
||||
// requestStorageAccessFor returns a Promise that fulfills with undefined
|
||||
// if the access to third-party cookies was granted and rejects if access was denied.
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const button = document.getElementById('button');
|
||||
button.addEventListener("click", () => {
|
||||
document.requestStorageAccessFor('https://myfakesite').then(
|
||||
(res) => { resolve('granted') },
|
||||
(err) => { resolve('denied') },
|
||||
);
|
||||
});
|
||||
button.click();
|
||||
});
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('granted');
|
||||
});
|
||||
|
||||
it('can deny a permission request for "top-level-storage-access"', async () => {
|
||||
session.defaultSession.setPermissionRequestHandler(
|
||||
(_wc, permission, callback) => {
|
||||
callback(permission !== 'top-level-storage-access');
|
||||
}
|
||||
);
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixturesPath, 'pages', 'button.html'));
|
||||
|
||||
// requestStorageAccessFor returns a Promise that fulfills with undefined
|
||||
// if the access to third-party cookies was granted and rejects if access was denied.
|
||||
const permission = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const button = document.getElementById('button');
|
||||
button.addEventListener("click", () => {
|
||||
document.requestStorageAccessFor('https://myfakesite').then(
|
||||
(res) => { resolve('granted') },
|
||||
(err) => { resolve('denied') },
|
||||
);
|
||||
});
|
||||
button.click();
|
||||
});
|
||||
`, true);
|
||||
|
||||
expect(permission).to.eq('denied');
|
||||
});
|
||||
});
|
||||
|
||||
describe('IdleDetection', () => {
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => {
|
||||
|
||||
50
spec/fixtures/api/localstorage-and-indexeddb.html
vendored
Normal file
50
spec/fixtures/api/localstorage-and-indexeddb.html
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const {ipcRenderer} = require('electron');
|
||||
|
||||
window.localStorage.setItem('test', 'hello localstorage');
|
||||
ipcRenderer.on('get-localstorage', () => {
|
||||
const result = window.localStorage.getItem('test');
|
||||
ipcRenderer.send('result-localstorage', result);
|
||||
});
|
||||
|
||||
const deleteRequest = window.indexedDB.deleteDatabase('testdb');
|
||||
deleteRequest.onerror = deleteRequest.onsuccess = (event) => {
|
||||
const openRequest = window.indexedDB.open('testdb');
|
||||
openRequest.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
db.onerror = (event) => {
|
||||
console.error(event);
|
||||
};
|
||||
const objectStore = db.createObjectStore('testdata');
|
||||
objectStore.createIndex('test', '');
|
||||
};
|
||||
openRequest.onsuccess = (event) => {
|
||||
const db = event.target.result;
|
||||
const addRequest = db.transaction("testdata", "readwrite").objectStore("testdata").add("hello indexeddb", 'test');
|
||||
addRequest.onsuccess = () => {
|
||||
ipcRenderer.send('indexeddb-ready');
|
||||
};
|
||||
};
|
||||
};
|
||||
ipcRenderer.on('get-indexeddb', () => {
|
||||
const openRequest = window.indexedDB.open('testdb');
|
||||
openRequest.onerror = (event) => {
|
||||
console.error(event);
|
||||
};
|
||||
openRequest.onsuccess = (event) => {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains('testdata')) {
|
||||
ipcRenderer.send('result-indexeddb', undefined);
|
||||
return;
|
||||
}
|
||||
const getRequest = db.transaction('testdata', 'readonly').objectStore('testdata').get('test');
|
||||
getRequest.onsuccess = (event) => {
|
||||
ipcRenderer.send('result-indexeddb', event.target.result);
|
||||
};
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -14,8 +14,6 @@ import { HexColors, ScreenCapture } from './lib/screen-helpers';
|
||||
declare let WebView: any;
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
const isMacArm64 = (process.platform === 'darwin' && process.arch === 'arm64');
|
||||
|
||||
async function loadWebView (w: WebContents, attributes: Record<string, string>, opts?: {openDevTools?: boolean}): Promise<void> {
|
||||
const { openDevTools } = {
|
||||
openDevTools: false,
|
||||
@@ -2107,9 +2105,8 @@ describe('<webview> tag', function () {
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(miniak): figure out why this is failing on windows
|
||||
// TODO(vertedinde): figure out why this is failing on mac arm64
|
||||
ifdescribe(process.platform !== 'win32' && !isMacArm64)('<webview>.capturePage()', () => {
|
||||
// FIXME: This test is flaking constantly on Linux and macOS.
|
||||
xdescribe('<webview>.capturePage()', () => {
|
||||
it('returns a Promise with a NativeImage', async function () {
|
||||
this.retries(5);
|
||||
|
||||
|
||||
65
yarn.lock
65
yarn.lock
@@ -199,10 +199,10 @@
|
||||
"@octokit/auth-app" "^4.0.13"
|
||||
"@octokit/rest" "^19.0.11"
|
||||
|
||||
"@electron/lint-roller@^1.9.0":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.11.1.tgz#bf4ab114e8cb3a77e2c634807d4af3d40c3ba863"
|
||||
integrity sha512-LfErOK5MnSmoSyVN5OnHWsQ8p5It8JBE0AmRYCx65WS4BZudnYeRcohlRSOSi+GGqldujdKHyEo+fdY5lREL/Q==
|
||||
"@electron/lint-roller@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.12.1.tgz#3152b9a68815b2ab51cc5a4d462ae6769d5052ce"
|
||||
integrity sha512-TGgVcHUAooM9/dV3iJTxhmKl35x/gzStsClz2/LWtBQZ59cRK+0YwWF5HWhtydGFIpOLEQGzCvUrty5zZLyd4w==
|
||||
dependencies:
|
||||
"@dsanders11/vscode-markdown-languageservice" "^0.3.0"
|
||||
balanced-match "^2.0.0"
|
||||
@@ -1599,13 +1599,13 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||
|
||||
body-parser@1.20.1:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
|
||||
body-parser@1.20.2:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.4"
|
||||
content-type "~1.0.5"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
@@ -1613,7 +1613,7 @@ body-parser@1.20.1:
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
raw-body "2.5.1"
|
||||
raw-body "2.5.2"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
@@ -2017,20 +2017,20 @@ content-disposition@0.5.4:
|
||||
dependencies:
|
||||
safe-buffer "5.2.1"
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
content-type@~1.0.4, content-type@~1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
|
||||
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
cookie@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
@@ -2071,14 +2071,7 @@ debug@^3.1.0, debug@^3.2.7:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.0.0:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
@@ -2808,17 +2801,17 @@ execa@^4.0.1:
|
||||
signal-exit "^3.0.2"
|
||||
strip-final-newline "^2.0.0"
|
||||
|
||||
express@^4.16.4:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
|
||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||
express@^4.19.2:
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.1"
|
||||
body-parser "1.20.2"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.5.0"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
@@ -5243,10 +5236,10 @@ range-parser@~1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
|
||||
raw-body@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
|
||||
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
|
||||
raw-body@2.5.2:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
|
||||
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user