mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
33 Commits
v31.7.1
...
v9.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b9ef75d82 | ||
|
|
4dc2b4d55f | ||
|
|
69f77d309d | ||
|
|
9cd1744a2b | ||
|
|
c6e411173e | ||
|
|
8b11adc6f2 | ||
|
|
57ec30e459 | ||
|
|
c4a836f95a | ||
|
|
9b2de2583e | ||
|
|
7b2bfb4a0f | ||
|
|
0e31826043 | ||
|
|
7b3c073b3e | ||
|
|
2dc900b95d | ||
|
|
f41423501a | ||
|
|
07d9728b63 | ||
|
|
d4c90e80a5 | ||
|
|
91141028e6 | ||
|
|
9d69d4b9ef | ||
|
|
d57d5c544e | ||
|
|
74d4dab157 | ||
|
|
94246dabe3 | ||
|
|
4fe91e56ac | ||
|
|
0d7440d676 | ||
|
|
60edd2d3b1 | ||
|
|
d4d06660f3 | ||
|
|
8d8f15121b | ||
|
|
1f8cb5144d | ||
|
|
667ee359a1 | ||
|
|
4b009159ba | ||
|
|
89f66bd00c | ||
|
|
2d542c6028 | ||
|
|
68346fec55 | ||
|
|
23f32ca9f3 |
@@ -41,6 +41,14 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-linux-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
run-macos-publish:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
# The config expects the following environment variables to be set:
|
||||
# - "SLACK_WEBHOOK" Slack hook URL to send notifications.
|
||||
#
|
||||
@@ -65,12 +73,12 @@ machine-linux-2xlarge: &machine-linux-2xlarge
|
||||
|
||||
machine-mac: &machine-mac
|
||||
macos:
|
||||
xcode: "10.3.0"
|
||||
xcode: "11.1.0"
|
||||
|
||||
machine-mac-large: &machine-mac-large
|
||||
resource_class: large
|
||||
macos:
|
||||
xcode: "10.3.0"
|
||||
xcode: "11.1.0"
|
||||
|
||||
# Build configurations options.
|
||||
env-testing-build: &env-testing-build
|
||||
@@ -432,15 +440,17 @@ step-electron-dist-store: &step-electron-dist-store
|
||||
path: src/out/Default/dist.zip
|
||||
destination: dist.zip
|
||||
|
||||
step-electron-chromedriver-gn-gen: &step-electron-chromedriver-gn-gen
|
||||
step-electron-maybe-chromedriver-gn-gen: &step-electron-maybe-chromedriver-gn-gen
|
||||
run:
|
||||
name: chromedriver GN gen
|
||||
command: |
|
||||
cd src
|
||||
if [ "$USE_GOMA" == "true" ]; then
|
||||
gn gen out/chromedriver --args="import(\"$GN_CONFIG\") import(\"//electron/build/args/goma.gn\") is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS"
|
||||
else
|
||||
gn gen out/chromedriver --args="import(\"$GN_CONFIG\") cc_wrapper=\"$SCCACHE_PATH\" is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS"
|
||||
if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
if [ "$USE_GOMA" == "true" ]; then
|
||||
gn gen out/chromedriver --args="import(\"$GN_CONFIG\") import(\"//electron/build/args/goma.gn\") is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS"
|
||||
else
|
||||
gn gen out/chromedriver --args="import(\"$GN_CONFIG\") cc_wrapper=\"$SCCACHE_PATH\" is_component_ffmpeg=false proprietary_codecs=false $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS"
|
||||
fi
|
||||
fi
|
||||
|
||||
step-electron-chromedriver-build: &step-electron-chromedriver-build
|
||||
@@ -448,13 +458,23 @@ step-electron-chromedriver-build: &step-electron-chromedriver-build
|
||||
name: Build chromedriver.zip
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/chromedriver electron:electron_chromedriver -j $NUMBER_OF_NINJA_PROCESSES
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/chromedriver/chromedriver
|
||||
ninja -C out/chromedriver electron:electron_chromedriver_zip
|
||||
if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
export CHROMEDRIVER_DIR="out/chromedriver"
|
||||
else
|
||||
export CHROMEDRIVER_DIR="out/Default"
|
||||
fi
|
||||
ninja -C $CHROMEDRIVER_DIR electron:electron_chromedriver -j $NUMBER_OF_NINJA_PROCESSES
|
||||
if [ "`uname`" == "Linux" ]; then
|
||||
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/$CHROMEDRIVER_DIR/chromedriver
|
||||
fi
|
||||
ninja -C $CHROMEDRIVER_DIR electron:electron_chromedriver_zip
|
||||
if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
cp out/chromedriver/chromedriver.zip out/Default
|
||||
fi
|
||||
|
||||
step-electron-chromedriver-store: &step-electron-chromedriver-store
|
||||
store_artifacts:
|
||||
path: src/out/chromedriver/chromedriver.zip
|
||||
path: src/out/Default/chromedriver.zip
|
||||
destination: chromedriver.zip
|
||||
|
||||
step-nodejs-headers-build: &step-nodejs-headers-build
|
||||
@@ -481,6 +501,7 @@ step-electron-publish: &step-electron-publish
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
rm -rf src/out/Default/obj
|
||||
fi
|
||||
|
||||
cd src/electron
|
||||
if [ "$UPLOAD_TO_S3" == "1" ]; then
|
||||
echo 'Uploading Electron release distribution to S3'
|
||||
@@ -497,7 +518,7 @@ step-persist-data-for-tests: &step-persist-data-for-tests
|
||||
# Build artifacts
|
||||
- src/out/Default/dist.zip
|
||||
- src/out/Default/mksnapshot.zip
|
||||
- src/out/chromedriver/chromedriver.zip
|
||||
- src/out/Default/chromedriver.zip
|
||||
- src/out/Default/shell_browser_ui_unittests
|
||||
- src/out/Default/gen/node_headers
|
||||
- src/out/ffmpeg/ffmpeg.zip
|
||||
@@ -532,7 +553,7 @@ step-chromedriver-unzip: &step-chromedriver-unzip
|
||||
run:
|
||||
name: Unzip chromedriver.zip
|
||||
command: |
|
||||
cd src/out/chromedriver
|
||||
cd src/out/Default
|
||||
unzip -o chromedriver.zip
|
||||
|
||||
step-ffmpeg-gn-gen: &step-ffmpeg-gn-gen
|
||||
@@ -577,7 +598,7 @@ step-verify-chromedriver: &step-verify-chromedriver
|
||||
name: Verify ChromeDriver
|
||||
command: |
|
||||
cd src
|
||||
python electron/script/verify-chromedriver.py --source-root "$PWD" --build-dir out/chromedriver
|
||||
python electron/script/verify-chromedriver.py --source-root "$PWD" --build-dir out/Default
|
||||
|
||||
step-setup-linux-for-headless-testing: &step-setup-linux-for-headless-testing
|
||||
run:
|
||||
@@ -833,7 +854,7 @@ step-check-for-doc-only-change: &step-check-for-doc-only-change
|
||||
command: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
if node script/doc-only-change.js --prNumber=$CIRCLE_PR_NUMBER --prURL=$CIRCLE_PULL_REQUEST; then
|
||||
if node script/doc-only-change.js --prNumber=$CIRCLE_PR_NUMBER --prURL=$CIRCLE_PULL_REQUEST --prBranch=$CIRCLE_BRANCH; then
|
||||
#PR is doc only change; save file with value true to indicate doc only change
|
||||
echo "true" > .skip-ci-build
|
||||
else
|
||||
@@ -869,7 +890,7 @@ step-ts-compile: &step-ts-compile
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default electron:default_app_js -j $NUMBER_OF_NINJA_PROCESSES
|
||||
ninja -C out/Default electron:atom_js2c -j $NUMBER_OF_NINJA_PROCESSES
|
||||
ninja -C out/Default electron:electron_js2c -j $NUMBER_OF_NINJA_PROCESSES
|
||||
|
||||
# Lists of steps.
|
||||
steps-lint: &steps-lint
|
||||
@@ -1003,7 +1024,7 @@ steps-electron-build: &steps-electron-build
|
||||
- *step-maybe-cross-arch-snapshot-store
|
||||
|
||||
# chromedriver
|
||||
- *step-electron-chromedriver-gn-gen
|
||||
- *step-electron-maybe-chromedriver-gn-gen
|
||||
- *step-electron-chromedriver-build
|
||||
- *step-electron-chromedriver-store
|
||||
|
||||
@@ -1067,56 +1088,6 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha
|
||||
#Compile ts/js to verify doc change didn't break anything
|
||||
- *step-ts-compile
|
||||
|
||||
steps-electron-build-for-publish: &steps-electron-build-for-publish
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-gclient-sync
|
||||
- *step-setup-env-for-build
|
||||
- *step-delete-git-directories
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
- *step-gn-gen-default
|
||||
|
||||
# Electron app
|
||||
- *step-electron-build
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-electron-dist-strip
|
||||
- *step-electron-dist-build
|
||||
- *step-electron-dist-store
|
||||
- *step-maybe-zip-symbols
|
||||
- *step-symbols-store
|
||||
|
||||
# mksnapshot
|
||||
- *step-mksnapshot-build
|
||||
- *step-mksnapshot-store
|
||||
|
||||
# chromedriver
|
||||
- *step-electron-chromedriver-gn-gen
|
||||
- *step-electron-chromedriver-build
|
||||
- *step-electron-chromedriver-store
|
||||
|
||||
# Node.js headers
|
||||
- *step-nodejs-headers-build
|
||||
- *step-nodejs-headers-store
|
||||
|
||||
# ffmpeg
|
||||
- *step-ffmpeg-gn-gen
|
||||
- *step-ffmpeg-build
|
||||
- *step-ffmpeg-store
|
||||
|
||||
# hunspell
|
||||
- *step-hunspell-build
|
||||
- *step-hunspell-store
|
||||
|
||||
# typescript defs
|
||||
- *step-maybe-generate-typescript-defs
|
||||
|
||||
# Publish
|
||||
- *step-electron-publish
|
||||
|
||||
steps-chromedriver-build: &steps-chromedriver-build
|
||||
steps:
|
||||
- attach_workspace:
|
||||
@@ -1125,7 +1096,7 @@ steps-chromedriver-build: &steps-chromedriver-build
|
||||
- *step-setup-env-for-build
|
||||
- *step-fix-sync-on-mac
|
||||
|
||||
- *step-electron-chromedriver-gn-gen
|
||||
- *step-electron-maybe-chromedriver-gn-gen
|
||||
- *step-electron-chromedriver-build
|
||||
- *step-electron-chromedriver-store
|
||||
|
||||
@@ -1341,6 +1312,12 @@ commands:
|
||||
use-out-cache:
|
||||
type: boolean
|
||||
default: true
|
||||
restore-src-cache:
|
||||
type: boolean
|
||||
default: true
|
||||
preserve-vendor-dirs:
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- when:
|
||||
condition: << parameters.attach >>
|
||||
@@ -1366,17 +1343,38 @@ commands:
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-generate-deps-hash
|
||||
- *step-touch-sync-done
|
||||
- maybe-restore-portaled-src-cache
|
||||
- when:
|
||||
condition: << parameters.restore-src-cache >>
|
||||
steps:
|
||||
- maybe-restore-portaled-src-cache
|
||||
- *step-maybe-restore-git-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- when:
|
||||
condition: << parameters.preserve-vendor-dirs >>
|
||||
steps:
|
||||
- run:
|
||||
name: Preserve vendor dirs for release
|
||||
command: |
|
||||
mv src/electron/vendor/boto .
|
||||
mv src/electron/vendor/requests .
|
||||
- run:
|
||||
name: Wipe Electron
|
||||
command: rm -rf src/electron
|
||||
- *step-checkout-electron
|
||||
- *step-run-electron-only-hooks
|
||||
- when:
|
||||
condition: << parameters.preserve-vendor-dirs >>
|
||||
steps:
|
||||
- run:
|
||||
name: Preserve vendor dirs for release
|
||||
command: |
|
||||
rm -rf src/electron/vendor/boto
|
||||
rm -rf src/electron/vendor/requests
|
||||
mv boto src/electron/vendor
|
||||
mv requests src/electron/vendor/requests
|
||||
- *step-generate-deps-hash-cleanly
|
||||
- *step-mark-sync-done
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
@@ -1429,7 +1427,7 @@ commands:
|
||||
- *step-maybe-cross-arch-snapshot-store
|
||||
|
||||
# chromedriver
|
||||
- *step-electron-chromedriver-gn-gen
|
||||
- *step-electron-maybe-chromedriver-gn-gen
|
||||
- *step-electron-chromedriver-build
|
||||
- *step-electron-chromedriver-store
|
||||
|
||||
@@ -1475,10 +1473,80 @@ commands:
|
||||
steps:
|
||||
- *step-save-out-cache
|
||||
|
||||
# Trigger tests on arm hardware if needed
|
||||
- *step-maybe-trigger-arm-test
|
||||
# Trigger tests on arm hardware if needed
|
||||
- *step-maybe-trigger-arm-test
|
||||
|
||||
- *step-maybe-notify-slack-failure
|
||||
- *step-maybe-notify-slack-failure
|
||||
|
||||
electron-publish:
|
||||
parameters:
|
||||
attach:
|
||||
type: boolean
|
||||
default: false
|
||||
checkout:
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- when:
|
||||
condition: << parameters.attach >>
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- when:
|
||||
condition: << parameters.checkout >>
|
||||
steps:
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
- *step-get-more-space-on-mac
|
||||
- when:
|
||||
condition: << parameters.checkout >>
|
||||
steps:
|
||||
- *step-checkout-electron
|
||||
- *step-gclient-sync
|
||||
- *step-delete-git-directories
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
- *step-fix-sync-on-mac
|
||||
- *step-setup-env-for-build
|
||||
- *step-gn-gen-default
|
||||
|
||||
# Electron app
|
||||
- *step-electron-build
|
||||
- *step-show-sccache-stats
|
||||
- *step-maybe-generate-breakpad-symbols
|
||||
- *step-maybe-electron-dist-strip
|
||||
- *step-electron-dist-build
|
||||
- *step-electron-dist-store
|
||||
- *step-maybe-zip-symbols
|
||||
- *step-symbols-store
|
||||
|
||||
# mksnapshot
|
||||
- *step-mksnapshot-build
|
||||
- *step-mksnapshot-store
|
||||
|
||||
# chromedriver
|
||||
- *step-electron-maybe-chromedriver-gn-gen
|
||||
- *step-electron-chromedriver-build
|
||||
- *step-electron-chromedriver-store
|
||||
|
||||
# Node.js headers
|
||||
- *step-nodejs-headers-build
|
||||
- *step-nodejs-headers-store
|
||||
|
||||
# ffmpeg
|
||||
- *step-ffmpeg-gn-gen
|
||||
- *step-ffmpeg-build
|
||||
- *step-ffmpeg-store
|
||||
|
||||
# hunspell
|
||||
- *step-hunspell-build
|
||||
- *step-hunspell-store
|
||||
|
||||
# typescript defs
|
||||
- *step-maybe-generate-typescript-defs
|
||||
|
||||
# Publish
|
||||
- *step-electron-publish
|
||||
|
||||
# List of all jobs.
|
||||
jobs:
|
||||
@@ -1497,6 +1565,20 @@ jobs:
|
||||
<<: *steps-electron-ts-compile-for-doc-change
|
||||
|
||||
# Layer 1: Checkout.
|
||||
linux-checkout:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: false
|
||||
build: false
|
||||
checkout: true
|
||||
persist-checkout: true
|
||||
restore-src-cache: false
|
||||
preserve-vendor-dirs: true
|
||||
|
||||
linux-checkout-fast:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
@@ -1540,6 +1622,22 @@ jobs:
|
||||
checkout: true
|
||||
persist-checkout: true
|
||||
|
||||
mac-checkout:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-testing-build
|
||||
<<: *env-macos-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: false
|
||||
build: false
|
||||
checkout: true
|
||||
persist-checkout: true
|
||||
restore-src-cache: false
|
||||
preserve-vendor-dirs: true
|
||||
|
||||
mac-checkout-fast:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
@@ -1620,6 +1718,18 @@ jobs:
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-x64-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
linux-x64-publish-skip-checkout:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
@@ -1627,7 +1737,10 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
linux-ia32-testing:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1666,6 +1779,20 @@ jobs:
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-ia32-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-ia32
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-32bit-release
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
linux-ia32-publish-skip-checkout:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
@@ -1675,7 +1802,10 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-32bit-release
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
linux-arm-testing:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1715,6 +1845,20 @@ jobs:
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-arm-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
<<: *env-32bit-release
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
linux-arm-publish-skip-checkout:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
@@ -1724,7 +1868,10 @@ jobs:
|
||||
<<: *env-32bit-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
linux-arm64-testing:
|
||||
<<: *machine-linux-2xlarge
|
||||
@@ -1772,6 +1919,19 @@ jobs:
|
||||
<<: *steps-electron-build
|
||||
|
||||
linux-arm64-publish:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
linux-arm64-publish-skip-checkout:
|
||||
<<: *machine-linux-2xlarge
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
@@ -1780,7 +1940,10 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
osx-testing:
|
||||
<<: *machine-mac-large
|
||||
@@ -1824,6 +1987,18 @@ jobs:
|
||||
<<: *steps-electron-build
|
||||
|
||||
osx-publish:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
osx-publish-skip-checkout:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
@@ -1831,7 +2006,10 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
mas-testing:
|
||||
<<: *machine-mac-large
|
||||
@@ -1878,6 +2056,19 @@ jobs:
|
||||
<<: *steps-electron-build
|
||||
|
||||
mas-publish:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
<<: *env-enable-sccache
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: false
|
||||
checkout: true
|
||||
|
||||
mas-publish-skip-checkout:
|
||||
<<: *machine-mac-large
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
@@ -1886,7 +2077,10 @@ jobs:
|
||||
<<: *env-enable-sccache
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
<<: *steps-electron-build-for-publish
|
||||
steps:
|
||||
- electron-publish:
|
||||
attach: true
|
||||
checkout: false
|
||||
|
||||
# Layer 3: Tests.
|
||||
linux-x64-unittests:
|
||||
@@ -2180,6 +2374,27 @@ workflows:
|
||||
# will need to be updated and there will most likely need to be changes to
|
||||
# sudowoodo
|
||||
|
||||
publish-linux:
|
||||
when: << pipeline.parameters.run-linux-publish >>
|
||||
jobs:
|
||||
- linux-checkout
|
||||
- linux-x64-publish-skip-checkout:
|
||||
requires:
|
||||
- linux-checkout
|
||||
context: release-env
|
||||
- linux-ia32-publish-skip-checkout:
|
||||
requires:
|
||||
- linux-checkout
|
||||
context: release-env
|
||||
- linux-arm-publish-skip-checkout:
|
||||
requires:
|
||||
- linux-checkout
|
||||
context: release-env
|
||||
- linux-arm64-publish-skip-checkout:
|
||||
requires:
|
||||
- linux-checkout
|
||||
context: release-env
|
||||
|
||||
publish-x64-linux:
|
||||
when: << pipeline.parameters.run-linux-x64-publish >>
|
||||
jobs:
|
||||
@@ -2216,6 +2431,17 @@ workflows:
|
||||
- mas-publish:
|
||||
context: release-env
|
||||
|
||||
publish-macos:
|
||||
when: << pipeline.parameters.run-macos-publish >>
|
||||
jobs:
|
||||
- mac-checkout
|
||||
- osx-publish-skip-checkout:
|
||||
requires:
|
||||
- mac-checkout
|
||||
- mas-publish-skip-checkout:
|
||||
requires:
|
||||
- mac-checkout
|
||||
|
||||
lint:
|
||||
when: << pipeline.parameters.run-lint >>
|
||||
jobs:
|
||||
|
||||
14
BUILD.gn
14
BUILD.gn
@@ -629,8 +629,6 @@ source_set("electron_lib") {
|
||||
deps += [ "//components/printing/common:mojo_interfaces" ]
|
||||
}
|
||||
|
||||
deps += [ "shell/common/extensions/api:extensions_features" ]
|
||||
deps += [ "shell/common/extensions/api" ]
|
||||
deps += [
|
||||
"//components/pref_registry",
|
||||
"//components/user_prefs",
|
||||
@@ -642,12 +640,22 @@ source_set("electron_lib") {
|
||||
]
|
||||
if (enable_electron_extensions) {
|
||||
sources += filenames.lib_sources_extensions
|
||||
deps += [
|
||||
"shell/browser/extensions/api:api_registration",
|
||||
"shell/common/extensions/api",
|
||||
"shell/common/extensions/api:extensions_features",
|
||||
"//chrome/browser/resources:component_extension_resources",
|
||||
"//components/zoom",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_pdf) {
|
||||
# Printing depends on some //pdf code, so it needs to be built even if the
|
||||
# pdf viewer isn't enabled.
|
||||
deps += [ "//pdf" ]
|
||||
deps += [
|
||||
"//pdf",
|
||||
"//pdf:features",
|
||||
]
|
||||
}
|
||||
if (enable_pdf_viewer) {
|
||||
deps += [
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.0.0-nightly.20200205
|
||||
9.0.0-beta.2
|
||||
@@ -2,7 +2,7 @@ is_electron_build = true
|
||||
root_extra_deps = [ "//electron" ]
|
||||
|
||||
# Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json
|
||||
node_module_version = 76
|
||||
node_module_version = 80
|
||||
|
||||
v8_promise_internal_field_count = 1
|
||||
v8_typed_array_max_size_in_heap = 0
|
||||
|
||||
@@ -14,7 +14,7 @@ declare_args() {
|
||||
|
||||
enable_view_api = false
|
||||
|
||||
enable_pdf_viewer = false
|
||||
enable_pdf_viewer = true
|
||||
|
||||
enable_tts = true
|
||||
|
||||
|
||||
@@ -233,6 +233,12 @@ static_library("chrome") {
|
||||
|
||||
if (enable_electron_extensions) {
|
||||
sources += [
|
||||
"//chrome/browser/extensions/chrome_url_request_util.cc",
|
||||
"//chrome/browser/extensions/chrome_url_request_util.h",
|
||||
"//chrome/browser/pdf/pdf_extension_util.cc",
|
||||
"//chrome/browser/pdf/pdf_extension_util.h",
|
||||
"//chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc",
|
||||
"//chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h",
|
||||
"//chrome/renderer/extensions/extension_hooks_delegate.cc",
|
||||
"//chrome/renderer/extensions/extension_hooks_delegate.h",
|
||||
"//chrome/renderer/extensions/tabs_hooks_delegate.cc",
|
||||
|
||||
@@ -53,16 +53,23 @@ Unsupported options are:
|
||||
|
||||
### `GOOGLE_API_KEY`
|
||||
|
||||
You can provide an API key for making requests to Google's geocoding webservice. To do this, place the following code in your main process
|
||||
file, before opening any browser windows that will make geocoding requests:
|
||||
Geolocation support in Electron requires the use of Google Cloud Platform's
|
||||
geolocation webservice. To enable this feature, acquire a
|
||||
[Google API key](https://developers.google.com/maps/documentation/geolocation/get-api-key)
|
||||
and place the following code in your main process file, before opening any
|
||||
browser windows that will make geolocation requests:
|
||||
|
||||
```javascript
|
||||
process.env.GOOGLE_API_KEY = 'YOUR_KEY_HERE'
|
||||
```
|
||||
|
||||
For instructions on how to acquire a Google API key, visit [this page](https://developers.google.com/maps/documentation/javascript/get-api-key).
|
||||
By default, a newly generated Google API key may not be allowed to make
|
||||
geocoding requests. To enable geocoding requests, visit [this page](https://developers.google.com/maps/documentation/geocoding/get-api-key).
|
||||
By default, a newly generated Google API key may not be allowed to make geolocation requests.
|
||||
To enable the geolocation webservice for your project, enable it through the
|
||||
[API library](https://console.cloud.google.com/apis/library).
|
||||
|
||||
N.B. You will need to add a
|
||||
[Billing Account](https://cloud.google.com/billing/docs/how-to/payment-methods#add_a_payment_method)
|
||||
to the project associated to the API key for the geolocation webservice to work.
|
||||
|
||||
### `ELECTRON_NO_ASAR`
|
||||
|
||||
|
||||
@@ -487,6 +487,11 @@ to host here.
|
||||
|
||||
**Note:** On macOS the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS.
|
||||
|
||||
#### `ses.listWordsInSpellCheckerDictionary()`
|
||||
|
||||
Returns `Promise<String[]>` - An array of all words in app's custom dictionary.
|
||||
Resolves when the full dictionary is loaded from disk.
|
||||
|
||||
#### `ses.addWordToSpellCheckerDictionary(word)`
|
||||
|
||||
* `word` String - The word you want to add to the dictionary
|
||||
|
||||
@@ -146,6 +146,7 @@ response are visible by the time this listener is fired.
|
||||
* `timestamp` Double
|
||||
* `statusLine` String
|
||||
* `statusCode` Integer
|
||||
* `requestHeaders` Record<string, string>
|
||||
* `responseHeaders` Record<string, string[]> (optional)
|
||||
* `callback` Function
|
||||
* `headersReceivedResponse` Object
|
||||
@@ -228,6 +229,7 @@ redirect is about to occur.
|
||||
* `fromCache` Boolean
|
||||
* `statusCode` Integer
|
||||
* `statusLine` String
|
||||
* `error` String
|
||||
|
||||
The `listener` will be called with `listener(details)` when a request is
|
||||
completed.
|
||||
|
||||
@@ -92,10 +92,14 @@ template("electron_extra_paks") {
|
||||
}
|
||||
if (enable_electron_extensions) {
|
||||
sources += [
|
||||
"$root_gen_dir/chrome/component_extension_resources.pak",
|
||||
"$root_gen_dir/extensions/extensions_renderer_resources.pak",
|
||||
"$root_gen_dir/extensions/extensions_resources.pak",
|
||||
]
|
||||
deps += [ "//extensions:extensions_resources" ]
|
||||
deps += [
|
||||
"//chrome/browser/resources:component_extension_resources",
|
||||
"//extensions:extensions_resources",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
<part file="electron_strings.grdp" />
|
||||
</messages>
|
||||
<includes>
|
||||
<include name="IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE" file="${target_gen_dir}\shell_devtools_discovery_page.html" use_base_dir="false" type="BINDATA" />
|
||||
<include name="IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE" file="${target_gen_dir}/shell_devtools_discovery_page.html" use_base_dir="false" type="BINDATA" />
|
||||
<include name="IDR_PDF_MANIFEST" file="../chrome/browser/resources/pdf/manifest.json" type="BINDATA" />
|
||||
</includes>
|
||||
</release>
|
||||
</grit>
|
||||
|
||||
@@ -234,12 +234,15 @@ filenames = {
|
||||
"shell/browser/net/node_stream_loader.h",
|
||||
"shell/browser/net/proxying_url_loader_factory.cc",
|
||||
"shell/browser/net/proxying_url_loader_factory.h",
|
||||
"shell/browser/net/proxying_websocket.cc",
|
||||
"shell/browser/net/proxying_websocket.h",
|
||||
"shell/browser/net/resolve_proxy_helper.cc",
|
||||
"shell/browser/net/resolve_proxy_helper.h",
|
||||
"shell/browser/net/system_network_context_manager.cc",
|
||||
"shell/browser/net/system_network_context_manager.h",
|
||||
"shell/browser/net/url_pipe_loader.cc",
|
||||
"shell/browser/net/url_pipe_loader.h",
|
||||
"shell/browser/net/web_request_api_interface.h",
|
||||
"shell/browser/network_hints_handler_impl.cc",
|
||||
"shell/browser/network_hints_handler_impl.h",
|
||||
"shell/browser/node_debugger.cc",
|
||||
@@ -276,6 +279,8 @@ filenames = {
|
||||
"shell/browser/notifications/win/win32_notification.h",
|
||||
"shell/browser/notifications/win/windows_toast_notification.cc",
|
||||
"shell/browser/notifications/win/windows_toast_notification.h",
|
||||
"shell/browser/plugins/plugin_utils.cc",
|
||||
"shell/browser/plugins/plugin_utils.h",
|
||||
"shell/browser/pref_store_delegate.cc",
|
||||
"shell/browser/pref_store_delegate.h",
|
||||
"shell/browser/relauncher.cc",
|
||||
@@ -591,14 +596,18 @@ filenames = {
|
||||
]
|
||||
|
||||
lib_sources_extensions = [
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.cc",
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.cc",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.h",
|
||||
"shell/browser/extensions/electron_extensions_browser_client.cc",
|
||||
"shell/browser/extensions/electron_extensions_browser_client.h",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.cc",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.h",
|
||||
"shell/browser/extensions/electron_browser_context_keyed_service_factories.cc",
|
||||
"shell/browser/extensions/electron_browser_context_keyed_service_factories.h",
|
||||
"shell/browser/extensions/electron_component_extension_resource_manager.cc",
|
||||
"shell/browser/extensions/electron_component_extension_resource_manager.h",
|
||||
"shell/browser/extensions/electron_display_info_provider.cc",
|
||||
"shell/browser/extensions/electron_display_info_provider.h",
|
||||
"shell/browser/extensions/electron_extension_host_delegate.cc",
|
||||
@@ -611,24 +620,26 @@ filenames = {
|
||||
"shell/browser/extensions/electron_extension_system_factory.h",
|
||||
"shell/browser/extensions/electron_extension_web_contents_observer.cc",
|
||||
"shell/browser/extensions/electron_extension_web_contents_observer.h",
|
||||
"shell/browser/extensions/electron_navigation_ui_data.cc",
|
||||
"shell/browser/extensions/electron_navigation_ui_data.h",
|
||||
"shell/browser/extensions/electron_process_manager_delegate.cc",
|
||||
"shell/browser/extensions/electron_process_manager_delegate.h",
|
||||
"shell/browser/extensions/electron_extensions_api_client.cc",
|
||||
"shell/browser/extensions/electron_extensions_api_client.h",
|
||||
"shell/browser/extensions/electron_extensions_browser_api_provider.cc",
|
||||
"shell/browser/extensions/electron_extensions_browser_api_provider.h",
|
||||
"shell/browser/extensions/electron_extensions_browser_client.cc",
|
||||
"shell/browser/extensions/electron_extensions_browser_client.h",
|
||||
"shell/browser/extensions/electron_messaging_delegate.cc",
|
||||
"shell/browser/extensions/electron_messaging_delegate.h",
|
||||
"shell/browser/extensions/electron_navigation_ui_data.cc",
|
||||
"shell/browser/extensions/electron_navigation_ui_data.h",
|
||||
"shell/browser/extensions/electron_process_manager_delegate.cc",
|
||||
"shell/browser/extensions/electron_process_manager_delegate.h",
|
||||
"shell/common/extensions/electron_extensions_api_provider.cc",
|
||||
"shell/common/extensions/electron_extensions_api_provider.h",
|
||||
"shell/common/extensions/electron_extensions_client.cc",
|
||||
"shell/common/extensions/electron_extensions_client.h",
|
||||
"shell/renderer/extensions/electron_extensions_renderer_client.cc",
|
||||
"shell/renderer/extensions/electron_extensions_renderer_client.h",
|
||||
"shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc",
|
||||
"shell/renderer/extensions/electron_extensions_dispatcher_delegate.h",
|
||||
"shell/renderer/extensions/electron_extensions_renderer_client.cc",
|
||||
"shell/renderer/extensions/electron_extensions_renderer_client.h",
|
||||
]
|
||||
|
||||
app_sources = [
|
||||
|
||||
@@ -18,8 +18,10 @@ require('@electron/internal/common/init')
|
||||
if (process.platform === 'win32') {
|
||||
// Redirect node's console to use our own implementations, since node can not
|
||||
// handle console output when running as GUI program.
|
||||
const consoleLog = (format: any, ...args: any[]) => {
|
||||
return process.log(util.format(format, ...args) + '\n')
|
||||
const consoleLog = (...args: any[]) => {
|
||||
// @ts-ignore this typing is incorrect; 'format' is an optional parameter
|
||||
// See https://nodejs.org/api/util.html#util_util_format_format_args
|
||||
return process.log(util.format(...args) + '\n')
|
||||
}
|
||||
const streamWrite: NodeJS.WritableStream['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) {
|
||||
if (Buffer.isBuffer(chunk)) {
|
||||
|
||||
@@ -14,7 +14,6 @@ const remoteObjectCache = v8Util.createIDWeakMap()
|
||||
const contextId = v8Util.getHiddenValue(global, 'contextId')
|
||||
|
||||
ipcRendererInternal.invoke('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES').then(preferences => {
|
||||
console.log(preferences)
|
||||
if (!preferences.enableRemoteModule) {
|
||||
console.warn('%cElectron Deprecation Warning', 'font-weight: bold', "The 'remote' module is deprecated and will be disabled by default in a future version of Electron. To ensure a smooth upgrade and silence this warning, specify {enableRemoteModule: true} in the WebPreferences for this window.")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "9.0.0-nightly.20200205",
|
||||
"version": "9.0.0-beta.2",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -81,3 +81,5 @@ feat_allow_disabling_blink_scheduler_throttling_per_renderview.patch
|
||||
accessible_pane_view.patch
|
||||
fixme_grit_conflicts.patch
|
||||
fix_use_the_new_mediaplaypause_key_listener_for_internal_chrome.patch
|
||||
use_electron_resources_in_pdf_util.patch
|
||||
hack_plugin_response_interceptor_to_point_to_electron.patch
|
||||
|
||||
@@ -6,6 +6,18 @@ Subject: chore: use electron resources not chrome for spellchecker
|
||||
spellchecker uses a few IDS_ resources. We need to load these from
|
||||
Electrons grit header instead of Chromes
|
||||
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index d730660c086f..ba9b098d848e 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -5049,6 +5049,7 @@ jumbo_split_static_library("browser") {
|
||||
deps += [
|
||||
"//components/spellcheck/browser",
|
||||
"//components/spellcheck/common",
|
||||
+ "//electron:resources",
|
||||
]
|
||||
|
||||
if (!is_android) {
|
||||
diff --git a/chrome/browser/spellchecker/spellcheck_factory.cc b/chrome/browser/spellchecker/spellcheck_factory.cc
|
||||
index 48ac0a24efde0cb7d3ba71c8b8bdf5178f606e80..e2beefc276098fdc8f1cdab2e0edb8fae4ee67ca 100644
|
||||
--- a/chrome/browser/spellchecker/spellcheck_factory.cc
|
||||
@@ -19,6 +31,18 @@ index 48ac0a24efde0cb7d3ba71c8b8bdf5178f606e80..e2beefc276098fdc8f1cdab2e0edb8fa
|
||||
#include "components/keyed_service/content/browser_context_dependency_manager.h"
|
||||
#include "components/pref_registry/pref_registry_syncable.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
diff --git a/components/language/core/browser/BUILD.gn b/components/language/core/browser/BUILD.gn
|
||||
index f28230ea94fe..c10eea103852 100644
|
||||
--- a/components/language/core/browser/BUILD.gn
|
||||
+++ b/components/language/core/browser/BUILD.gn
|
||||
@@ -31,6 +31,7 @@ static_library("browser") {
|
||||
"//components/pref_registry",
|
||||
"//components/prefs",
|
||||
"//components/strings",
|
||||
+ "//electron:resources",
|
||||
"//ui/base",
|
||||
]
|
||||
}
|
||||
diff --git a/components/language/core/browser/language_prefs.cc b/components/language/core/browser/language_prefs.cc
|
||||
index 7e06f689632ae67e3e17d7985c6e7b6ea0057f3c..0b4676f4c40b4a0eda927a51016ec91ec230291d 100644
|
||||
--- a/components/language/core/browser/language_prefs.cc
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Apthorp <nornagon@nornagon.net>
|
||||
Date: Mon, 10 Feb 2020 11:52:23 -0800
|
||||
Subject: hack plugin response interceptor to point to electron
|
||||
|
||||
chrome's streams_private_api does prerender and other things and would
|
||||
require a largeish patch to get working, so just redirect it to our
|
||||
implementation instead.
|
||||
|
||||
diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
|
||||
index 1c9aeb8fa71d054159c2ccccccbe57aa474d417e..0bd6412f2c79f77f936720b1a0795b50ecefd26c 100644
|
||||
--- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
|
||||
+++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
|
||||
@@ -8,8 +8,8 @@
|
||||
#include "base/feature_list.h"
|
||||
#include "base/guid.h"
|
||||
#include "base/task/post_task.h"
|
||||
-#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
|
||||
-#include "chrome/browser/plugins/plugin_utils.h"
|
||||
+#include "electron/shell/browser/extensions/api/streams_private/streams_private_api.h"
|
||||
+#include "electron/shell/browser/plugins/plugin_utils.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/download_utils.h"
|
||||
22
patches/chromium/use_electron_resources_in_pdf_util.patch
Normal file
22
patches/chromium/use_electron_resources_in_pdf_util.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Apthorp <nornagon@nornagon.net>
|
||||
Date: Mon, 10 Feb 2020 10:37:48 -0800
|
||||
Subject: use electron resources in pdf_util
|
||||
|
||||
Without this, the ID for IDR_PDF_MANIFEST will be wrong on linux
|
||||
and cause a DCHECK(), since the resource will be loaded as an empty
|
||||
string.
|
||||
|
||||
diff --git a/chrome/browser/pdf/pdf_extension_util.cc b/chrome/browser/pdf/pdf_extension_util.cc
|
||||
index 877d0ba2f48359243527ddebf1d91132b3d5c455..cd69de498c4968ade0ffabf45b72d4d63b70b10f 100644
|
||||
--- a/chrome/browser/pdf/pdf_extension_util.cc
|
||||
+++ b/chrome/browser/pdf/pdf_extension_util.cc
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "base/strings/string_util.h"
|
||||
#include "chrome/common/chrome_content_client.h"
|
||||
-#include "chrome/grit/browser_resources.h"
|
||||
+#include "electron/grit/electron_resources.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
|
||||
namespace pdf_extension_util {
|
||||
@@ -9,7 +9,11 @@ async function checkIfDocOnlyChange () {
|
||||
try {
|
||||
let pullRequestNumber = args.prNumber
|
||||
if (!pullRequestNumber || isNaN(pullRequestNumber)) {
|
||||
if (args.prBranch) {
|
||||
if (args.prURL) {
|
||||
// CircleCI doesn't provide the PR number for branch builds, but it does provide the PR URL
|
||||
const pullRequestParts = args.prURL.split('/')
|
||||
pullRequestNumber = pullRequestParts[pullRequestParts.length - 1]
|
||||
} else if (args.prBranch) {
|
||||
// AppVeyor doesn't provide a PR number for branch builds - figure it out from the branch
|
||||
const prsForBranch = await octokit.pulls.list({
|
||||
owner: 'electron',
|
||||
@@ -23,10 +27,6 @@ async function checkIfDocOnlyChange () {
|
||||
// If there are 0 PRs or more than one PR on a branch, just assume that this is more than a doc change
|
||||
process.exit(1)
|
||||
}
|
||||
} else if (args.prURL) {
|
||||
// CircleCI doesn't provide the PR number for branch builds, but it does provide the PR URL
|
||||
const pullRequestParts = args.prURL.split('/')
|
||||
pullRequestNumber = pullRequestParts[pullRequestParts.length - 1]
|
||||
}
|
||||
}
|
||||
const filesChanged = await octokit.pulls.listFiles({
|
||||
|
||||
@@ -23,6 +23,11 @@ const circleCIJobs = [
|
||||
'osx-publish'
|
||||
]
|
||||
|
||||
const circleCIPublishWorkflows = [
|
||||
'linux-publish',
|
||||
'macos-publish'
|
||||
]
|
||||
|
||||
const vstsArmJobs = [
|
||||
'electron-arm-testing',
|
||||
'electron-arm64-testing',
|
||||
@@ -87,13 +92,18 @@ async function circleCIcall (targetBranch, job, options) {
|
||||
if (workflowId === -1) {
|
||||
return
|
||||
}
|
||||
console.log(`CircleCI release build workflow running at https://circleci.com/workflow-run/${workflowId} for ${job}.`)
|
||||
const jobNumber = await getCircleCIJobNumber(workflowId)
|
||||
if (jobNumber === -1) {
|
||||
return
|
||||
const workFlowUrl = `https://circleci.com/workflow-run/${workflowId}`
|
||||
if (options.runningPublishWorkflows) {
|
||||
console.log(`CircleCI release workflow request for ${job} successful. Check ${workFlowUrl} for status.`)
|
||||
} else {
|
||||
console.log(`CircleCI release build workflow running at https://circleci.com/workflow-run/${workflowId} for ${job}.`)
|
||||
const jobNumber = await getCircleCIJobNumber(workflowId)
|
||||
if (jobNumber === -1) {
|
||||
return
|
||||
}
|
||||
const jobUrl = `https://circleci.com/gh/electron/electron/${jobNumber}`
|
||||
console.log(`CircleCI release build request for ${job} successful. Check ${jobUrl} for status.`)
|
||||
}
|
||||
const jobUrl = `https://circleci.com/gh/electron/electron/${jobNumber}`
|
||||
console.log(`CircleCI release build request for ${job} successful. Check ${jobUrl} for status.`)
|
||||
} catch (err) {
|
||||
console.log('Error calling CircleCI: ', err)
|
||||
}
|
||||
@@ -232,7 +242,8 @@ function buildCircleCI (targetBranch, options) {
|
||||
assert(circleCIJobs.includes(options.job), `Unknown CircleCI job name: ${options.job}. Valid values are: ${circleCIJobs}.`)
|
||||
circleCIcall(targetBranch, options.job, options)
|
||||
} else {
|
||||
circleCIJobs.forEach((job) => circleCIcall(targetBranch, job, options))
|
||||
options.runningPublishWorkflows = true
|
||||
circleCIPublishWorkflows.forEach((job) => circleCIcall(targetBranch, job, options))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'
|
||||
|
||||
const runners = new Map([
|
||||
['main', { description: 'Main process specs', run: runMainProcessElectronTests }],
|
||||
['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }]
|
||||
// TODO(codebytere): refactor native tests to only depend on what we need
|
||||
/* ['native', { description: 'Native specs', run: runNativeElectronTests }] */
|
||||
['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }],
|
||||
['native', { description: 'Native specs', run: runNativeElectronTests }]
|
||||
])
|
||||
|
||||
const specHashPath = path.resolve(__dirname, '../spec/.hash')
|
||||
|
||||
@@ -4,7 +4,7 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from lib.config import LINUX_BINARIES
|
||||
from lib.config import LINUX_BINARIES, enable_verbose_mode
|
||||
from lib.util import execute, get_out_dir
|
||||
|
||||
def strip_binaries(directory, target_cpu):
|
||||
@@ -22,10 +22,12 @@ def strip_binary(binary_path, target_cpu):
|
||||
strip = 'mips64el-redhat-linux-strip'
|
||||
else:
|
||||
strip = 'strip'
|
||||
execute([strip, binary_path])
|
||||
execute([strip, '--preserve-dates', binary_path])
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
if args.verbose:
|
||||
enable_verbose_mode()
|
||||
if args.file:
|
||||
strip_binary(args.file, args.target_cpu)
|
||||
else:
|
||||
|
||||
@@ -33,12 +33,13 @@
|
||||
#endif // defined(WIDEVINE_CDM_AVAILABLE)
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
#include "pdf/pdf.h" // nogncheck
|
||||
#include "pdf/pdf_ppapi.h" // nogncheck
|
||||
#include "shell/common/electron_constants.h" // nogncheck
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
#include "pdf/pdf.h" // nogncheck
|
||||
#include "pdf/pdf_ppapi.h" // nogncheck
|
||||
#include "shell/common/electron_constants.h"
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
#include "content/public/browser/plugin_service.h"
|
||||
#include "content/public/common/pepper_plugin_info.h"
|
||||
#include "ppapi/shared_impl/ppapi_permissions.h"
|
||||
#endif // BUILDFLAG(ENABLE_PLUGINS)
|
||||
@@ -155,7 +156,8 @@ void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
|
||||
pdf_info.is_out_of_process = true;
|
||||
pdf_info.name = "Chromium PDF Viewer";
|
||||
pdf_info.description = "Portable Document Format";
|
||||
pdf_info.path = base::FilePath(FILE_PATH_LITERAL("internal-pdf-viewer"));
|
||||
// This isn't a real file path; it's just used as a unique identifier.
|
||||
pdf_info.path = base::FilePath(kPdfPluginPath);
|
||||
content::WebPluginMimeType pdf_mime_type(kPdfPluginMimeType, "pdf",
|
||||
"Portable Document Format");
|
||||
pdf_info.mime_types.push_back(pdf_mime_type);
|
||||
@@ -166,6 +168,21 @@ void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
|
||||
chrome_pdf::PPP_ShutdownModule;
|
||||
pdf_info.permissions = ppapi::PERMISSION_PDF | ppapi::PERMISSION_DEV;
|
||||
plugins->push_back(pdf_info);
|
||||
|
||||
// NB. in Chrome, this plugin isn't registered until the PDF extension is
|
||||
// loaded. However, in Electron, we load the PDF extension unconditionally
|
||||
// when it is enabled in the build, so we're OK to load the plugin eagerly
|
||||
// here.
|
||||
content::WebPluginInfo info;
|
||||
info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
|
||||
info.name = base::UTF8ToUTF16("Chromium PDF Viewer");
|
||||
// This isn't a real file path; it's just used as a unique identifier.
|
||||
info.path = base::FilePath::FromUTF8Unsafe(extension_misc::kPdfExtensionId);
|
||||
info.background_color = content::WebPluginInfo::kDefaultBackgroundColor;
|
||||
info.mime_types.emplace_back("application/pdf", "pdf",
|
||||
"Portable Document Format");
|
||||
content::PluginService::GetInstance()->RefreshPlugins();
|
||||
content::PluginService::GetInstance()->RegisterInternalPlugin(info, true);
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PLUGINS)
|
||||
|
||||
@@ -118,15 +118,6 @@ void LoadResourceBundle(const std::string& locale) {
|
||||
bundle.ReloadLocaleResources(locale);
|
||||
bundle.AddDataPackFromPath(pak_dir.Append(FILE_PATH_LITERAL("resources.pak")),
|
||||
ui::SCALE_FACTOR_NONE);
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
NOTIMPLEMENTED()
|
||||
<< "Hi, whoever's fixing PDF support! Thanks! The pdf "
|
||||
"viewer resources haven't been ported over to the GN build yet, so "
|
||||
"you'll probably need to change this bit of code.";
|
||||
bundle.AddDataPackFromPath(
|
||||
pak_dir.Append(FILE_PATH_LITERAL("pdf_viewer_resources.pak")),
|
||||
ui::GetSupportedScaleFactors()[0]);
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
}
|
||||
|
||||
ElectronMainDelegate::ElectronMainDelegate() = default;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -226,6 +227,42 @@ void DestroyGlobalHandle(v8::Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
class DictionaryObserver final : public SpellcheckCustomDictionary::Observer {
|
||||
private:
|
||||
std::unique_ptr<gin_helper::Promise<std::set<std::string>>> promise_;
|
||||
base::WeakPtr<SpellcheckService> spellcheck_;
|
||||
|
||||
public:
|
||||
DictionaryObserver(gin_helper::Promise<std::set<std::string>> promise,
|
||||
base::WeakPtr<SpellcheckService> spellcheck)
|
||||
: spellcheck_(spellcheck) {
|
||||
promise_ = std::make_unique<gin_helper::Promise<std::set<std::string>>>(
|
||||
std::move(promise));
|
||||
if (spellcheck_)
|
||||
spellcheck_->GetCustomDictionary()->AddObserver(this);
|
||||
}
|
||||
|
||||
~DictionaryObserver() {
|
||||
if (spellcheck_)
|
||||
spellcheck_->GetCustomDictionary()->RemoveObserver(this);
|
||||
}
|
||||
|
||||
void OnCustomDictionaryLoaded() override {
|
||||
if (spellcheck_) {
|
||||
promise_->Resolve(spellcheck_->GetCustomDictionary()->GetWords());
|
||||
} else {
|
||||
promise_->RejectWithErrorMessage(
|
||||
"Spellcheck in unexpected state: failed to load custom dictionary.");
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void OnCustomDictionaryChanged(
|
||||
const SpellcheckCustomDictionary::Change& dictionary_change) override {
|
||||
// noop
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
|
||||
@@ -660,7 +697,8 @@ v8::Local<v8::Value> Session::GetAllExtensions() {
|
||||
auto installed_extensions = registry->GenerateInstalledExtensionsSet();
|
||||
std::vector<const extensions::Extension*> extensions_vector;
|
||||
for (const auto& extension : *installed_extensions) {
|
||||
extensions_vector.emplace_back(extension.get());
|
||||
if (extension->location() != extensions::Manifest::COMPONENT)
|
||||
extensions_vector.emplace_back(extension.get());
|
||||
}
|
||||
return gin::ConvertToV8(isolate(), extensions_vector);
|
||||
}
|
||||
@@ -766,6 +804,29 @@ void SetSpellCheckerDictionaryDownloadURL(gin_helper::ErrorThrower thrower,
|
||||
SpellcheckHunspellDictionary::SetDownloadURLForTesting(url);
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> Session::ListWordsInSpellCheckerDictionary() {
|
||||
gin_helper::Promise<std::set<std::string>> promise(isolate());
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
SpellcheckService* spellcheck =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_.get());
|
||||
|
||||
if (!spellcheck)
|
||||
promise.RejectWithErrorMessage(
|
||||
"Spellcheck in unexpected state: failed to load custom dictionary.");
|
||||
|
||||
if (spellcheck->GetCustomDictionary()->IsLoaded()) {
|
||||
promise.Resolve(spellcheck->GetCustomDictionary()->GetWords());
|
||||
} else {
|
||||
new DictionaryObserver(std::move(promise), spellcheck->GetWeakPtr());
|
||||
// Dictionary loads by default asynchronously,
|
||||
// call the load function anyways just to be sure.
|
||||
spellcheck->GetCustomDictionary()->Load();
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool Session::AddWordToSpellCheckerDictionary(const std::string& word) {
|
||||
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
|
||||
if (spellcheck::UseBrowserSpellChecker()) {
|
||||
@@ -865,6 +926,8 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
||||
&spellcheck::SpellCheckLanguages)
|
||||
.SetMethod("setSpellCheckerDictionaryDownloadURL",
|
||||
&SetSpellCheckerDictionaryDownloadURL)
|
||||
.SetMethod("listWordsInSpellCheckerDictionary",
|
||||
&Session::ListWordsInSpellCheckerDictionary)
|
||||
.SetMethod("addWordToSpellCheckerDictionary",
|
||||
&Session::AddWordToSpellCheckerDictionary)
|
||||
#endif
|
||||
|
||||
@@ -95,6 +95,7 @@ class Session : public gin_helper::TrackableObject<Session>,
|
||||
base::Value GetSpellCheckerLanguages();
|
||||
void SetSpellCheckerLanguages(gin_helper::ErrorThrower thrower,
|
||||
const std::vector<std::string>& languages);
|
||||
v8::Local<v8::Promise> ListWordsInSpellCheckerDictionary();
|
||||
bool AddWordToSpellCheckerDictionary(const std::string& word);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1803,18 +1803,21 @@ void WebContents::Print(gin_helper::Arguments* args) {
|
||||
settings.SetIntKey(printing::kSettingMarginsType, margin_type);
|
||||
|
||||
if (margin_type == printing::CUSTOM_MARGINS) {
|
||||
base::Value custom_margins(base::Value::Type::DICTIONARY);
|
||||
int top = 0;
|
||||
margins.Get("top", &top);
|
||||
settings.SetIntKey(printing::kSettingMarginTop, top);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginTop, top);
|
||||
int bottom = 0;
|
||||
margins.Get("bottom", &bottom);
|
||||
settings.SetIntKey(printing::kSettingMarginBottom, bottom);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginBottom, bottom);
|
||||
int left = 0;
|
||||
margins.Get("left", &left);
|
||||
settings.SetIntKey(printing::kSettingMarginLeft, left);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginLeft, left);
|
||||
int right = 0;
|
||||
margins.Get("right", &right);
|
||||
settings.SetIntKey(printing::kSettingMarginRight, right);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginRight, right);
|
||||
settings.SetPath(printing::kSettingMarginsCustom,
|
||||
std::move(custom_margins));
|
||||
}
|
||||
} else {
|
||||
settings.SetIntKey(printing::kSettingMarginsType,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "gin/arguments.h"
|
||||
#include "gin/handle.h"
|
||||
#include "gin/wrappable.h"
|
||||
#include "shell/browser/net/proxying_url_loader_factory.h"
|
||||
#include "shell/browser/net/web_request_api_interface.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
@@ -49,10 +49,6 @@ class WebRequest : public gin::Wrappable<WebRequest>, public WebRequestAPI {
|
||||
v8::Isolate* isolate) override;
|
||||
const char* GetTypeName() override;
|
||||
|
||||
private:
|
||||
WebRequest(v8::Isolate* isolate, content::BrowserContext* browser_context);
|
||||
~WebRequest() override;
|
||||
|
||||
// WebRequestAPI:
|
||||
bool HasListener() const override;
|
||||
int OnBeforeRequest(extensions::WebRequestInfo* info,
|
||||
@@ -86,6 +82,10 @@ class WebRequest : public gin::Wrappable<WebRequest>, public WebRequestAPI {
|
||||
int net_error) override;
|
||||
void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) override;
|
||||
|
||||
private:
|
||||
WebRequest(v8::Isolate* isolate, content::BrowserContext* browser_context);
|
||||
~WebRequest() override;
|
||||
|
||||
enum SimpleEvent {
|
||||
kOnSendHeaders,
|
||||
kOnBeforeRedirect,
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "extensions/common/constants.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "ppapi/host/ppapi_host.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "services/device/public/cpp/geolocation/location_provider.h"
|
||||
@@ -75,6 +76,7 @@
|
||||
#include "shell/browser/net/network_context_service.h"
|
||||
#include "shell/browser/net/network_context_service_factory.h"
|
||||
#include "shell/browser/net/proxying_url_loader_factory.h"
|
||||
#include "shell/browser/net/proxying_websocket.h"
|
||||
#include "shell/browser/net/system_network_context_manager.h"
|
||||
#include "shell/browser/network_hints_handler_impl.h"
|
||||
#include "shell/browser/notifications/notification_presenter.h"
|
||||
@@ -128,13 +130,29 @@
|
||||
#endif // BUILDFLAG(ENABLE_PRINTING)
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "chrome/common/webui_url_constants.h"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/file_url_loader.h"
|
||||
#include "content/public/browser/web_ui_url_loader_factory.h"
|
||||
#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
|
||||
#include "extensions/browser/extension_host.h"
|
||||
#include "extensions/browser/extension_message_filter.h"
|
||||
#include "extensions/browser/extension_navigation_throttle.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
|
||||
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
|
||||
#include "extensions/browser/info_map.h"
|
||||
#include "extensions/browser/process_manager.h"
|
||||
#include "extensions/browser/process_map.h"
|
||||
#include "extensions/common/api/mime_handler.mojom.h"
|
||||
#include "extensions/common/extension.h"
|
||||
#include "shell/browser/extensions/electron_extension_system.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
#include "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h" // nogncheck
|
||||
#include "shell/browser/plugins/plugin_utils.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
@@ -385,6 +403,8 @@ void ElectronBrowserClient::RenderProcessWillLaunch(
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
host->AddFilter(
|
||||
new extensions::ExtensionMessageFilter(process_id, browser_context));
|
||||
host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(
|
||||
process_id, browser_context));
|
||||
#endif
|
||||
|
||||
ProcessPreferences prefs;
|
||||
@@ -727,6 +747,7 @@ void ElectronBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
|
||||
additional_schemes->insert(additional_schemes->end(), schemes_list.begin(),
|
||||
schemes_list.end());
|
||||
additional_schemes->push_back(content::kChromeDevToolsScheme);
|
||||
additional_schemes->push_back(content::kChromeUIScheme);
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::GetAdditionalWebUISchemes(
|
||||
@@ -1052,28 +1073,160 @@ void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
|
||||
protocol->RegisterURLLoaderFactories(factories);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
namespace {
|
||||
|
||||
// The FileURLLoaderFactory provided to the extension background pages.
|
||||
// Checks with the ChildProcessSecurityPolicy to validate the file access.
|
||||
class FileURLLoaderFactory : public network::mojom::URLLoaderFactory {
|
||||
public:
|
||||
explicit FileURLLoaderFactory(int child_id) : child_id_(child_id) {}
|
||||
|
||||
private:
|
||||
// network::mojom::URLLoaderFactory:
|
||||
void CreateLoaderAndStart(
|
||||
mojo::PendingReceiver<network::mojom::URLLoader> loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
|
||||
override {
|
||||
if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL(
|
||||
child_id_, request.url)) {
|
||||
mojo::Remote<network::mojom::URLLoaderClient>(std::move(client))
|
||||
->OnComplete(
|
||||
network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
|
||||
return;
|
||||
}
|
||||
content::CreateFileURLLoaderBypassingSecurityChecks(
|
||||
request, std::move(loader), std::move(client),
|
||||
/*observer=*/nullptr,
|
||||
/* allow_directory_listing */ true);
|
||||
}
|
||||
|
||||
void Clone(
|
||||
mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader) override {
|
||||
receivers_.Add(this, std::move(loader));
|
||||
}
|
||||
|
||||
int child_id_;
|
||||
mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
|
||||
DISALLOW_COPY_AND_ASSIGN(FileURLLoaderFactory);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
|
||||
int render_process_id,
|
||||
int render_frame_id,
|
||||
NonNetworkURLLoaderFactoryMap* factories) {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
|
||||
render_frame_id);
|
||||
if (factory)
|
||||
factories->emplace(extensions::kExtensionScheme, std::move(factory));
|
||||
#endif
|
||||
|
||||
// Chromium may call this even when NetworkService is not enabled.
|
||||
content::RenderFrameHost* frame_host =
|
||||
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
|
||||
content::WebContents* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(frame_host);
|
||||
|
||||
if (web_contents) {
|
||||
api::Protocol* protocol = api::Protocol::FromWrappedClass(
|
||||
v8::Isolate::GetCurrent(), web_contents->GetBrowserContext());
|
||||
if (protocol)
|
||||
protocol->RegisterURLLoaderFactories(factories);
|
||||
}
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
|
||||
render_frame_id);
|
||||
if (factory)
|
||||
factories->emplace(extensions::kExtensionScheme, std::move(factory));
|
||||
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
extensions::ElectronExtensionWebContentsObserver* web_observer =
|
||||
extensions::ElectronExtensionWebContentsObserver::FromWebContents(
|
||||
web_contents);
|
||||
|
||||
// There is nothing to do if no ElectronExtensionWebContentsObserver is
|
||||
// attached to the |web_contents|.
|
||||
if (!web_observer)
|
||||
return;
|
||||
|
||||
const extensions::Extension* extension =
|
||||
web_observer->GetExtensionFromFrame(frame_host, false);
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
// Support for chrome:// scheme if appropriate.
|
||||
if (extension->is_extension() &&
|
||||
extensions::Manifest::IsComponentLocation(extension->location())) {
|
||||
// Components of chrome that are implemented as extensions or platform apps
|
||||
// are allowed to use chrome://resources/ and chrome://theme/ URLs.
|
||||
factories->emplace(
|
||||
content::kChromeUIScheme,
|
||||
content::CreateWebUIURLLoader(frame_host, content::kChromeUIScheme,
|
||||
{content::kChromeUIResourcesHost}));
|
||||
}
|
||||
|
||||
// Extension with a background page get file access that gets approval from
|
||||
// ChildProcessSecurityPolicy.
|
||||
extensions::ExtensionHost* host =
|
||||
extensions::ProcessManager::Get(web_contents->GetBrowserContext())
|
||||
->GetBackgroundHostForExtension(extension->id());
|
||||
if (host) {
|
||||
factories->emplace(url::kFileScheme, std::make_unique<FileURLLoaderFactory>(
|
||||
render_process_id));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
|
||||
base::StringPiece scheme,
|
||||
bool is_embedded_origin_secure) {
|
||||
if (is_embedded_origin_secure && scheme == content::kChromeUIScheme)
|
||||
return true;
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return scheme == extensions::kExtensionScheme;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::WillInterceptWebSocket(
|
||||
content::RenderFrameHost* frame) {
|
||||
if (!frame)
|
||||
return false;
|
||||
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
auto* browser_context = frame->GetProcess()->GetBrowserContext();
|
||||
auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context);
|
||||
|
||||
// NOTE: Some unit test environments do not initialize
|
||||
// BrowserContextKeyedAPI factories for e.g. WebRequest.
|
||||
if (!web_request.get())
|
||||
return false;
|
||||
|
||||
return web_request->HasListener();
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::CreateWebSocket(
|
||||
content::RenderFrameHost* frame,
|
||||
WebSocketFactory factory,
|
||||
const GURL& url,
|
||||
const net::SiteForCookies& site_for_cookies,
|
||||
const base::Optional<std::string>& user_agent,
|
||||
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
|
||||
handshake_client) {
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
auto* browser_context = frame->GetProcess()->GetBrowserContext();
|
||||
auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context);
|
||||
DCHECK(web_request.get());
|
||||
ProxyingWebSocket::StartProxying(
|
||||
web_request.get(), std::move(factory), url,
|
||||
site_for_cookies.RepresentativeUrl(), user_agent,
|
||||
std::move(handshake_client), true, frame->GetProcess()->GetID(),
|
||||
frame->GetRoutingID(), frame->GetLastCommittedOrigin(), browser_context,
|
||||
&next_id_);
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::WillCreateURLLoaderFactory(
|
||||
@@ -1118,7 +1271,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory(
|
||||
|
||||
new ProxyingURLLoaderFactory(
|
||||
web_request.get(), protocol->intercept_handlers(), browser_context,
|
||||
render_process_id, std::move(navigation_ui_data),
|
||||
render_process_id, &next_id_, std::move(navigation_ui_data),
|
||||
std::move(navigation_id), std::move(proxied_receiver),
|
||||
std::move(target_factory_remote), std::move(header_client_receiver),
|
||||
type);
|
||||
@@ -1202,11 +1355,31 @@ void ElectronBrowserClient::BindHostReceiverForRenderer(
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
void BindMimeHandlerService(
|
||||
content::RenderFrameHost* frame_host,
|
||||
mojo::PendingReceiver<extensions::mime_handler::MimeHandlerService>
|
||||
receiver) {
|
||||
content::WebContents* contents =
|
||||
content::WebContents::FromRenderFrameHost(frame_host);
|
||||
auto* guest_view =
|
||||
extensions::MimeHandlerViewGuest::FromWebContents(contents);
|
||||
if (!guest_view)
|
||||
return;
|
||||
extensions::MimeHandlerServiceImpl::Create(guest_view->GetStreamWeakPtr(),
|
||||
std::move(receiver));
|
||||
}
|
||||
#endif
|
||||
|
||||
void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
service_manager::BinderMapWithContext<content::RenderFrameHost*>* map) {
|
||||
map->Add<network_hints::mojom::NetworkHintsHandler>(
|
||||
base::BindRepeating(&BindNetworkHintsHandler));
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
map->Add<extensions::mime_handler::MimeHandlerService>(
|
||||
base::BindRepeating(&BindMimeHandlerService));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<content::LoginDelegate>
|
||||
@@ -1224,4 +1397,35 @@ ElectronBrowserClient::CreateLoginDelegate(
|
||||
first_auth_attempt, std::move(auth_required_callback));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
|
||||
ElectronBrowserClient::CreateURLLoaderThrottles(
|
||||
const network::ResourceRequest& request,
|
||||
content::BrowserContext* browser_context,
|
||||
const base::RepeatingCallback<content::WebContents*()>& wc_getter,
|
||||
content::NavigationUIData* navigation_ui_data,
|
||||
int frame_tree_node_id) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
result.push_back(std::make_unique<PluginResponseInterceptorURLLoaderThrottle>(
|
||||
request.resource_type, frame_tree_node_id));
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
base::flat_set<std::string>
|
||||
ElectronBrowserClient::GetPluginMimeTypesWithExternalHandlers(
|
||||
content::BrowserContext* browser_context) {
|
||||
base::flat_set<std::string> mime_types;
|
||||
#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
auto map = PluginUtils::GetMimeTypeToExtensionIdMap(browser_context);
|
||||
for (const auto& pair : map)
|
||||
mime_types.insert(pair.first);
|
||||
#endif
|
||||
return mime_types;
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -173,6 +173,15 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
int render_process_id,
|
||||
int render_frame_id,
|
||||
NonNetworkURLLoaderFactoryMap* factories) override;
|
||||
void CreateWebSocket(
|
||||
content::RenderFrameHost* frame,
|
||||
WebSocketFactory factory,
|
||||
const GURL& url,
|
||||
const net::SiteForCookies& site_for_cookies,
|
||||
const base::Optional<std::string>& user_agent,
|
||||
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
|
||||
handshake_client) override;
|
||||
bool WillInterceptWebSocket(content::RenderFrameHost*) override;
|
||||
bool WillCreateURLLoaderFactory(
|
||||
content::BrowserContext* browser_context,
|
||||
content::RenderFrameHost* frame,
|
||||
@@ -186,6 +195,9 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
bool* bypass_redirect_checks,
|
||||
bool* disable_secure_dns,
|
||||
network::mojom::URLLoaderFactoryOverridePtr* factory_override) override;
|
||||
bool ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
|
||||
base::StringPiece scheme,
|
||||
bool is_embedded_origin_secure) override;
|
||||
void OverrideURLLoaderFactoryParams(
|
||||
content::BrowserContext* browser_context,
|
||||
const url::Origin& origin,
|
||||
@@ -221,6 +233,15 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
bool first_auth_attempt,
|
||||
LoginAuthRequiredCallback auth_required_callback) override;
|
||||
void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
|
||||
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
|
||||
CreateURLLoaderThrottles(
|
||||
const network::ResourceRequest& request,
|
||||
content::BrowserContext* browser_context,
|
||||
const base::RepeatingCallback<content::WebContents*()>& wc_getter,
|
||||
content::NavigationUIData* navigation_ui_data,
|
||||
int frame_tree_node_id) override;
|
||||
base::flat_set<std::string> GetPluginMimeTypesWithExternalHandlers(
|
||||
content::BrowserContext* browser_context) override;
|
||||
|
||||
// content::RenderProcessHostObserver:
|
||||
void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
|
||||
@@ -286,6 +307,10 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
|
||||
bool disable_process_restart_tricks_ = false;
|
||||
|
||||
// Simple shared ID generator, used by ProxyingURLLoaderFactory and
|
||||
// ProxyingWebSocket classes.
|
||||
uint64_t next_id_ = 0;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ElectronBrowserClient);
|
||||
};
|
||||
|
||||
|
||||
35
shell/browser/extensions/api/BUILD.gn
Normal file
35
shell/browser/extensions/api/BUILD.gn
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//extensions/buildflags/buildflags.gni")
|
||||
import("//tools/json_schema_compiler/json_schema_api.gni")
|
||||
|
||||
assert(enable_extensions,
|
||||
"Cannot depend on extensions because enable_extensions=false.")
|
||||
|
||||
function_registration("api_registration") {
|
||||
sources = [
|
||||
"//electron/shell/common/extensions/api/extension.json",
|
||||
"//electron/shell/common/extensions/api/resources_private.idl",
|
||||
"//electron/shell/common/extensions/api/tabs.json",
|
||||
]
|
||||
impl_dir = "//electron/shell/browser/extensions/api"
|
||||
configs = [ "//build/config:precompiled_headers" ]
|
||||
bundle_name = "Electron"
|
||||
root_namespace = "extensions::api::%(namespace)s"
|
||||
schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s"
|
||||
|
||||
deps = [
|
||||
# Different APIs include headers from these targets.
|
||||
"//components/zoom",
|
||||
"//content/public/browser",
|
||||
"//extensions/browser",
|
||||
|
||||
# Different APIs include some headers from chrome/common that in turn
|
||||
# include generated headers from these targets.
|
||||
# TODO(brettw) this should be made unnecessary if possible.
|
||||
"//electron/shell/common/extensions/api",
|
||||
]
|
||||
deps += [ "//extensions/common/api" ]
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/extensions/api/resources_private/resources_private_api.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/values.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/common/extensions/api/resources_private.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
#include "components/zoom/page_zoom_constants.h"
|
||||
#include "pdf/buildflags.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/webui/web_ui_util.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
#include "pdf/pdf_features.h"
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
|
||||
// To add a new component to this API, simply:
|
||||
// 1. Add your component to the Component enum in
|
||||
// chrome/common/extensions/api/resources_private.idl
|
||||
// 2. Create an AddStringsForMyComponent(base::DictionaryValue * dict) method.
|
||||
// 3. Tie in that method to the switch statement in Run()
|
||||
|
||||
namespace extensions {
|
||||
|
||||
namespace {
|
||||
|
||||
void AddStringsForPdf(base::DictionaryValue* dict) {
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
static constexpr webui::LocalizedString kPdfResources[] = {
|
||||
{"passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE},
|
||||
{"passwordPrompt", IDS_PDF_NEED_PASSWORD},
|
||||
{"passwordSubmit", IDS_PDF_PASSWORD_SUBMIT},
|
||||
{"passwordInvalid", IDS_PDF_PASSWORD_INVALID},
|
||||
{"pageLoading", IDS_PDF_PAGE_LOADING},
|
||||
{"pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED},
|
||||
{"errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE},
|
||||
{"pageReload", IDS_PDF_PAGE_RELOAD_BUTTON},
|
||||
{"bookmarks", IDS_PDF_BOOKMARKS},
|
||||
{"labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER},
|
||||
{"tooltipRotateCW", IDS_PDF_TOOLTIP_ROTATE_CW},
|
||||
{"tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD},
|
||||
{"tooltipPrint", IDS_PDF_TOOLTIP_PRINT},
|
||||
{"tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE},
|
||||
{"tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH},
|
||||
{"tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN},
|
||||
{"tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT},
|
||||
};
|
||||
for (const auto& resource : kPdfResources)
|
||||
dict->SetString(resource.name, l10n_util::GetStringUTF16(resource.id));
|
||||
|
||||
dict->SetString("presetZoomFactors", zoom::GetPresetZoomFactorsAsJSON());
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
}
|
||||
|
||||
void AddAdditionalDataForPdf(base::DictionaryValue* dict) {
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
dict->SetKey("pdfFormSaveEnabled",
|
||||
base::Value(base::FeatureList::IsEnabled(
|
||||
chrome_pdf::features::kSaveEditedPDFForm)));
|
||||
dict->SetKey("pdfAnnotationsEnabled",
|
||||
base::Value(base::FeatureList::IsEnabled(
|
||||
chrome_pdf::features::kPDFAnnotations)));
|
||||
|
||||
// TODO(nornagon): enable printing once it works.
|
||||
bool enable_printing = false;
|
||||
dict->SetKey("printingEnabled", base::Value(enable_printing));
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace get_strings = api::resources_private::GetStrings;
|
||||
|
||||
ResourcesPrivateGetStringsFunction::ResourcesPrivateGetStringsFunction() {}
|
||||
|
||||
ResourcesPrivateGetStringsFunction::~ResourcesPrivateGetStringsFunction() {}
|
||||
|
||||
ExtensionFunction::ResponseAction ResourcesPrivateGetStringsFunction::Run() {
|
||||
std::unique_ptr<get_strings::Params> params(
|
||||
get_strings::Params::Create(*args_));
|
||||
auto dict = std::make_unique<base::DictionaryValue>();
|
||||
|
||||
api::resources_private::Component component = params->component;
|
||||
|
||||
switch (component) {
|
||||
case api::resources_private::COMPONENT_PDF:
|
||||
AddStringsForPdf(dict.get());
|
||||
AddAdditionalDataForPdf(dict.get());
|
||||
break;
|
||||
case api::resources_private::COMPONENT_IDENTITY:
|
||||
NOTREACHED();
|
||||
break;
|
||||
case api::resources_private::COMPONENT_NONE:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string& app_locale = g_browser_process->GetApplicationLocale();
|
||||
webui::SetLoadTimeDataDefaults(app_locale, dict.get());
|
||||
|
||||
return RespondNow(OneArgument(std::move(dict)));
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "extensions/browser/extension_function.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
class ResourcesPrivateGetStringsFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("resourcesPrivate.getStrings",
|
||||
RESOURCESPRIVATE_GETSTRINGS)
|
||||
ResourcesPrivateGetStringsFunction();
|
||||
|
||||
protected:
|
||||
~ResourcesPrivateGetStringsFunction() override;
|
||||
|
||||
// Override from ExtensionFunction:
|
||||
ExtensionFunction::ResponseAction Run() override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ResourcesPrivateGetStringsFunction);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "electron/shell/browser/extensions/api/streams_private/streams_private_api.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
|
||||
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
|
||||
#include "extensions/common/manifest_handlers/mime_types_handler.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
|
||||
const std::string& extension_id,
|
||||
const std::string& view_id,
|
||||
bool embedded,
|
||||
int frame_tree_node_id,
|
||||
int render_process_id,
|
||||
int render_frame_id,
|
||||
content::mojom::TransferrableURLLoaderPtr transferrable_loader,
|
||||
const GURL& original_url) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
content::WebContents* web_contents = nullptr;
|
||||
if (frame_tree_node_id != -1) {
|
||||
web_contents =
|
||||
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
|
||||
} else {
|
||||
web_contents = content::WebContents::FromRenderFrameHost(
|
||||
content::RenderFrameHost::FromID(render_process_id, render_frame_id));
|
||||
}
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
auto* browser_context = web_contents->GetBrowserContext();
|
||||
|
||||
const extensions::Extension* extension =
|
||||
extensions::ExtensionRegistry::Get(browser_context)
|
||||
->enabled_extensions()
|
||||
.GetByID(extension_id);
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
|
||||
if (!handler->HasPlugin())
|
||||
return;
|
||||
|
||||
// If the mime handler uses MimeHandlerViewGuest, the MimeHandlerViewGuest
|
||||
// will take ownership of the stream.
|
||||
GURL handler_url(
|
||||
extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() +
|
||||
handler->handler_url());
|
||||
int tab_id = -1;
|
||||
auto* api_contents = electron::api::WebContents::FromWrappedClass(
|
||||
v8::Isolate::GetCurrent(), web_contents);
|
||||
if (api_contents)
|
||||
tab_id = api_contents->ID();
|
||||
std::unique_ptr<extensions::StreamContainer> stream_container(
|
||||
new extensions::StreamContainer(
|
||||
tab_id, embedded, handler_url, extension_id,
|
||||
std::move(transferrable_loader), original_url));
|
||||
extensions::MimeHandlerStreamManager::Get(browser_context)
|
||||
->AddStream(view_id, std::move(stream_container), frame_tree_node_id,
|
||||
render_process_id, render_frame_id);
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "content/public/common/transferrable_url_loader.mojom.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// TODO(devlin): This is now only used for the MimeTypesHandler API. We should
|
||||
// rename and move it to make that clear. https://crbug.com/890401.
|
||||
class StreamsPrivateAPI {
|
||||
public:
|
||||
// Send the onExecuteMimeTypeHandler event to |extension_id|. If the viewer is
|
||||
// being opened in a BrowserPlugin, specify a non-empty |view_id| of the
|
||||
// plugin. |embedded| should be set to whether the document is embedded
|
||||
// within another document. The |frame_tree_node_id| parameter is used for the
|
||||
// top level plugins case. (PDF, etc). If this parameter has a valid value
|
||||
// then it overrides the |render_process_id| and |render_frame_id| parameters.
|
||||
// The |render_process_id| is the id of the renderer process. The
|
||||
// |render_frame_id| is the routing id of the RenderFrameHost.
|
||||
//
|
||||
// If the network service is not enabled, |stream| is used; otherwise,
|
||||
// |transferrable_loader| and |original_url| are used instead.
|
||||
static void SendExecuteMimeTypeHandlerEvent(
|
||||
const std::string& extension_id,
|
||||
const std::string& view_id,
|
||||
bool embedded,
|
||||
int frame_tree_node_id,
|
||||
int render_process_id,
|
||||
int render_frame_id,
|
||||
content::mojom::TransferrableURLLoaderPtr transferrable_loader,
|
||||
const GURL& original_url);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_
|
||||
@@ -12,13 +12,48 @@
|
||||
#include "extensions/common/manifest_constants.h"
|
||||
#include "extensions/common/permissions/permissions_data.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/web_contents_zoom_controller.h"
|
||||
#include "shell/common/extensions/api/tabs.h"
|
||||
#include "third_party/blink/public/common/page/page_zoom.h"
|
||||
|
||||
using electron::WebContentsZoomController;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
namespace tabs = api::tabs;
|
||||
|
||||
const char kFrameNotFoundError[] = "No frame with id * in tab *.";
|
||||
const char kPerOriginOnlyInAutomaticError[] =
|
||||
"Can only set scope to "
|
||||
"\"per-origin\" in \"automatic\" mode.";
|
||||
|
||||
using api::extension_types::InjectDetails;
|
||||
|
||||
namespace {
|
||||
void ZoomModeToZoomSettings(WebContentsZoomController::ZoomMode zoom_mode,
|
||||
api::tabs::ZoomSettings* zoom_settings) {
|
||||
DCHECK(zoom_settings);
|
||||
switch (zoom_mode) {
|
||||
case WebContentsZoomController::ZoomMode::DEFAULT:
|
||||
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
|
||||
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN;
|
||||
break;
|
||||
case WebContentsZoomController::ZoomMode::ISOLATED:
|
||||
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
|
||||
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
|
||||
break;
|
||||
case WebContentsZoomController::ZoomMode::MANUAL:
|
||||
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL;
|
||||
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
|
||||
break;
|
||||
case WebContentsZoomController::ZoomMode::DISABLED:
|
||||
zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED;
|
||||
zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : execute_tab_id_(-1) {}
|
||||
|
||||
ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
|
||||
@@ -130,4 +165,149 @@ bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction TabsGetFunction::Run() {
|
||||
std::unique_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
|
||||
EXTENSION_FUNCTION_VALIDATE(params.get());
|
||||
int tab_id = params->tab_id;
|
||||
|
||||
auto* contents = electron::api::WebContents::FromWeakMapID(
|
||||
v8::Isolate::GetCurrent(), tab_id);
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
tabs::Tab tab;
|
||||
|
||||
tab.id.reset(new int(tab_id));
|
||||
// TODO(nornagon): in Chrome, the tab URL is only available to extensions
|
||||
// that have the "tabs" (or "activeTab") permission. We should do the same
|
||||
// permission check here.
|
||||
tab.url = std::make_unique<std::string>(
|
||||
contents->web_contents()->GetLastCommittedURL().spec());
|
||||
|
||||
return RespondNow(ArgumentList(tabs::Get::Results::Create(std::move(tab))));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction TabsSetZoomFunction::Run() {
|
||||
std::unique_ptr<tabs::SetZoom::Params> params(
|
||||
tabs::SetZoom::Params::Create(*args_));
|
||||
EXTENSION_FUNCTION_VALIDATE(params);
|
||||
|
||||
int tab_id = params->tab_id ? *params->tab_id : -1;
|
||||
auto* contents = electron::api::WebContents::FromWeakMapID(
|
||||
v8::Isolate::GetCurrent(), tab_id);
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
auto* web_contents = contents->web_contents();
|
||||
GURL url(web_contents->GetVisibleURL());
|
||||
std::string error;
|
||||
if (extension()->permissions_data()->IsRestrictedUrl(url, &error))
|
||||
return RespondNow(Error(error));
|
||||
|
||||
auto* zoom_controller = contents->GetZoomController();
|
||||
double zoom_level =
|
||||
params->zoom_factor > 0
|
||||
? blink::PageZoomFactorToZoomLevel(params->zoom_factor)
|
||||
: blink::PageZoomFactorToZoomLevel(
|
||||
zoom_controller->GetDefaultZoomFactor());
|
||||
|
||||
zoom_controller->SetZoomLevel(zoom_level);
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction TabsGetZoomFunction::Run() {
|
||||
std::unique_ptr<tabs::GetZoom::Params> params(
|
||||
tabs::GetZoom::Params::Create(*args_));
|
||||
EXTENSION_FUNCTION_VALIDATE(params);
|
||||
|
||||
int tab_id = params->tab_id ? *params->tab_id : -1;
|
||||
auto* contents = electron::api::WebContents::FromWeakMapID(
|
||||
v8::Isolate::GetCurrent(), tab_id);
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
double zoom_level = contents->GetZoomController()->GetZoomLevel();
|
||||
double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
|
||||
|
||||
return RespondNow(ArgumentList(tabs::GetZoom::Results::Create(zoom_factor)));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction TabsGetZoomSettingsFunction::Run() {
|
||||
std::unique_ptr<tabs::GetZoomSettings::Params> params(
|
||||
tabs::GetZoomSettings::Params::Create(*args_));
|
||||
EXTENSION_FUNCTION_VALIDATE(params);
|
||||
|
||||
int tab_id = params->tab_id ? *params->tab_id : -1;
|
||||
auto* contents = electron::api::WebContents::FromWeakMapID(
|
||||
v8::Isolate::GetCurrent(), tab_id);
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
auto* zoom_controller = contents->GetZoomController();
|
||||
WebContentsZoomController::ZoomMode zoom_mode =
|
||||
contents->GetZoomController()->zoom_mode();
|
||||
api::tabs::ZoomSettings zoom_settings;
|
||||
ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
|
||||
zoom_settings.default_zoom_factor.reset(
|
||||
new double(blink::PageZoomLevelToZoomFactor(
|
||||
zoom_controller->GetDefaultZoomLevel())));
|
||||
|
||||
return RespondNow(
|
||||
ArgumentList(api::tabs::GetZoomSettings::Results::Create(zoom_settings)));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction TabsSetZoomSettingsFunction::Run() {
|
||||
using api::tabs::ZoomSettings;
|
||||
|
||||
std::unique_ptr<tabs::SetZoomSettings::Params> params(
|
||||
tabs::SetZoomSettings::Params::Create(*args_));
|
||||
EXTENSION_FUNCTION_VALIDATE(params);
|
||||
|
||||
int tab_id = params->tab_id ? *params->tab_id : -1;
|
||||
auto* contents = electron::api::WebContents::FromWeakMapID(
|
||||
v8::Isolate::GetCurrent(), tab_id);
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
std::string error;
|
||||
GURL url(contents->web_contents()->GetVisibleURL());
|
||||
if (extension()->permissions_data()->IsRestrictedUrl(url, &error))
|
||||
return RespondNow(Error(error));
|
||||
|
||||
// "per-origin" scope is only available in "automatic" mode.
|
||||
if (params->zoom_settings.scope == tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN &&
|
||||
params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_AUTOMATIC &&
|
||||
params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_NONE) {
|
||||
return RespondNow(Error(kPerOriginOnlyInAutomaticError));
|
||||
}
|
||||
|
||||
// Determine the correct internal zoom mode to set |web_contents| to from the
|
||||
// user-specified |zoom_settings|.
|
||||
WebContentsZoomController::ZoomMode zoom_mode =
|
||||
WebContentsZoomController::ZoomMode::DEFAULT;
|
||||
switch (params->zoom_settings.mode) {
|
||||
case tabs::ZOOM_SETTINGS_MODE_NONE:
|
||||
case tabs::ZOOM_SETTINGS_MODE_AUTOMATIC:
|
||||
switch (params->zoom_settings.scope) {
|
||||
case tabs::ZOOM_SETTINGS_SCOPE_NONE:
|
||||
case tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN:
|
||||
zoom_mode = WebContentsZoomController::ZoomMode::DEFAULT;
|
||||
break;
|
||||
case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB:
|
||||
zoom_mode = WebContentsZoomController::ZoomMode::ISOLATED;
|
||||
}
|
||||
break;
|
||||
case tabs::ZOOM_SETTINGS_MODE_MANUAL:
|
||||
zoom_mode = WebContentsZoomController::ZoomMode::MANUAL;
|
||||
break;
|
||||
case tabs::ZOOM_SETTINGS_MODE_DISABLED:
|
||||
zoom_mode = WebContentsZoomController::ZoomMode::DISABLED;
|
||||
}
|
||||
|
||||
contents->GetZoomController()->SetZoomMode(zoom_mode);
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
@@ -44,6 +44,48 @@ class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction {
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT)
|
||||
};
|
||||
|
||||
class TabsGetFunction : public ExtensionFunction {
|
||||
~TabsGetFunction() override {}
|
||||
ResponseAction Run() override;
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.get", TABS_GET)
|
||||
};
|
||||
|
||||
class TabsSetZoomFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsSetZoomFunction() override {}
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.setZoom", TABS_SETZOOM)
|
||||
};
|
||||
|
||||
class TabsGetZoomFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsGetZoomFunction() override {}
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.getZoom", TABS_GETZOOM)
|
||||
};
|
||||
|
||||
class TabsSetZoomSettingsFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsSetZoomSettingsFunction() override {}
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.setZoomSettings", TABS_SETZOOMSETTINGS)
|
||||
};
|
||||
|
||||
class TabsGetZoomSettingsFunction : public ExtensionFunction {
|
||||
private:
|
||||
~TabsGetZoomSettingsFunction() override {}
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
DECLARE_EXTENSION_FUNCTION("tabs.getZoomSettings", TABS_GETZOOMSETTINGS)
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_
|
||||
|
||||
@@ -5,15 +5,12 @@
|
||||
#include "shell/browser/extensions/electron_browser_context_keyed_service_factories.h"
|
||||
|
||||
#include "extensions/browser/updater/update_service_factory.h"
|
||||
// #include "extensions/shell/browser/api/identity/identity_api.h"
|
||||
#include "shell/browser/extensions/electron_extension_system_factory.h"
|
||||
|
||||
namespace extensions {
|
||||
namespace electron {
|
||||
|
||||
void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
|
||||
// IdentityAPI::GetFactoryInstance();
|
||||
|
||||
// TODO(rockot): Remove this once UpdateService is supported across all
|
||||
// extensions embedders (and namely chrome.)
|
||||
UpdateServiceFactory::GetInstance();
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/extensions/electron_component_extension_resource_manager.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/grit/component_extension_resources_map.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
ElectronComponentExtensionResourceManager::
|
||||
ElectronComponentExtensionResourceManager() {
|
||||
AddComponentResourceEntries(kComponentExtensionResources,
|
||||
kComponentExtensionResourcesSize);
|
||||
}
|
||||
|
||||
ElectronComponentExtensionResourceManager::
|
||||
~ElectronComponentExtensionResourceManager() {}
|
||||
|
||||
bool ElectronComponentExtensionResourceManager::IsComponentExtensionResource(
|
||||
const base::FilePath& extension_path,
|
||||
const base::FilePath& resource_path,
|
||||
int* resource_id) const {
|
||||
base::FilePath directory_path = extension_path;
|
||||
base::FilePath resources_dir;
|
||||
base::FilePath relative_path;
|
||||
if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_dir) ||
|
||||
!resources_dir.AppendRelativePath(directory_path, &relative_path)) {
|
||||
return false;
|
||||
}
|
||||
relative_path = relative_path.Append(resource_path);
|
||||
relative_path = relative_path.NormalizePathSeparators();
|
||||
|
||||
auto entry = path_to_resource_id_.find(relative_path);
|
||||
if (entry != path_to_resource_id_.end()) {
|
||||
*resource_id = entry->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const ui::TemplateReplacements*
|
||||
ElectronComponentExtensionResourceManager::GetTemplateReplacementsForExtension(
|
||||
const std::string& extension_id) const {
|
||||
auto it = extension_template_replacements_.find(extension_id);
|
||||
if (it == extension_template_replacements_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
void ElectronComponentExtensionResourceManager::AddComponentResourceEntries(
|
||||
const GritResourceMap* entries,
|
||||
size_t size) {
|
||||
base::FilePath gen_folder_path = base::FilePath().AppendASCII(
|
||||
"@out_folder@/gen/chrome/browser/resources/");
|
||||
gen_folder_path = gen_folder_path.NormalizePathSeparators();
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
base::FilePath resource_path =
|
||||
base::FilePath().AppendASCII(entries[i].name);
|
||||
resource_path = resource_path.NormalizePathSeparators();
|
||||
|
||||
if (!gen_folder_path.IsParent(resource_path)) {
|
||||
DCHECK(!base::Contains(path_to_resource_id_, resource_path));
|
||||
path_to_resource_id_[resource_path] = entries[i].value;
|
||||
} else {
|
||||
// If the resource is a generated file, strip the generated folder's path,
|
||||
// so that it can be served from a normal URL (as if it were not
|
||||
// generated).
|
||||
base::FilePath effective_path =
|
||||
base::FilePath().AppendASCII(resource_path.AsUTF8Unsafe().substr(
|
||||
gen_folder_path.value().length()));
|
||||
DCHECK(!base::Contains(path_to_resource_id_, effective_path));
|
||||
path_to_resource_id_[effective_path] = entries[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_ELECTRON_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "extensions/browser/component_extension_resource_manager.h"
|
||||
|
||||
struct GritResourceMap;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
class ElectronComponentExtensionResourceManager
|
||||
: public ComponentExtensionResourceManager {
|
||||
public:
|
||||
ElectronComponentExtensionResourceManager();
|
||||
~ElectronComponentExtensionResourceManager() override;
|
||||
|
||||
// Overridden from ComponentExtensionResourceManager:
|
||||
bool IsComponentExtensionResource(const base::FilePath& extension_path,
|
||||
const base::FilePath& resource_path,
|
||||
int* resource_id) const override;
|
||||
const ui::TemplateReplacements* GetTemplateReplacementsForExtension(
|
||||
const std::string& extension_id) const override;
|
||||
|
||||
private:
|
||||
void AddComponentResourceEntries(const GritResourceMap* entries, size_t size);
|
||||
|
||||
// A map from a resource path to the resource ID. Used by
|
||||
// IsComponentExtensionResource.
|
||||
std::map<base::FilePath, int> path_to_resource_id_;
|
||||
|
||||
// A map from an extension ID to its i18n template replacements.
|
||||
std::map<std::string, ui::TemplateReplacements>
|
||||
extension_template_replacements_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ElectronComponentExtensionResourceManager);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_
|
||||
@@ -49,6 +49,8 @@ class ElectronExtensionLoader : public ExtensionRegistrar::Delegate {
|
||||
void UnloadExtension(const ExtensionId& extension_id,
|
||||
extensions::UnloadedExtensionReason reason);
|
||||
|
||||
ExtensionRegistrar* registrar() { return &extension_registrar_; }
|
||||
|
||||
private:
|
||||
// If the extension loaded successfully, enables it. If it's an app, launches
|
||||
// it. If the load failed, updates ShellKeepAliveRequester.
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/json/json_string_value_serializer.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "chrome/browser/pdf/pdf_extension_util.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
@@ -84,6 +88,35 @@ void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
|
||||
app_sorting_ = std::make_unique<NullAppSorting>();
|
||||
extension_loader_ =
|
||||
std::make_unique<ElectronExtensionLoader>(browser_context_);
|
||||
|
||||
if (!browser_context_->IsOffTheRecord())
|
||||
LoadComponentExtensions();
|
||||
}
|
||||
|
||||
std::unique_ptr<base::DictionaryValue> ParseManifest(
|
||||
base::StringPiece manifest_contents) {
|
||||
JSONStringValueDeserializer deserializer(manifest_contents);
|
||||
std::unique_ptr<base::Value> manifest = deserializer.Deserialize(NULL, NULL);
|
||||
|
||||
if (!manifest.get() || !manifest->is_dict()) {
|
||||
LOG(ERROR) << "Failed to parse extension manifest.";
|
||||
return std::unique_ptr<base::DictionaryValue>();
|
||||
}
|
||||
return base::DictionaryValue::From(std::move(manifest));
|
||||
}
|
||||
|
||||
void ElectronExtensionSystem::LoadComponentExtensions() {
|
||||
std::string utf8_error;
|
||||
std::string pdf_manifest_string = pdf_extension_util::GetManifest();
|
||||
std::unique_ptr<base::DictionaryValue> pdf_manifest =
|
||||
ParseManifest(pdf_manifest_string);
|
||||
base::FilePath root_directory;
|
||||
CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory));
|
||||
root_directory = root_directory.Append(FILE_PATH_LITERAL("pdf"));
|
||||
scoped_refptr<const Extension> pdf_extension = extensions::Extension::Create(
|
||||
root_directory, extensions::Manifest::COMPONENT, *pdf_manifest,
|
||||
extensions::Extension::REQUIRE_KEY, &utf8_error);
|
||||
extension_loader_->registrar()->AddExtension(pdf_extension);
|
||||
}
|
||||
|
||||
ExtensionService* ElectronExtensionSystem::extension_service() {
|
||||
|
||||
@@ -88,6 +88,8 @@ class ElectronExtensionSystem : public ExtensionSystem {
|
||||
private:
|
||||
void OnExtensionRegisteredWithRequestContexts(
|
||||
scoped_refptr<Extension> extension);
|
||||
void LoadComponentExtensions();
|
||||
|
||||
content::BrowserContext* browser_context_; // Not owned.
|
||||
|
||||
// Data to be accessed on the IO thread. Must outlive process_manager_.
|
||||
|
||||
@@ -5,12 +5,34 @@
|
||||
#include "shell/browser/extensions/electron_extensions_api_client.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#include "shell/browser/extensions/electron_messaging_delegate.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
class ElectronMimeHandlerViewGuestDelegate
|
||||
: public MimeHandlerViewGuestDelegate {
|
||||
public:
|
||||
ElectronMimeHandlerViewGuestDelegate() {}
|
||||
~ElectronMimeHandlerViewGuestDelegate() override {}
|
||||
|
||||
// MimeHandlerViewGuestDelegate.
|
||||
bool HandleContextMenu(content::WebContents* web_contents,
|
||||
const content::ContextMenuParams& params) override {
|
||||
// TODO(nornagon): surface this event to JS
|
||||
LOG(INFO) << "HCM";
|
||||
return true;
|
||||
}
|
||||
void RecordLoadMetric(bool in_main_frame,
|
||||
const std::string& mime_type) override {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ElectronMimeHandlerViewGuestDelegate);
|
||||
};
|
||||
|
||||
ElectronExtensionsAPIClient::ElectronExtensionsAPIClient() = default;
|
||||
ElectronExtensionsAPIClient::~ElectronExtensionsAPIClient() = default;
|
||||
|
||||
@@ -26,4 +48,10 @@ void ElectronExtensionsAPIClient::AttachWebContentsHelpers(
|
||||
web_contents);
|
||||
}
|
||||
|
||||
std::unique_ptr<MimeHandlerViewGuestDelegate>
|
||||
ElectronExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate(
|
||||
MimeHandlerViewGuest* guest) const {
|
||||
return std::make_unique<ElectronMimeHandlerViewGuestDelegate>();
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
@@ -22,6 +22,9 @@ class ElectronExtensionsAPIClient : public ExtensionsAPIClient {
|
||||
MessagingDelegate* GetMessagingDelegate() override;
|
||||
void AttachWebContentsHelpers(
|
||||
content::WebContents* web_contents) const override;
|
||||
std::unique_ptr<MimeHandlerViewGuestDelegate>
|
||||
CreateMimeHandlerViewGuestDelegate(
|
||||
MimeHandlerViewGuest* guest) const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ElectronMessagingDelegate> messaging_delegate_;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "shell/browser/extensions/electron_extensions_browser_api_provider.h"
|
||||
|
||||
#include "extensions/browser/extension_function_registry.h"
|
||||
#include "shell/browser/extensions/api/generated_api_registration.h"
|
||||
#include "shell/browser/extensions/api/tabs/tabs_api.h"
|
||||
|
||||
namespace extensions {
|
||||
@@ -16,11 +17,8 @@ ElectronExtensionsBrowserAPIProvider::~ElectronExtensionsBrowserAPIProvider() =
|
||||
|
||||
void ElectronExtensionsBrowserAPIProvider::RegisterExtensionFunctions(
|
||||
ExtensionFunctionRegistry* registry) {
|
||||
registry->RegisterFunction<TabsExecuteScriptFunction>();
|
||||
/*
|
||||
// Generated APIs from Electron.
|
||||
api::ElectronGeneratedFunctionRegistry::RegisterAll(registry);
|
||||
*/
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
@@ -8,8 +8,12 @@
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/extensions/chrome_url_request_util.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
|
||||
#include "components/version_info/version_info.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
@@ -20,17 +24,21 @@
|
||||
#include "extensions/browser/component_extension_resource_manager.h"
|
||||
#include "extensions/browser/core_extensions_browser_api_provider.h"
|
||||
#include "extensions/browser/event_router.h"
|
||||
#include "extensions/browser/extension_protocols.h"
|
||||
#include "extensions/browser/null_app_sorting.h"
|
||||
#include "extensions/browser/updater/null_extension_cache.h"
|
||||
#include "extensions/browser/url_request_util.h"
|
||||
#include "extensions/common/features/feature_channel.h"
|
||||
#include "extensions/common/file_util.h"
|
||||
#include "extensions/common/manifest_constants.h"
|
||||
#include "extensions/common/manifest_url_handlers.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/electron_browser_client.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h"
|
||||
#include "shell/browser/extensions/electron_component_extension_resource_manager.h"
|
||||
#include "shell/browser/extensions/electron_extension_host_delegate.h"
|
||||
#include "shell/browser/extensions/electron_extension_system_factory.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
@@ -38,9 +46,11 @@
|
||||
#include "shell/browser/extensions/electron_extensions_browser_api_provider.h"
|
||||
#include "shell/browser/extensions/electron_navigation_ui_data.h"
|
||||
#include "shell/browser/extensions/electron_process_manager_delegate.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
|
||||
using content::BrowserContext;
|
||||
using content::BrowserThread;
|
||||
using extensions::ExtensionsBrowserClient;
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -51,6 +61,8 @@ ElectronExtensionsBrowserClient::ElectronExtensionsBrowserClient()
|
||||
// Electron does not have a concept of channel, so leave UNKNOWN to
|
||||
// enable all channel-dependent extension APIs.
|
||||
extensions::SetCurrentChannel(version_info::Channel::UNKNOWN);
|
||||
resource_manager_.reset(
|
||||
new extensions::ElectronComponentExtensionResourceManager());
|
||||
|
||||
AddAPIProvider(
|
||||
std::make_unique<extensions::CoreExtensionsBrowserAPIProvider>());
|
||||
@@ -127,7 +139,28 @@ base::FilePath ElectronExtensionsBrowserClient::GetBundleResourcePath(
|
||||
const base::FilePath& extension_resources_path,
|
||||
int* resource_id) const {
|
||||
*resource_id = 0;
|
||||
return base::FilePath();
|
||||
base::FilePath chrome_resources_path;
|
||||
if (!base::PathService::Get(chrome::DIR_RESOURCES, &chrome_resources_path))
|
||||
return base::FilePath();
|
||||
|
||||
// Since component extension resources are included in
|
||||
// component_extension_resources.pak file in |chrome_resources_path|,
|
||||
// calculate the extension |request_relative_path| against
|
||||
// |chrome_resources_path|.
|
||||
if (!chrome_resources_path.IsParent(extension_resources_path))
|
||||
return base::FilePath();
|
||||
|
||||
const base::FilePath request_relative_path =
|
||||
extensions::file_util::ExtensionURLToRelativeFilePath(request.url);
|
||||
if (!ExtensionsBrowserClient::Get()
|
||||
->GetComponentExtensionResourceManager()
|
||||
->IsComponentExtensionResource(extension_resources_path,
|
||||
request_relative_path, resource_id)) {
|
||||
return base::FilePath();
|
||||
}
|
||||
DCHECK_NE(0, *resource_id);
|
||||
|
||||
return request_relative_path;
|
||||
}
|
||||
|
||||
void ElectronExtensionsBrowserClient::LoadResourceFromResourceBundle(
|
||||
@@ -138,7 +171,9 @@ void ElectronExtensionsBrowserClient::LoadResourceFromResourceBundle(
|
||||
const std::string& content_security_policy,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
bool send_cors_header) {
|
||||
NOTREACHED() << "Load resources from bundles not supported.";
|
||||
extensions::chrome_url_request_util::LoadResourceFromResourceBundle(
|
||||
request, std::move(loader), resource_relative_path, resource_id,
|
||||
content_security_policy, std::move(client), send_cors_header);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -160,8 +195,7 @@ bool AllowCrossRendererResourceLoad(const GURL& url,
|
||||
// If there aren't any explicitly marked web accessible resources, the
|
||||
// load should be allowed only if it is by DevTools. A close approximation is
|
||||
// checking if the extension contains a DevTools page.
|
||||
if (extension && !extensions::ManifestURL::Get(
|
||||
extension, extensions::manifest_keys::kDevToolsPage)
|
||||
if (extension && !extensions::chrome_manifest_urls::GetDevToolsPage(extension)
|
||||
.is_empty()) {
|
||||
*allowed = true;
|
||||
return true;
|
||||
@@ -255,7 +289,7 @@ ElectronExtensionsBrowserClient::CreateRuntimeAPIDelegate(
|
||||
|
||||
const extensions::ComponentExtensionResourceManager*
|
||||
ElectronExtensionsBrowserClient::GetComponentExtensionResourceManager() {
|
||||
return NULL;
|
||||
return resource_manager_.get();
|
||||
}
|
||||
|
||||
void ElectronExtensionsBrowserClient::BroadcastEventToRenderers(
|
||||
|
||||
@@ -23,6 +23,7 @@ class KioskDelegate;
|
||||
class ProcessManagerDelegate;
|
||||
class ElectronProcessManagerDelegate;
|
||||
class ProcessMap;
|
||||
class ElectronComponentExtensionResourceManager;
|
||||
} // namespace extensions
|
||||
|
||||
namespace electron {
|
||||
@@ -138,6 +139,9 @@ class ElectronExtensionsBrowserClient
|
||||
// The extension cache used for download and installation.
|
||||
std::unique_ptr<extensions::ExtensionCache> extension_cache_;
|
||||
|
||||
std::unique_ptr<extensions::ElectronComponentExtensionResourceManager>
|
||||
resource_manager_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsBrowserClient);
|
||||
};
|
||||
|
||||
|
||||
@@ -542,17 +542,27 @@ void NativeWindowMac::RepositionTrafficLights() {
|
||||
CGFloat buttonHeight = [close frame].size.height;
|
||||
CGFloat titleBarFrameHeight = buttonHeight + traffic_light_position_.y();
|
||||
CGRect titleBarRect = titleBarContainerView.frame;
|
||||
CGFloat titleBarWidth = NSWidth(titleBarRect);
|
||||
titleBarRect.size.height = titleBarFrameHeight;
|
||||
titleBarRect.origin.y = window.frame.size.height - titleBarFrameHeight;
|
||||
[titleBarContainerView setFrame:titleBarRect];
|
||||
|
||||
BOOL isRTL = [titleBarContainerView userInterfaceLayoutDirection] ==
|
||||
NSUserInterfaceLayoutDirectionRightToLeft;
|
||||
NSArray* windowButtons = @[ close, miniaturize, zoom ];
|
||||
const CGFloat space_between =
|
||||
[miniaturize frame].origin.x - [close frame].origin.x;
|
||||
for (NSUInteger i = 0; i < windowButtons.count; i++) {
|
||||
NSView* view = [windowButtons objectAtIndex:i];
|
||||
CGRect rect = [view frame];
|
||||
rect.origin.x = traffic_light_position_.x() + (i * space_between);
|
||||
if (isRTL) {
|
||||
CGFloat buttonWidth = NSWidth(rect);
|
||||
// origin is always top-left, even in RTL
|
||||
rect.origin.x = titleBarWidth - traffic_light_position_.x() +
|
||||
(i * space_between) - buttonWidth;
|
||||
} else {
|
||||
rect.origin.x = traffic_light_position_.x() + (i * space_between);
|
||||
}
|
||||
rect.origin.y = (titleBarFrameHeight - rect.size.height) / 2;
|
||||
[view setFrameOrigin:rect.origin];
|
||||
}
|
||||
@@ -1455,8 +1465,7 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
relativeTo:nil];
|
||||
}
|
||||
|
||||
std::string dep_warn =
|
||||
" has been deprecated and will be removed in a future version of macOS.";
|
||||
std::string dep_warn = " has been deprecated and removed as of macOS 10.15.";
|
||||
node::Environment* env =
|
||||
node::Environment::GetCurrent(v8::Isolate::GetCurrent());
|
||||
|
||||
@@ -1477,61 +1486,44 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
}
|
||||
|
||||
if (@available(macOS 10.11, *)) {
|
||||
// TODO(codebytere): Use NSVisualEffectMaterial* constants directly once
|
||||
// they are available in the minimum SDK version
|
||||
if (type == "selection") {
|
||||
// NSVisualEffectMaterialSelection
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(4);
|
||||
vibrancyType = NSVisualEffectMaterialSelection;
|
||||
} else if (type == "menu") {
|
||||
// NSVisualEffectMaterialMenu
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(5);
|
||||
vibrancyType = NSVisualEffectMaterialMenu;
|
||||
} else if (type == "popover") {
|
||||
// NSVisualEffectMaterialPopover
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(6);
|
||||
vibrancyType = NSVisualEffectMaterialPopover;
|
||||
} else if (type == "sidebar") {
|
||||
// NSVisualEffectMaterialSidebar
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(7);
|
||||
vibrancyType = NSVisualEffectMaterialSidebar;
|
||||
} else if (type == "medium-light") {
|
||||
// NSVisualEffectMaterialMediumLight
|
||||
EmitWarning(env, "NSVisualEffectMaterialMediumLight" + dep_warn,
|
||||
"electron");
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(8);
|
||||
vibrancyType = NSVisualEffectMaterialMediumLight;
|
||||
} else if (type == "ultra-dark") {
|
||||
// NSVisualEffectMaterialUltraDark
|
||||
EmitWarning(env, "NSVisualEffectMaterialUltraDark" + dep_warn,
|
||||
"electron");
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(9);
|
||||
vibrancyType = NSVisualEffectMaterialUltraDark;
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(macOS 10.14, *)) {
|
||||
if (type == "header") {
|
||||
// NSVisualEffectMaterialHeaderView
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(10);
|
||||
vibrancyType = NSVisualEffectMaterialHeaderView;
|
||||
} else if (type == "sheet") {
|
||||
// NSVisualEffectMaterialSheet
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(11);
|
||||
vibrancyType = NSVisualEffectMaterialSheet;
|
||||
} else if (type == "window") {
|
||||
// NSVisualEffectMaterialWindowBackground
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(12);
|
||||
vibrancyType = NSVisualEffectMaterialWindowBackground;
|
||||
} else if (type == "hud") {
|
||||
// NSVisualEffectMaterialHUDWindow
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(13);
|
||||
vibrancyType = NSVisualEffectMaterialHUDWindow;
|
||||
} else if (type == "fullscreen-ui") {
|
||||
// NSVisualEffectMaterialFullScreenUI
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(16);
|
||||
vibrancyType = NSVisualEffectMaterialFullScreenUI;
|
||||
} else if (type == "tooltip") {
|
||||
// NSVisualEffectMaterialToolTip
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(17);
|
||||
vibrancyType = NSVisualEffectMaterialToolTip;
|
||||
} else if (type == "content") {
|
||||
// NSVisualEffectMaterialContentBackground
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(18);
|
||||
vibrancyType = NSVisualEffectMaterialContentBackground;
|
||||
} else if (type == "under-window") {
|
||||
// NSVisualEffectMaterialUnderWindowBackground
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(21);
|
||||
vibrancyType = NSVisualEffectMaterialUnderWindowBackground;
|
||||
} else if (type == "under-page") {
|
||||
// NSVisualEffectMaterialUnderPageBackground
|
||||
vibrancyType = static_cast<NSVisualEffectMaterial>(22);
|
||||
vibrancyType = NSVisualEffectMaterialUnderPageBackground;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,6 @@
|
||||
#include "shell/common/options_switches.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
int64_t g_request_id = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
|
||||
FollowRedirectParams() = default;
|
||||
ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
|
||||
@@ -677,6 +670,7 @@ ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
|
||||
const HandlersMap& intercepted_handlers,
|
||||
content::BrowserContext* browser_context,
|
||||
int render_process_id,
|
||||
uint64_t* request_id_generator,
|
||||
std::unique_ptr<extensions::ExtensionNavigationUIData> navigation_ui_data,
|
||||
base::Optional<int64_t> navigation_id,
|
||||
network::mojom::URLLoaderFactoryRequest loader_request,
|
||||
@@ -688,6 +682,7 @@ ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
|
||||
intercepted_handlers_(intercepted_handlers),
|
||||
browser_context_(browser_context),
|
||||
render_process_id_(render_process_id),
|
||||
request_id_generator_(request_id_generator),
|
||||
navigation_ui_data_(std::move(navigation_ui_data)),
|
||||
navigation_id_(std::move(navigation_id)),
|
||||
loader_factory_type_(loader_factory_type) {
|
||||
@@ -765,7 +760,7 @@ void ProxyingURLLoaderFactory::CreateLoaderAndStart(
|
||||
// per-BrowserContext so extensions can make sense of it. Note that
|
||||
// |network_service_request_id_| by contrast is not necessarily unique, so we
|
||||
// don't use it for identity here.
|
||||
const uint64_t web_request_id = ++g_request_id;
|
||||
const uint64_t web_request_id = ++(*request_id_generator_);
|
||||
|
||||
// Notes: Chromium assumes that requests with zero-ID would never use the
|
||||
// "extraHeaders" code path, however in Electron requests started from
|
||||
|
||||
@@ -23,53 +23,12 @@
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
#include "services/network/public/mojom/url_response_head.mojom.h"
|
||||
#include "shell/browser/api/electron_api_web_request.h"
|
||||
#include "shell/browser/net/electron_url_loader_factory.h"
|
||||
#include "shell/browser/net/web_request_api_interface.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Defines the interface for WebRequest API, implemented by api::WebRequest.
|
||||
class WebRequestAPI {
|
||||
public:
|
||||
virtual ~WebRequestAPI() {}
|
||||
|
||||
using BeforeSendHeadersCallback =
|
||||
base::OnceCallback<void(const std::set<std::string>& removed_headers,
|
||||
const std::set<std::string>& set_headers,
|
||||
int error_code)>;
|
||||
|
||||
virtual bool HasListener() const = 0;
|
||||
virtual int OnBeforeRequest(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
net::CompletionOnceCallback callback,
|
||||
GURL* new_url) = 0;
|
||||
virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
BeforeSendHeadersCallback callback,
|
||||
net::HttpRequestHeaders* headers) = 0;
|
||||
virtual int OnHeadersReceived(
|
||||
extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
net::CompletionOnceCallback callback,
|
||||
const net::HttpResponseHeaders* original_response_headers,
|
||||
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
|
||||
GURL* allowed_unsafe_redirect_url) = 0;
|
||||
virtual void OnSendHeaders(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const net::HttpRequestHeaders& headers) = 0;
|
||||
virtual void OnBeforeRedirect(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const GURL& new_location) = 0;
|
||||
virtual void OnResponseStarted(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request) = 0;
|
||||
virtual void OnErrorOccurred(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
int net_error) = 0;
|
||||
virtual void OnCompleted(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
int net_error) = 0;
|
||||
virtual void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) = 0;
|
||||
};
|
||||
|
||||
// This class is responsible for following tasks when NetworkService is enabled:
|
||||
// 1. handling intercepted protocols;
|
||||
// 2. implementing webRequest module;
|
||||
@@ -210,6 +169,7 @@ class ProxyingURLLoaderFactory
|
||||
const HandlersMap& intercepted_handlers,
|
||||
content::BrowserContext* browser_context,
|
||||
int render_process_id,
|
||||
uint64_t* request_id_generator,
|
||||
std::unique_ptr<extensions::ExtensionNavigationUIData> navigation_ui_data,
|
||||
base::Optional<int64_t> navigation_id,
|
||||
network::mojom::URLLoaderFactoryRequest loader_request,
|
||||
@@ -269,6 +229,7 @@ class ProxyingURLLoaderFactory
|
||||
|
||||
content::BrowserContext* const browser_context_;
|
||||
const int render_process_id_;
|
||||
uint64_t* request_id_generator_; // managed by AtomBrowserClient
|
||||
std::unique_ptr<extensions::ExtensionNavigationUIData> navigation_ui_data_;
|
||||
base::Optional<int64_t> navigation_id_;
|
||||
mojo::ReceiverSet<network::mojom::URLLoaderFactory> proxy_receivers_;
|
||||
|
||||
456
shell/browser/net/proxying_websocket.cc
Normal file
456
shell/browser/net/proxying_websocket.cc
Normal file
@@ -0,0 +1,456 @@
|
||||
// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "electron/shell/browser/net/proxying_websocket.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "extensions/browser/extension_navigation_ui_data.h"
|
||||
#include "net/base/ip_endpoint.h"
|
||||
#include "net/http/http_util.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
ProxyingWebSocket::ProxyingWebSocket(
|
||||
WebRequestAPI* web_request_api,
|
||||
WebSocketFactory factory,
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
|
||||
handshake_client,
|
||||
bool has_extra_headers,
|
||||
int process_id,
|
||||
int render_frame_id,
|
||||
content::BrowserContext* browser_context,
|
||||
uint64_t* request_id_generator)
|
||||
: web_request_api_(web_request_api),
|
||||
request_(request),
|
||||
factory_(std::move(factory)),
|
||||
forwarding_handshake_client_(std::move(handshake_client)),
|
||||
request_headers_(request.headers),
|
||||
response_(network::mojom::URLResponseHead::New()),
|
||||
has_extra_headers_(has_extra_headers),
|
||||
info_(extensions::WebRequestInfoInitParams(
|
||||
++(*request_id_generator),
|
||||
process_id,
|
||||
render_frame_id,
|
||||
nullptr,
|
||||
MSG_ROUTING_NONE,
|
||||
request,
|
||||
/*is_download=*/false,
|
||||
/*is_async=*/true,
|
||||
/*is_service_worker_script=*/false,
|
||||
/*navigation_id=*/base::nullopt)) {}
|
||||
|
||||
ProxyingWebSocket::~ProxyingWebSocket() {
|
||||
if (on_before_send_headers_callback_) {
|
||||
std::move(on_before_send_headers_callback_)
|
||||
.Run(net::ERR_ABORTED, base::nullopt);
|
||||
}
|
||||
if (on_headers_received_callback_) {
|
||||
std::move(on_headers_received_callback_)
|
||||
.Run(net::ERR_ABORTED, base::nullopt, GURL());
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::Start() {
|
||||
// If the header client will be used, we start the request immediately, and
|
||||
// OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise,
|
||||
// send these events before the request starts.
|
||||
base::RepeatingCallback<void(int)> continuation;
|
||||
if (has_extra_headers_) {
|
||||
continuation = base::BindRepeating(
|
||||
&ProxyingWebSocket::ContinueToStartRequest, weak_factory_.GetWeakPtr());
|
||||
} else {
|
||||
continuation =
|
||||
base::BindRepeating(&ProxyingWebSocket::OnBeforeRequestComplete,
|
||||
weak_factory_.GetWeakPtr());
|
||||
}
|
||||
|
||||
int result = web_request_api_->OnBeforeRequest(&info_, request_, continuation,
|
||||
&redirect_url_);
|
||||
|
||||
if (result == net::ERR_BLOCKED_BY_CLIENT) {
|
||||
OnError(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == net::ERR_IO_PENDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK_EQ(net::OK, result);
|
||||
continuation.Run(net::OK);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnOpeningHandshakeStarted(
|
||||
network::mojom::WebSocketHandshakeRequestPtr request) {
|
||||
DCHECK(forwarding_handshake_client_);
|
||||
forwarding_handshake_client_->OnOpeningHandshakeStarted(std::move(request));
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::ContinueToHeadersReceived() {
|
||||
auto continuation =
|
||||
base::BindRepeating(&ProxyingWebSocket::OnHeadersReceivedComplete,
|
||||
weak_factory_.GetWeakPtr());
|
||||
info_.AddResponseInfoFromResourceResponse(*response_);
|
||||
int result = web_request_api_->OnHeadersReceived(
|
||||
&info_, request_, continuation, response_->headers.get(),
|
||||
&override_headers_, &redirect_url_);
|
||||
|
||||
if (result == net::ERR_BLOCKED_BY_CLIENT) {
|
||||
OnError(result);
|
||||
return;
|
||||
}
|
||||
|
||||
PauseIncomingMethodCallProcessing();
|
||||
if (result == net::ERR_IO_PENDING)
|
||||
return;
|
||||
|
||||
DCHECK_EQ(net::OK, result);
|
||||
OnHeadersReceivedComplete(net::OK);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnConnectionEstablished(
|
||||
mojo::PendingRemote<network::mojom::WebSocket> websocket,
|
||||
mojo::PendingReceiver<network::mojom::WebSocketClient> client_receiver,
|
||||
network::mojom::WebSocketHandshakeResponsePtr response,
|
||||
mojo::ScopedDataPipeConsumerHandle readable) {
|
||||
DCHECK(forwarding_handshake_client_);
|
||||
DCHECK(!is_done_);
|
||||
is_done_ = true;
|
||||
websocket_ = std::move(websocket);
|
||||
client_receiver_ = std::move(client_receiver);
|
||||
handshake_response_ = std::move(response);
|
||||
readable_ = std::move(readable);
|
||||
|
||||
response_->remote_endpoint = handshake_response_->remote_endpoint;
|
||||
|
||||
// response_->headers will be set in OnBeforeSendHeaders if
|
||||
// |receiver_as_header_client_| is set.
|
||||
if (receiver_as_header_client_.is_bound()) {
|
||||
ContinueToCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
response_->headers =
|
||||
base::MakeRefCounted<net::HttpResponseHeaders>(base::StringPrintf(
|
||||
"HTTP/%d.%d %d %s", handshake_response_->http_version.major_value(),
|
||||
handshake_response_->http_version.minor_value(),
|
||||
handshake_response_->status_code,
|
||||
handshake_response_->status_text.c_str()));
|
||||
for (const auto& header : handshake_response_->headers)
|
||||
response_->headers->AddHeader(header->name + ": " + header->value);
|
||||
|
||||
ContinueToHeadersReceived();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::ContinueToCompleted() {
|
||||
DCHECK(forwarding_handshake_client_);
|
||||
DCHECK(is_done_);
|
||||
web_request_api_->OnCompleted(&info_, request_, net::ERR_WS_UPGRADE);
|
||||
forwarding_handshake_client_->OnConnectionEstablished(
|
||||
std::move(websocket_), std::move(client_receiver_),
|
||||
std::move(handshake_response_), std::move(readable_));
|
||||
|
||||
// Deletes |this|.
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnAuthRequired(
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
const scoped_refptr<net::HttpResponseHeaders>& headers,
|
||||
const net::IPEndPoint& remote_endpoint,
|
||||
OnAuthRequiredCallback callback) {
|
||||
if (!callback) {
|
||||
OnError(net::ERR_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
response_->headers = headers;
|
||||
response_->remote_endpoint = remote_endpoint;
|
||||
auth_required_callback_ = std::move(callback);
|
||||
|
||||
auto continuation =
|
||||
base::BindRepeating(&ProxyingWebSocket::OnHeadersReceivedCompleteForAuth,
|
||||
weak_factory_.GetWeakPtr(), auth_info);
|
||||
info_.AddResponseInfoFromResourceResponse(*response_);
|
||||
int result = web_request_api_->OnHeadersReceived(
|
||||
&info_, request_, continuation, response_->headers.get(),
|
||||
&override_headers_, &redirect_url_);
|
||||
|
||||
if (result == net::ERR_BLOCKED_BY_CLIENT) {
|
||||
OnError(result);
|
||||
return;
|
||||
}
|
||||
|
||||
PauseIncomingMethodCallProcessing();
|
||||
if (result == net::ERR_IO_PENDING)
|
||||
return;
|
||||
|
||||
DCHECK_EQ(net::OK, result);
|
||||
OnHeadersReceivedCompleteForAuth(auth_info, net::OK);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnBeforeSendHeaders(
|
||||
const net::HttpRequestHeaders& headers,
|
||||
OnBeforeSendHeadersCallback callback) {
|
||||
DCHECK(receiver_as_header_client_.is_bound());
|
||||
|
||||
request_headers_ = headers;
|
||||
on_before_send_headers_callback_ = std::move(callback);
|
||||
OnBeforeRequestComplete(net::OK);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnHeadersReceived(const std::string& headers,
|
||||
const net::IPEndPoint& endpoint,
|
||||
OnHeadersReceivedCallback callback) {
|
||||
DCHECK(receiver_as_header_client_.is_bound());
|
||||
|
||||
on_headers_received_callback_ = std::move(callback);
|
||||
response_->headers = base::MakeRefCounted<net::HttpResponseHeaders>(headers);
|
||||
|
||||
ContinueToHeadersReceived();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::StartProxying(
|
||||
WebRequestAPI* web_request_api,
|
||||
WebSocketFactory factory,
|
||||
const GURL& url,
|
||||
const GURL& site_for_cookies,
|
||||
const base::Optional<std::string>& user_agent,
|
||||
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
|
||||
handshake_client,
|
||||
bool has_extra_headers,
|
||||
int process_id,
|
||||
int render_frame_id,
|
||||
const url::Origin& origin,
|
||||
content::BrowserContext* browser_context,
|
||||
uint64_t* request_id_generator) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
network::ResourceRequest request;
|
||||
request.url = url;
|
||||
request.site_for_cookies = net::SiteForCookies::FromUrl(site_for_cookies);
|
||||
if (user_agent) {
|
||||
request.headers.SetHeader(net::HttpRequestHeaders::kUserAgent, *user_agent);
|
||||
}
|
||||
request.request_initiator = origin;
|
||||
|
||||
auto* proxy = new ProxyingWebSocket(
|
||||
web_request_api, std::move(factory), request, std::move(handshake_client),
|
||||
has_extra_headers, process_id, render_frame_id, browser_context,
|
||||
request_id_generator);
|
||||
proxy->Start();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnBeforeRequestComplete(int error_code) {
|
||||
DCHECK(receiver_as_header_client_.is_bound() ||
|
||||
!receiver_as_handshake_client_.is_bound());
|
||||
DCHECK(info_.url.SchemeIsWSOrWSS());
|
||||
if (error_code != net::OK) {
|
||||
OnError(error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation =
|
||||
base::BindRepeating(&ProxyingWebSocket::OnBeforeSendHeadersComplete,
|
||||
weak_factory_.GetWeakPtr());
|
||||
|
||||
info_.AddResponseInfoFromResourceResponse(*response_);
|
||||
int result = web_request_api_->OnBeforeSendHeaders(
|
||||
&info_, request_, continuation, &request_headers_);
|
||||
|
||||
if (result == net::ERR_BLOCKED_BY_CLIENT) {
|
||||
OnError(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == net::ERR_IO_PENDING)
|
||||
return;
|
||||
|
||||
DCHECK_EQ(net::OK, result);
|
||||
OnBeforeSendHeadersComplete(std::set<std::string>(), std::set<std::string>(),
|
||||
net::OK);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnBeforeSendHeadersComplete(
|
||||
const std::set<std::string>& removed_headers,
|
||||
const std::set<std::string>& set_headers,
|
||||
int error_code) {
|
||||
DCHECK(receiver_as_header_client_.is_bound() ||
|
||||
!receiver_as_handshake_client_.is_bound());
|
||||
if (error_code != net::OK) {
|
||||
OnError(error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiver_as_header_client_.is_bound()) {
|
||||
CHECK(on_before_send_headers_callback_);
|
||||
std::move(on_before_send_headers_callback_)
|
||||
.Run(error_code, request_headers_);
|
||||
}
|
||||
|
||||
info_.AddResponseInfoFromResourceResponse(*response_);
|
||||
web_request_api_->OnSendHeaders(&info_, request_, request_headers_);
|
||||
|
||||
if (!receiver_as_header_client_.is_bound())
|
||||
ContinueToStartRequest(net::OK);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::ContinueToStartRequest(int error_code) {
|
||||
if (error_code != net::OK) {
|
||||
OnError(error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
base::flat_set<std::string> used_header_names;
|
||||
std::vector<network::mojom::HttpHeaderPtr> additional_headers;
|
||||
for (net::HttpRequestHeaders::Iterator it(request_headers_); it.GetNext();) {
|
||||
additional_headers.push_back(
|
||||
network::mojom::HttpHeader::New(it.name(), it.value()));
|
||||
used_header_names.insert(base::ToLowerASCII(it.name()));
|
||||
}
|
||||
for (const auto& header : additional_headers_) {
|
||||
if (!used_header_names.contains(base::ToLowerASCII(header->name))) {
|
||||
additional_headers.push_back(
|
||||
network::mojom::HttpHeader::New(header->name, header->value));
|
||||
}
|
||||
}
|
||||
|
||||
mojo::PendingRemote<network::mojom::TrustedHeaderClient>
|
||||
trusted_header_client = mojo::NullRemote();
|
||||
if (has_extra_headers_) {
|
||||
trusted_header_client =
|
||||
receiver_as_header_client_.BindNewPipeAndPassRemote();
|
||||
}
|
||||
|
||||
std::move(factory_).Run(
|
||||
info_.url, std::move(additional_headers),
|
||||
receiver_as_handshake_client_.BindNewPipeAndPassRemote(),
|
||||
receiver_as_auth_handler_.BindNewPipeAndPassRemote(),
|
||||
std::move(trusted_header_client));
|
||||
|
||||
// Here we detect mojo connection errors on |receiver_as_handshake_client_|.
|
||||
// See also CreateWebSocket in
|
||||
// //network/services/public/mojom/network_context.mojom.
|
||||
receiver_as_handshake_client_.set_disconnect_with_reason_handler(
|
||||
base::BindOnce(&ProxyingWebSocket::OnMojoConnectionErrorWithCustomReason,
|
||||
base::Unretained(this)));
|
||||
forwarding_handshake_client_.set_disconnect_handler(base::BindOnce(
|
||||
&ProxyingWebSocket::OnMojoConnectionError, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnHeadersReceivedComplete(int error_code) {
|
||||
if (error_code != net::OK) {
|
||||
OnError(error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
if (on_headers_received_callback_) {
|
||||
base::Optional<std::string> headers;
|
||||
if (override_headers_)
|
||||
headers = override_headers_->raw_headers();
|
||||
std::move(on_headers_received_callback_)
|
||||
.Run(net::OK, headers, base::nullopt);
|
||||
}
|
||||
|
||||
if (override_headers_) {
|
||||
response_->headers = override_headers_;
|
||||
override_headers_ = nullptr;
|
||||
}
|
||||
|
||||
ResumeIncomingMethodCallProcessing();
|
||||
info_.AddResponseInfoFromResourceResponse(*response_);
|
||||
web_request_api_->OnResponseStarted(&info_, request_);
|
||||
|
||||
if (!receiver_as_header_client_.is_bound())
|
||||
ContinueToCompleted();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnAuthRequiredComplete(
|
||||
extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse rv) {
|
||||
CHECK(auth_required_callback_);
|
||||
ResumeIncomingMethodCallProcessing();
|
||||
switch (rv) {
|
||||
case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse::
|
||||
AUTH_REQUIRED_RESPONSE_NO_ACTION:
|
||||
case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse::
|
||||
AUTH_REQUIRED_RESPONSE_CANCEL_AUTH:
|
||||
std::move(auth_required_callback_).Run(base::nullopt);
|
||||
break;
|
||||
|
||||
case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse::
|
||||
AUTH_REQUIRED_RESPONSE_SET_AUTH:
|
||||
std::move(auth_required_callback_).Run(auth_credentials_);
|
||||
break;
|
||||
case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse::
|
||||
AUTH_REQUIRED_RESPONSE_IO_PENDING:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnHeadersReceivedCompleteForAuth(
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
int rv) {
|
||||
if (rv != net::OK) {
|
||||
OnError(rv);
|
||||
return;
|
||||
}
|
||||
ResumeIncomingMethodCallProcessing();
|
||||
info_.AddResponseInfoFromResourceResponse(*response_);
|
||||
|
||||
auto continuation = base::BindRepeating(
|
||||
&ProxyingWebSocket::OnAuthRequiredComplete, weak_factory_.GetWeakPtr());
|
||||
auto auth_rv = extensions::ExtensionWebRequestEventRouter::
|
||||
AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_IO_PENDING;
|
||||
PauseIncomingMethodCallProcessing();
|
||||
|
||||
OnAuthRequiredComplete(auth_rv);
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::PauseIncomingMethodCallProcessing() {
|
||||
receiver_as_handshake_client_.Pause();
|
||||
receiver_as_auth_handler_.Pause();
|
||||
if (receiver_as_header_client_.is_bound())
|
||||
receiver_as_header_client_.Pause();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::ResumeIncomingMethodCallProcessing() {
|
||||
receiver_as_handshake_client_.Resume();
|
||||
receiver_as_auth_handler_.Resume();
|
||||
if (receiver_as_header_client_.is_bound())
|
||||
receiver_as_header_client_.Resume();
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnError(int error_code) {
|
||||
if (!is_done_) {
|
||||
is_done_ = true;
|
||||
web_request_api_->OnErrorOccurred(&info_, request_, error_code);
|
||||
}
|
||||
|
||||
// Deletes |this|.
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnMojoConnectionErrorWithCustomReason(
|
||||
uint32_t custom_reason,
|
||||
const std::string& description) {
|
||||
// Here we want to nofiy the custom reason to the client, which is why
|
||||
// we reset |forwarding_handshake_client_| manually.
|
||||
forwarding_handshake_client_.ResetWithReason(custom_reason, description);
|
||||
OnError(net::ERR_FAILED);
|
||||
// Deletes |this|.
|
||||
}
|
||||
|
||||
void ProxyingWebSocket::OnMojoConnectionError() {
|
||||
OnError(net::ERR_FAILED);
|
||||
// Deletes |this|.
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
165
shell/browser/net/proxying_websocket.h
Normal file
165
shell/browser/net/proxying_websocket.h
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_NET_PROXYING_WEBSOCKET_H_
|
||||
#define SHELL_BROWSER_NET_PROXYING_WEBSOCKET_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/optional.h"
|
||||
#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
|
||||
#include "extensions/browser/api/web_request/web_request_api.h"
|
||||
#include "extensions/browser/api/web_request/web_request_info.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/network/public/cpp/resource_request.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
#include "services/network/public/mojom/websocket.mojom.h"
|
||||
#include "shell/browser/net/web_request_api_interface.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
// A ProxyingWebSocket proxies a WebSocket connection and dispatches
|
||||
// WebRequest API events.
|
||||
//
|
||||
// The code is referenced from the
|
||||
// extensions::WebRequestProxyingWebSocket class.
|
||||
class ProxyingWebSocket : public network::mojom::WebSocketHandshakeClient,
|
||||
public network::mojom::AuthenticationHandler,
|
||||
public network::mojom::TrustedHeaderClient {
|
||||
public:
|
||||
using WebSocketFactory = content::ContentBrowserClient::WebSocketFactory;
|
||||
|
||||
ProxyingWebSocket(
|
||||
WebRequestAPI* web_request_api,
|
||||
WebSocketFactory factory,
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
|
||||
handshake_client,
|
||||
bool has_extra_headers,
|
||||
int process_id,
|
||||
int render_frame_id,
|
||||
content::BrowserContext* browser_context,
|
||||
uint64_t* request_id_generator);
|
||||
~ProxyingWebSocket() override;
|
||||
|
||||
void Start();
|
||||
|
||||
// network::mojom::WebSocketHandshakeClient methods:
|
||||
void OnOpeningHandshakeStarted(
|
||||
network::mojom::WebSocketHandshakeRequestPtr request) override;
|
||||
void OnConnectionEstablished(
|
||||
mojo::PendingRemote<network::mojom::WebSocket> websocket,
|
||||
mojo::PendingReceiver<network::mojom::WebSocketClient> client_receiver,
|
||||
network::mojom::WebSocketHandshakeResponsePtr response,
|
||||
mojo::ScopedDataPipeConsumerHandle readable) override;
|
||||
|
||||
// network::mojom::AuthenticationHandler method:
|
||||
void OnAuthRequired(const net::AuthChallengeInfo& auth_info,
|
||||
const scoped_refptr<net::HttpResponseHeaders>& headers,
|
||||
const net::IPEndPoint& remote_endpoint,
|
||||
OnAuthRequiredCallback callback) override;
|
||||
|
||||
// network::mojom::TrustedHeaderClient methods:
|
||||
void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers,
|
||||
OnBeforeSendHeadersCallback callback) override;
|
||||
void OnHeadersReceived(const std::string& headers,
|
||||
const net::IPEndPoint& endpoint,
|
||||
OnHeadersReceivedCallback callback) override;
|
||||
|
||||
static void StartProxying(
|
||||
WebRequestAPI* web_request_api,
|
||||
WebSocketFactory factory,
|
||||
const GURL& url,
|
||||
const GURL& site_for_cookies,
|
||||
const base::Optional<std::string>& user_agent,
|
||||
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
|
||||
handshake_client,
|
||||
bool has_extra_headers,
|
||||
int process_id,
|
||||
int render_frame_id,
|
||||
const url::Origin& origin,
|
||||
content::BrowserContext* browser_context,
|
||||
uint64_t* request_id_generator);
|
||||
|
||||
WebRequestAPI* web_request_api() { return web_request_api_; }
|
||||
|
||||
private:
|
||||
void OnBeforeRequestComplete(int error_code);
|
||||
void OnBeforeSendHeadersComplete(const std::set<std::string>& removed_headers,
|
||||
const std::set<std::string>& set_headers,
|
||||
int error_code);
|
||||
void ContinueToStartRequest(int error_code);
|
||||
void OnHeadersReceivedComplete(int error_code);
|
||||
void ContinueToHeadersReceived();
|
||||
void OnAuthRequiredComplete(
|
||||
extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse rv);
|
||||
void OnHeadersReceivedCompleteForAuth(const net::AuthChallengeInfo& auth_info,
|
||||
int rv);
|
||||
void ContinueToCompleted();
|
||||
|
||||
void PauseIncomingMethodCallProcessing();
|
||||
void ResumeIncomingMethodCallProcessing();
|
||||
void OnError(int result);
|
||||
// This is used for detecting errors on mojo connection with the network
|
||||
// service.
|
||||
void OnMojoConnectionErrorWithCustomReason(uint32_t custom_reason,
|
||||
const std::string& description);
|
||||
// This is used for detecting errors on mojo connection with original client
|
||||
// (i.e., renderer).
|
||||
void OnMojoConnectionError();
|
||||
|
||||
// Passed from api::WebRequest.
|
||||
WebRequestAPI* web_request_api_;
|
||||
|
||||
// Saved to feed the api::WebRequest.
|
||||
network::ResourceRequest request_;
|
||||
|
||||
WebSocketFactory factory_;
|
||||
mojo::Remote<network::mojom::WebSocketHandshakeClient>
|
||||
forwarding_handshake_client_;
|
||||
mojo::Receiver<network::mojom::WebSocketHandshakeClient>
|
||||
receiver_as_handshake_client_{this};
|
||||
mojo::Receiver<network::mojom::AuthenticationHandler>
|
||||
receiver_as_auth_handler_{this};
|
||||
mojo::Receiver<network::mojom::TrustedHeaderClient>
|
||||
receiver_as_header_client_{this};
|
||||
|
||||
net::HttpRequestHeaders request_headers_;
|
||||
network::mojom::URLResponseHeadPtr response_;
|
||||
net::AuthCredentials auth_credentials_;
|
||||
OnAuthRequiredCallback auth_required_callback_;
|
||||
scoped_refptr<net::HttpResponseHeaders> override_headers_;
|
||||
std::vector<network::mojom::HttpHeaderPtr> additional_headers_;
|
||||
|
||||
OnBeforeSendHeadersCallback on_before_send_headers_callback_;
|
||||
OnHeadersReceivedCallback on_headers_received_callback_;
|
||||
|
||||
GURL redirect_url_;
|
||||
bool is_done_ = false;
|
||||
bool has_extra_headers_;
|
||||
mojo::PendingRemote<network::mojom::WebSocket> websocket_;
|
||||
mojo::PendingReceiver<network::mojom::WebSocketClient> client_receiver_;
|
||||
network::mojom::WebSocketHandshakeResponsePtr handshake_response_ = nullptr;
|
||||
mojo::ScopedDataPipeConsumerHandle readable_;
|
||||
|
||||
extensions::WebRequestInfo info_;
|
||||
|
||||
// Notifies the proxy that the browser context has been shutdown.
|
||||
std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
|
||||
shutdown_notifier_;
|
||||
|
||||
base::WeakPtrFactory<ProxyingWebSocket> weak_factory_{this};
|
||||
DISALLOW_COPY_AND_ASSIGN(ProxyingWebSocket);
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_NET_PROXYING_WEBSOCKET_H_
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/cors_exempt_headers.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "content/public/common/content_features.h"
|
||||
#include "content/public/common/service_names.mojom.h"
|
||||
@@ -154,6 +155,10 @@ SystemNetworkContextManager::CreateDefaultNetworkContextParams() {
|
||||
network::mojom::NetworkContextParamsPtr network_context_params =
|
||||
network::mojom::NetworkContextParams::New();
|
||||
|
||||
// This is required to avoid blocking X-Requested-With headers sent by PPAPI
|
||||
// plugins, more info crbug.com/940331
|
||||
content::UpdateCorsExemptHeader(network_context_params.get());
|
||||
|
||||
network_context_params->enable_brotli = true;
|
||||
|
||||
network_context_params->enable_referrers = true;
|
||||
|
||||
61
shell/browser/net/web_request_api_interface.h
Normal file
61
shell/browser/net/web_request_api_interface.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2020 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_NET_WEB_REQUEST_API_INTERFACE_H_
|
||||
#define SHELL_BROWSER_NET_WEB_REQUEST_API_INTERFACE_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "extensions/browser/api/web_request/web_request_info.h"
|
||||
#include "services/network/public/cpp/resource_request.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Defines the interface for WebRequest API, implemented by api::WebRequestNS.
|
||||
class WebRequestAPI {
|
||||
public:
|
||||
virtual ~WebRequestAPI() {}
|
||||
|
||||
using BeforeSendHeadersCallback =
|
||||
base::OnceCallback<void(const std::set<std::string>& removed_headers,
|
||||
const std::set<std::string>& set_headers,
|
||||
int error_code)>;
|
||||
|
||||
virtual bool HasListener() const = 0;
|
||||
virtual int OnBeforeRequest(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
net::CompletionOnceCallback callback,
|
||||
GURL* new_url) = 0;
|
||||
virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
BeforeSendHeadersCallback callback,
|
||||
net::HttpRequestHeaders* headers) = 0;
|
||||
virtual int OnHeadersReceived(
|
||||
extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
net::CompletionOnceCallback callback,
|
||||
const net::HttpResponseHeaders* original_response_headers,
|
||||
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
|
||||
GURL* allowed_unsafe_redirect_url) = 0;
|
||||
virtual void OnSendHeaders(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const net::HttpRequestHeaders& headers) = 0;
|
||||
virtual void OnBeforeRedirect(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
const GURL& new_location) = 0;
|
||||
virtual void OnResponseStarted(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request) = 0;
|
||||
virtual void OnErrorOccurred(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
int net_error) = 0;
|
||||
virtual void OnCompleted(extensions::WebRequestInfo* info,
|
||||
const network::ResourceRequest& request,
|
||||
int net_error) = 0;
|
||||
virtual void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) = 0;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_NET_WEB_REQUEST_API_INTERFACE_H_
|
||||
63
shell/browser/plugins/plugin_utils.cc
Normal file
63
shell/browser/plugins/plugin_utils.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/plugins/plugin_utils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/values.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/extension_util.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "extensions/common/extension.h"
|
||||
#include "extensions/common/manifest_handlers/mime_types_handler.h"
|
||||
#endif
|
||||
|
||||
// static
|
||||
std::string PluginUtils::GetExtensionIdForMimeType(
|
||||
content::BrowserContext* browser_context,
|
||||
const std::string& mime_type) {
|
||||
auto map = GetMimeTypeToExtensionIdMap(browser_context);
|
||||
auto it = map.find(mime_type);
|
||||
if (it != map.end())
|
||||
return it->second;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
base::flat_map<std::string, std::string>
|
||||
PluginUtils::GetMimeTypeToExtensionIdMap(
|
||||
content::BrowserContext* browser_context) {
|
||||
base::flat_map<std::string, std::string> mime_type_to_extension_id_map;
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist();
|
||||
// Go through the white-listed extensions and try to use them to intercept
|
||||
// the URL request.
|
||||
for (const std::string& extension_id : whitelist) {
|
||||
const extensions::Extension* extension =
|
||||
extensions::ExtensionRegistry::Get(browser_context)
|
||||
->enabled_extensions()
|
||||
.GetByID(extension_id);
|
||||
// The white-listed extension may not be installed, so we have to nullptr
|
||||
// check |extension|.
|
||||
if (!extension) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension)) {
|
||||
for (const auto& supported_mime_type : handler->mime_type_set()) {
|
||||
DCHECK(!base::Contains(mime_type_to_extension_id_map,
|
||||
supported_mime_type));
|
||||
mime_type_to_extension_id_map[supported_mime_type] = extension_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return mime_type_to_extension_id_map;
|
||||
}
|
||||
34
shell/browser/plugins/plugin_utils.h
Normal file
34
shell/browser/plugins/plugin_utils.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_PLUGINS_PLUGIN_UTILS_H_
|
||||
#define SHELL_BROWSER_PLUGINS_PLUGIN_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
class PluginUtils {
|
||||
public:
|
||||
// If there's an extension that is allowed to handle |mime_type|, returns its
|
||||
// ID. Otherwise returns an empty string.
|
||||
static std::string GetExtensionIdForMimeType(
|
||||
content::BrowserContext* browser_context,
|
||||
const std::string& mime_type);
|
||||
|
||||
// Returns a map populated with MIME types that are handled by an extension as
|
||||
// keys and the corresponding extensions Ids as values.
|
||||
static base::flat_map<std::string, std::string> GetMimeTypeToExtensionIdMap(
|
||||
content::BrowserContext* browser_context);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PluginUtils);
|
||||
};
|
||||
|
||||
#endif // SHELL_BROWSER_PLUGINS_PLUGIN_UTILS_H_
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 9,0,0,20200205
|
||||
PRODUCTVERSION 9,0,0,20200205
|
||||
FILEVERSION 9,0,0,2
|
||||
PRODUCTVERSION 9,0,0,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -50,11 +50,10 @@
|
||||
#include "ui/display/screen.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/common/manifest_constants.h"
|
||||
#include "extensions/common/manifest_url_handlers.h"
|
||||
#include "extensions/common/permissions/permissions_data.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#endif
|
||||
@@ -601,8 +600,8 @@ void InspectableWebContentsImpl::AddDevToolsExtensionsToClient() {
|
||||
|
||||
base::ListValue results;
|
||||
for (auto& extension : registry->enabled_extensions()) {
|
||||
auto devtools_page_url = extensions::ManifestURL::Get(
|
||||
extension.get(), extensions::manifest_keys::kDevToolsPage);
|
||||
auto devtools_page_url =
|
||||
extensions::chrome_manifest_urls::GetDevToolsPage(extension.get());
|
||||
if (devtools_page_url.is_empty())
|
||||
continue;
|
||||
|
||||
|
||||
@@ -37,11 +37,8 @@ const char kRunAsNode[] = "ELECTRON_RUN_AS_NODE";
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
const char kPdfPluginMimeType[] = "application/x-google-chrome-pdf";
|
||||
const char kPdfPluginPath[] = "chrome://pdf-viewer/";
|
||||
const char kPdfPluginSrc[] = "src";
|
||||
|
||||
const char kPdfViewerUIOrigin[] = "chrome://pdf-viewer/";
|
||||
const char kPdfViewerUIHost[] = "pdf-viewer";
|
||||
const base::FilePath::CharType kPdfPluginPath[] =
|
||||
FILE_PATH_LITERAL("internal-pdf-viewer");
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef SHELL_COMMON_ELECTRON_CONSTANTS_H_
|
||||
#define SHELL_COMMON_ELECTRON_CONSTANTS_H_
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "build/build_config.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
|
||||
@@ -39,12 +40,7 @@ extern const char kRunAsNode[];
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
// The MIME type used for the PDF plugin.
|
||||
extern const char kPdfPluginMimeType[];
|
||||
extern const char kPdfPluginPath[];
|
||||
extern const char kPdfPluginSrc[];
|
||||
|
||||
// Constants for PDF viewer webui.
|
||||
extern const char kPdfViewerUIOrigin[];
|
||||
extern const char kPdfViewerUIHost[];
|
||||
extern const base::FilePath::CharType kPdfPluginPath[];
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -22,6 +22,7 @@ group("extensions_features") {
|
||||
public_deps = [
|
||||
":api_features",
|
||||
":manifest_features",
|
||||
":permission_features",
|
||||
|
||||
# TODO(devlin): It would be nicer to have this dependency hoisted up to
|
||||
# //extensions/common (since that's where it's consumed), but there's some
|
||||
@@ -36,6 +37,7 @@ group("extensions_features") {
|
||||
generated_json_strings("generated_api_json_strings") {
|
||||
sources = [
|
||||
"extension.json",
|
||||
"resources_private.idl",
|
||||
"tabs.json",
|
||||
]
|
||||
|
||||
@@ -50,7 +52,10 @@ generated_json_strings("generated_api_json_strings") {
|
||||
}
|
||||
|
||||
generated_types("generated_api_types") {
|
||||
sources = [ "tabs.json" ]
|
||||
sources = [
|
||||
"resources_private.idl",
|
||||
"tabs.json",
|
||||
]
|
||||
configs = [ "//build/config:precompiled_headers" ]
|
||||
schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s"
|
||||
|
||||
@@ -73,3 +78,10 @@ json_features("api_features") {
|
||||
sources = [ "_api_features.json" ]
|
||||
visibility = [ ":extensions_features" ]
|
||||
}
|
||||
|
||||
json_features("permission_features") {
|
||||
feature_type = "PermissionFeature"
|
||||
method_name = "AddElectronPermissionFeatures"
|
||||
sources = [ "_permission_features.json" ]
|
||||
visibility = [ ":extensions_features" ]
|
||||
}
|
||||
|
||||
@@ -11,5 +11,21 @@
|
||||
},
|
||||
"extension.getURL": {
|
||||
"contexts": ["blessed_extension", "unblessed_extension", "content_script"]
|
||||
}
|
||||
},
|
||||
"mimeHandlerViewGuestInternal": {
|
||||
"internal": true,
|
||||
"contexts": "all",
|
||||
"channel": "stable",
|
||||
"matches": ["<all_urls>"]
|
||||
},
|
||||
"resourcesPrivate": [{
|
||||
"dependencies": ["permission:resourcesPrivate"],
|
||||
"contexts": ["blessed_extension"]
|
||||
}, {
|
||||
"channel": "stable",
|
||||
"contexts": ["webui"],
|
||||
"matches": [
|
||||
"chrome://print/*"
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
9
shell/common/extensions/api/_permission_features.json
Normal file
9
shell/common/extensions/api/_permission_features.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"resourcesPrivate": {
|
||||
"channel": "stable",
|
||||
"extension_types": [
|
||||
"extension"
|
||||
],
|
||||
"location": "component"
|
||||
}
|
||||
}
|
||||
23
shell/common/extensions/api/resources_private.idl
Normal file
23
shell/common/extensions/api/resources_private.idl
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// resourcesPrivate.
|
||||
namespace resourcesPrivate {
|
||||
enum Component { identity, pdf };
|
||||
|
||||
callback GetStringsCallback = void (object result);
|
||||
|
||||
interface Functions {
|
||||
// Gets localized strings for a component extension. Includes default WebUI
|
||||
// loadTimeData values for text and language settings (fontsize, fontfamily,
|
||||
// language, textdirection). See
|
||||
// chrome/browser/extensions/api/resources_private/resources_private_api.cc
|
||||
// for instructions on adding a new component to this API.
|
||||
//
|
||||
// |component| : Internal chrome component to get strings for.
|
||||
// |callback| : Called with a dictionary mapping names to strings.
|
||||
static void getStrings(Component component,
|
||||
GetStringsCallback callback);
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,143 @@
|
||||
[
|
||||
{
|
||||
"namespace": "tabs",
|
||||
"description": "Use the <code>chrome.tabs</code> API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.",
|
||||
"types": [
|
||||
{ "id": "MutedInfoReason",
|
||||
"type": "string",
|
||||
"description": "An event that caused a muted state change.",
|
||||
"enum": [
|
||||
{"name": "user", "description": "A user input action set the muted state."},
|
||||
{"name": "capture", "description": "Tab capture was started, forcing a muted state change."},
|
||||
{"name": "extension", "description": "An extension, identified by the extensionId field, set the muted state."}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "MutedInfo",
|
||||
"type": "object",
|
||||
"description": "The tab's muted state and the reason for the last state change.",
|
||||
"properties": {
|
||||
"muted": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the tab is muted (prevented from playing sound). The tab may be muted even if it has not played or is not currently playing sound. Equivalent to whether the 'muted' audio indicator is showing."
|
||||
},
|
||||
"reason": {
|
||||
"$ref": "MutedInfoReason",
|
||||
"optional": true,
|
||||
"description": "The reason the tab was muted or unmuted. Not set if the tab's mute state has never been changed."
|
||||
},
|
||||
"extensionId": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The ID of the extension that changed the muted state. Not set if an extension was not the reason the muted state last changed."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Tab",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {"type": "integer", "minimum": -1, "optional": true, "description": "The ID of the tab. Tab IDs are unique within a browser session. Under some circumstances a tab may not be assigned an ID; for example, when querying foreign tabs using the $(ref:sessions) API, in which case a session ID may be present. Tab ID can also be set to <code>chrome.tabs.TAB_ID_NONE</code> for apps and devtools windows."},
|
||||
// TODO(kalman): Investigate how this is ending up as -1 (based on window type? a bug?) and whether it should be optional instead.
|
||||
"index": {"type": "integer", "minimum": -1, "description": "The zero-based index of the tab within its window."},
|
||||
"windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window that contains the tab."},
|
||||
"openerTabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The ID of the tab that opened this tab, if any. This property is only present if the opener tab still exists."},
|
||||
"selected": {"type": "boolean", "description": "Whether the tab is selected.", "deprecated": "Please use $(ref:tabs.Tab.highlighted)."},
|
||||
"highlighted": {"type": "boolean", "description": "Whether the tab is highlighted."},
|
||||
"active": {"type": "boolean", "description": "Whether the tab is active in its window. Does not necessarily mean the window is focused."},
|
||||
"pinned": {"type": "boolean", "description": "Whether the tab is pinned."},
|
||||
"audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the 'speaker audio' indicator is showing."},
|
||||
"discarded": {"type": "boolean", "description": "Whether the tab is discarded. A discarded tab is one whose content has been unloaded from memory, but is still visible in the tab strip. Its content is reloaded the next time it is activated."},
|
||||
"autoDiscardable": {"type": "boolean", "description": "Whether the tab can be discarded automatically by the browser when resources are low."},
|
||||
"mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "The tab's muted state and the reason for the last state change."},
|
||||
"url": {"type": "string", "optional": true, "description": "The last committed URL of the main frame of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission and may be an empty string if the tab has not yet committed. See also $(ref:Tab.pendingUrl)."},
|
||||
"pendingUrl": {"type": "string", "optional": true, "description": "The URL the tab is navigating to, before it has committed. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission and there is a pending navigation."},
|
||||
"title": {"type": "string", "optional": true, "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
|
||||
"favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
|
||||
"status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
|
||||
"incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."},
|
||||
"width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
|
||||
"height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."},
|
||||
"sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a tab obtained from the $(ref:sessions) API."}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ZoomSettingsMode",
|
||||
"type": "string",
|
||||
"description": "Defines how zoom changes are handled, i.e., which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
|
||||
"enum": [
|
||||
{
|
||||
"name": "automatic",
|
||||
"description": "Zoom changes are handled automatically by the browser."
|
||||
},
|
||||
{
|
||||
"name": "manual",
|
||||
"description": "Overrides the automatic handling of zoom changes. The <code>onZoomChange</code> event will still be dispatched, and it is the extension's responsibility to listen for this event and manually scale the page. This mode does not support <code>per-origin</code> zooming, and thus ignores the <code>scope</code> zoom setting and assumes <code>per-tab</code>."
|
||||
},
|
||||
{
|
||||
"name": "disabled",
|
||||
"description": "Disables all zooming in the tab. The tab reverts to the default zoom level, and all attempted zoom changes are ignored."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ZoomSettingsScope",
|
||||
"type": "string",
|
||||
"description": "Defines whether zoom changes persist for the page's origin, or only take effect in this tab; defaults to <code>per-origin</code> when in <code>automatic</code> mode, and <code>per-tab</code> otherwise.",
|
||||
"enum": [
|
||||
{
|
||||
"name": "per-origin",
|
||||
"description": "Zoom changes persist in the zoomed page's origin, i.e., all other tabs navigated to that same origin are zoomed as well. Moreover, <code>per-origin</code> zoom changes are saved with the origin, meaning that when navigating to other pages in the same origin, they are all zoomed to the same zoom factor. The <code>per-origin</code> scope is only available in the <code>automatic</code> mode."
|
||||
},
|
||||
{
|
||||
"name": "per-tab",
|
||||
"description": "Zoom changes only take effect in this tab, and zoom changes in other tabs do not affect the zooming of this tab. Also, <code>per-tab</code> zoom changes are reset on navigation; navigating a tab always loads pages with their <code>per-origin</code> zoom factors."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ZoomSettings",
|
||||
"type": "object",
|
||||
"description": "Defines how zoom changes in a tab are handled and at what scope.",
|
||||
"properties": {
|
||||
"mode": {
|
||||
"$ref": "ZoomSettingsMode",
|
||||
"description": "Defines how zoom changes are handled, i.e., which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
|
||||
"optional": true
|
||||
},
|
||||
"scope": {
|
||||
"$ref": "ZoomSettingsScope",
|
||||
"description": "Defines whether zoom changes persist for the page's origin, or only take effect in this tab; defaults to <code>per-origin</code> when in <code>automatic</code> mode, and <code>per-tab</code> otherwise.",
|
||||
"optional": true
|
||||
},
|
||||
"defaultZoomFactor": {
|
||||
"type": "number",
|
||||
"optional": true,
|
||||
"description": "Used to return the default zoom level for the current tab in calls to tabs.getZoomSettings."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
{
|
||||
"name": "get",
|
||||
"type": "function",
|
||||
"description": "Retrieves details about the specified tab.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{"name": "tab", "$ref": "Tab"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "executeScript",
|
||||
"type": "function",
|
||||
@@ -80,6 +216,127 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "setZoom",
|
||||
"type": "function",
|
||||
"description": "Zooms a specified tab.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab to zoom; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"name": "zoomFactor",
|
||||
"description": "The new zoom factor. A value of <code>0</code> sets the tab to its current default zoom factor. Values greater than <code>0</code> specify a (possibly non-default) zoom factor for the tab."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"description": "Called after the zoom factor has been changed.",
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getZoom",
|
||||
"type": "function",
|
||||
"description": "Gets the current zoom factor of a specified tab.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab to get the current zoom factor from; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"description": "Called with the tab's current zoom factor after it has been fetched.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "number",
|
||||
"name": "zoomFactor",
|
||||
"description": "The tab's current zoom factor."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "setZoomSettings",
|
||||
"type": "function",
|
||||
"description": "Sets the zoom settings for a specified tab, which define how zoom changes are handled. These settings are reset to defaults upon navigating the tab.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"optional": true,
|
||||
"minimum": 0,
|
||||
"description": "The ID of the tab to change the zoom settings for; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"$ref": "ZoomSettings",
|
||||
"name": "zoomSettings",
|
||||
"description": "Defines how zoom changes are handled and at what scope."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"description": "Called after the zoom settings are changed.",
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getZoomSettings",
|
||||
"type": "function",
|
||||
"description": "Gets the current zoom settings of a specified tab.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"optional": true,
|
||||
"minimum": 0,
|
||||
"description": "The ID of the tab to get the current zoom settings from; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"description": "Called with the tab's current zoom settings.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "ZoomSettings",
|
||||
"name": "zoomSettings",
|
||||
"description": "The tab's current zoom settings."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "onZoomChange",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is zoomed.",
|
||||
"parameters": [{
|
||||
"type": "object",
|
||||
"name": "ZoomChangeInfo",
|
||||
"properties": {
|
||||
"tabId": {"type": "integer", "minimum": 0},
|
||||
"oldZoomFactor": {"type": "number"},
|
||||
"newZoomFactor": {"type": "number"},
|
||||
"zoomSettings": {"$ref": "ZoomSettings"}
|
||||
}
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "electron/shell/common/extensions/api/generated_schemas.h"
|
||||
#include "extensions/common/alias.h"
|
||||
@@ -23,45 +24,20 @@
|
||||
#include "extensions/common/permissions/permissions_info.h"
|
||||
#include "shell/common/extensions/api/api_features.h"
|
||||
#include "shell/common/extensions/api/manifest_features.h"
|
||||
#include "shell/common/extensions/api/permission_features.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
namespace keys = manifest_keys;
|
||||
namespace errors = manifest_errors;
|
||||
|
||||
// Parses the "devtools_page" manifest key.
|
||||
class DevToolsPageHandler : public ManifestHandler {
|
||||
public:
|
||||
DevToolsPageHandler() = default;
|
||||
~DevToolsPageHandler() override = default;
|
||||
|
||||
bool Parse(Extension* extension, base::string16* error) override {
|
||||
std::unique_ptr<ManifestURL> manifest_url(new ManifestURL);
|
||||
std::string devtools_str;
|
||||
if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) {
|
||||
*error = base::ASCIIToUTF16(errors::kInvalidDevToolsPage);
|
||||
return false;
|
||||
}
|
||||
manifest_url->url_ = extension->GetResourceURL(devtools_str);
|
||||
extension->SetManifestData(keys::kDevToolsPage, std::move(manifest_url));
|
||||
PermissionsParser::AddAPIPermission(extension, APIPermission::kDevtools);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
base::span<const char* const> Keys() const override {
|
||||
static constexpr const char* kKeys[] = {keys::kDevToolsPage};
|
||||
return kKeys;
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DevToolsPageHandler);
|
||||
};
|
||||
|
||||
constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
|
||||
{APIPermission::kDevtools, "devtools",
|
||||
APIPermissionInfo::kFlagImpliesFullURLAccess |
|
||||
APIPermissionInfo::kFlagCannotBeOptional |
|
||||
APIPermissionInfo::kFlagInternal},
|
||||
{APIPermission::kResourcesPrivate, "resourcesPrivate",
|
||||
APIPermissionInfo::kFlagCannotBeOptional},
|
||||
};
|
||||
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
|
||||
return base::make_span(permissions_to_register);
|
||||
@@ -89,7 +65,7 @@ void ElectronExtensionsAPIProvider::AddManifestFeatures(
|
||||
|
||||
void ElectronExtensionsAPIProvider::AddPermissionFeatures(
|
||||
extensions::FeatureProvider* provider) {
|
||||
// No shell-specific permission features.
|
||||
extensions::AddElectronPermissionFeatures(provider);
|
||||
}
|
||||
|
||||
void ElectronExtensionsAPIProvider::AddBehaviorFeatures(
|
||||
|
||||
@@ -117,7 +117,6 @@ extensions::URLPatternSet
|
||||
ElectronExtensionsClient::GetPermittedChromeSchemeHosts(
|
||||
const extensions::Extension* extension,
|
||||
const extensions::APIPermissionSet& api_permissions) const {
|
||||
NOTIMPLEMENTED();
|
||||
return extensions::URLPatternSet();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ class RenderFramePersistenceStore final : public content::RenderFrameObserver {
|
||||
v8::Local<v8::Value> proxy_value);
|
||||
v8::MaybeLocal<v8::Value> GetCachedProxiedObject(v8::Local<v8::Value> from);
|
||||
|
||||
base::WeakPtr<RenderFramePersistenceStore> GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
private:
|
||||
// func_id ==> { function, owning_context }
|
||||
std::map<size_t, FunctionContextPair> functions_;
|
||||
|
||||
@@ -111,25 +111,31 @@ bool IsPlainArray(const v8::Local<v8::Value>& arr) {
|
||||
|
||||
class FunctionLifeMonitor final : public ObjectLifeMonitor {
|
||||
public:
|
||||
static void BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
context_bridge::RenderFramePersistenceStore* store,
|
||||
size_t func_id) {
|
||||
static void BindTo(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
base::WeakPtr<context_bridge::RenderFramePersistenceStore> store,
|
||||
size_t func_id) {
|
||||
new FunctionLifeMonitor(isolate, target, store, func_id);
|
||||
}
|
||||
|
||||
protected:
|
||||
FunctionLifeMonitor(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
context_bridge::RenderFramePersistenceStore* store,
|
||||
size_t func_id)
|
||||
FunctionLifeMonitor(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
base::WeakPtr<context_bridge::RenderFramePersistenceStore> store,
|
||||
size_t func_id)
|
||||
: ObjectLifeMonitor(isolate, target), store_(store), func_id_(func_id) {}
|
||||
~FunctionLifeMonitor() override = default;
|
||||
|
||||
void RunDestructor() override { store_->functions().erase(func_id_); }
|
||||
void RunDestructor() override {
|
||||
if (!store_)
|
||||
return;
|
||||
store_->functions().erase(func_id_);
|
||||
}
|
||||
|
||||
private:
|
||||
context_bridge::RenderFramePersistenceStore* store_;
|
||||
base::WeakPtr<context_bridge::RenderFramePersistenceStore> store_;
|
||||
size_t func_id_;
|
||||
};
|
||||
|
||||
@@ -176,7 +182,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
|
||||
base::BindRepeating(&ProxyFunctionWrapper, store, func_id));
|
||||
FunctionLifeMonitor::BindTo(destination_context->GetIsolate(),
|
||||
v8::Local<v8::Object>::Cast(proxy_func),
|
||||
store, func_id);
|
||||
store->GetWeakPtr(), func_id);
|
||||
store->CacheProxiedObject(value, proxy_func);
|
||||
return v8::MaybeLocal<v8::Value>(proxy_func);
|
||||
}
|
||||
|
||||
@@ -67,12 +67,16 @@
|
||||
#endif // BUILDFLAG(ENABLE_PRINTING)
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "extensions/common/extensions_client.h"
|
||||
#include "extensions/renderer/dispatcher.h"
|
||||
#include "extensions/renderer/extension_frame_helper.h"
|
||||
#include "extensions/renderer/guest_view/extensions_guest_view_container.h"
|
||||
#include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h"
|
||||
#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
|
||||
#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h"
|
||||
#include "shell/common/extensions/electron_extensions_client.h"
|
||||
#include "shell/renderer/extensions/electron_extensions_renderer_client.h"
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
@@ -271,6 +275,11 @@ void RendererClientBase::RenderFrameCreated(
|
||||
new extensions::ExtensionFrameHelper(render_frame, dispatcher);
|
||||
|
||||
dispatcher->OnRenderFrameCreated(render_frame);
|
||||
|
||||
render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
|
||||
base::BindRepeating(
|
||||
&extensions::MimeHandlerViewContainerManager::BindReceiver,
|
||||
render_frame->GetRoutingID()));
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
@@ -337,6 +346,52 @@ void RendererClientBase::DidSetUserAgent(const std::string& user_agent) {
|
||||
#endif
|
||||
}
|
||||
|
||||
content::BrowserPluginDelegate* RendererClientBase::CreateBrowserPluginDelegate(
|
||||
content::RenderFrame* render_frame,
|
||||
const content::WebPluginInfo& info,
|
||||
const std::string& mime_type,
|
||||
const GURL& original_url) {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// TODO(nornagon): check the mime type isn't content::kBrowserPluginMimeType?
|
||||
return new extensions::MimeHandlerViewContainer(render_frame, info, mime_type,
|
||||
original_url);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RendererClientBase::IsPluginHandledExternally(
|
||||
content::RenderFrame* render_frame,
|
||||
const blink::WebElement& plugin_element,
|
||||
const GURL& original_url,
|
||||
const std::string& mime_type) {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) && BUILDFLAG(ENABLE_PLUGINS)
|
||||
DCHECK(plugin_element.HasHTMLTagName("object") ||
|
||||
plugin_element.HasHTMLTagName("embed"));
|
||||
// TODO(nornagon): this info should be shared with the data in
|
||||
// electron_content_client.cc / ComputeBuiltInPlugins.
|
||||
content::WebPluginInfo info;
|
||||
info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
|
||||
info.name = base::UTF8ToUTF16("Chromium PDF Viewer");
|
||||
info.path = base::FilePath::FromUTF8Unsafe(extension_misc::kPdfExtensionId);
|
||||
info.background_color = content::WebPluginInfo::kDefaultBackgroundColor;
|
||||
info.mime_types.emplace_back("application/pdf", "pdf",
|
||||
"Portable Document Format");
|
||||
return extensions::MimeHandlerViewContainerManager::Get(
|
||||
content::RenderFrame::FromWebFrame(
|
||||
plugin_element.GetDocument().GetFrame()),
|
||||
true /* create_if_does_not_exist */)
|
||||
->CreateFrameContainer(plugin_element, original_url, mime_type, info);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RendererClientBase::IsOriginIsolatedPepperPlugin(
|
||||
const base::FilePath& plugin_path) {
|
||||
return plugin_path.value() == kPdfPluginPath;
|
||||
}
|
||||
|
||||
std::unique_ptr<blink::WebPrescientNetworking>
|
||||
RendererClientBase::CreatePrescientNetworking(
|
||||
content::RenderFrame* render_frame) {
|
||||
|
||||
@@ -100,6 +100,16 @@ class RendererClientBase : public content::ContentRendererClient
|
||||
override;
|
||||
bool IsKeySystemsUpdateNeeded() override;
|
||||
void DidSetUserAgent(const std::string& user_agent) override;
|
||||
content::BrowserPluginDelegate* CreateBrowserPluginDelegate(
|
||||
content::RenderFrame* render_frame,
|
||||
const content::WebPluginInfo& info,
|
||||
const std::string& mime_type,
|
||||
const GURL& original_url) override;
|
||||
bool IsPluginHandledExternally(content::RenderFrame* render_frame,
|
||||
const blink::WebElement& plugin_element,
|
||||
const GURL& original_url,
|
||||
const std::string& mime_type) override;
|
||||
bool IsOriginIsolatedPepperPlugin(const base::FilePath& plugin_path) override;
|
||||
|
||||
void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override;
|
||||
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
|
||||
|
||||
@@ -888,8 +888,12 @@ describe('Menu module', function () {
|
||||
|
||||
let output = ''
|
||||
appProcess.stdout.on('data', data => { output += data })
|
||||
appProcess.stderr.on('data', data => { output += data })
|
||||
|
||||
await emittedOnce(appProcess, 'exit')
|
||||
const [code] = await emittedOnce(appProcess, 'exit')
|
||||
if (!output.includes('Window has no menu')) {
|
||||
console.log(code, output)
|
||||
}
|
||||
expect(output).to.include('Window has no menu')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -135,9 +135,18 @@ describe('webContents module', () => {
|
||||
}).to.throw('Unsupported pageSize: i-am-a-bad-pagesize')
|
||||
})
|
||||
|
||||
it('does not crash', () => {
|
||||
it('does not crash with custom margins', () => {
|
||||
expect(() => {
|
||||
w.webContents.print({ silent: true })
|
||||
w.webContents.print({
|
||||
silent: true,
|
||||
margins: {
|
||||
marginType: 'custom',
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
left: 1,
|
||||
right: 1
|
||||
}
|
||||
})
|
||||
}).to.not.throw()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,8 +29,14 @@ describe('WebContentsView', () => {
|
||||
it('does not crash on exit', async () => {
|
||||
const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-webcontentsview.js')
|
||||
const electronPath = process.execPath
|
||||
const appProcess = ChildProcess.spawn(electronPath, [appPath])
|
||||
const appProcess = ChildProcess.spawn(electronPath, ['--enable-logging', appPath])
|
||||
let output = ''
|
||||
appProcess.stdout.on('data', data => { output += data })
|
||||
appProcess.stderr.on('data', data => { output += data })
|
||||
const [code] = await emittedOnce(appProcess, 'exit')
|
||||
if (code !== 0) {
|
||||
console.log(code, output)
|
||||
}
|
||||
expect(code).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,8 +2,10 @@ import { expect } from 'chai'
|
||||
import * as http from 'http'
|
||||
import * as qs from 'querystring'
|
||||
import * as path from 'path'
|
||||
import { session, WebContents, webContents } from 'electron'
|
||||
import * as WebSocket from 'ws'
|
||||
import { ipcMain, session, WebContents, webContents } from 'electron'
|
||||
import { AddressInfo } from 'net'
|
||||
import { emittedOnce } from './events-helpers'
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures')
|
||||
|
||||
@@ -348,4 +350,100 @@ describe('webRequest module', () => {
|
||||
await expect(ajax(defaultURL)).to.eventually.be.rejectedWith('404')
|
||||
})
|
||||
})
|
||||
|
||||
describe('WebSocket connections', () => {
|
||||
it('can be proxyed', async () => {
|
||||
// Setup server.
|
||||
const reqHeaders : { [key: string] : any } = {}
|
||||
const server = http.createServer((req, res) => {
|
||||
reqHeaders[req.url!] = req.headers
|
||||
res.setHeader('foo1', 'bar1')
|
||||
res.end('ok')
|
||||
})
|
||||
const wss = new WebSocket.Server({ noServer: true })
|
||||
wss.on('connection', function connection (ws) {
|
||||
ws.on('message', function incoming (message) {
|
||||
if (message === 'foo') {
|
||||
ws.send('bar')
|
||||
}
|
||||
})
|
||||
})
|
||||
server.on('upgrade', function upgrade (request, socket, head) {
|
||||
const pathname = require('url').parse(request.url).pathname
|
||||
if (pathname === '/websocket') {
|
||||
reqHeaders[request.url] = request.headers
|
||||
wss.handleUpgrade(request, socket, head, function done (ws) {
|
||||
wss.emit('connection', ws, request)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Start server.
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve))
|
||||
const port = String((server.address() as AddressInfo).port)
|
||||
|
||||
// Use a separate session for testing.
|
||||
const ses = session.fromPartition('WebRequestWebSocket')
|
||||
|
||||
// Setup listeners.
|
||||
const receivedHeaders : { [key: string] : any } = {}
|
||||
ses.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
details.requestHeaders.foo = 'bar'
|
||||
callback({ requestHeaders: details.requestHeaders })
|
||||
})
|
||||
ses.webRequest.onHeadersReceived((details, callback) => {
|
||||
const pathname = require('url').parse(details.url).pathname
|
||||
receivedHeaders[pathname] = details.responseHeaders
|
||||
callback({ cancel: false })
|
||||
})
|
||||
ses.webRequest.onResponseStarted((details) => {
|
||||
if (details.url.startsWith('ws://')) {
|
||||
expect(details.responseHeaders!['Connection'][0]).be.equal('Upgrade')
|
||||
} else if (details.url.startsWith('http')) {
|
||||
expect(details.responseHeaders!['foo1'][0]).be.equal('bar1')
|
||||
}
|
||||
})
|
||||
ses.webRequest.onSendHeaders((details) => {
|
||||
if (details.url.startsWith('ws://')) {
|
||||
expect(details.requestHeaders['foo']).be.equal('bar')
|
||||
expect(details.requestHeaders['Upgrade']).be.equal('websocket')
|
||||
} else if (details.url.startsWith('http')) {
|
||||
expect(details.requestHeaders['foo']).be.equal('bar')
|
||||
}
|
||||
})
|
||||
ses.webRequest.onCompleted((details) => {
|
||||
if (details.url.startsWith('ws://')) {
|
||||
expect(details['error']).be.equal('net::ERR_WS_UPGRADE')
|
||||
} else if (details.url.startsWith('http')) {
|
||||
expect(details['error']).be.equal('net::OK')
|
||||
}
|
||||
})
|
||||
|
||||
const contents = (webContents as any).create({
|
||||
session: ses,
|
||||
nodeIntegration: true,
|
||||
webSecurity: false
|
||||
})
|
||||
|
||||
// Cleanup.
|
||||
after(() => {
|
||||
contents.destroy()
|
||||
server.close()
|
||||
ses.webRequest.onBeforeRequest(null)
|
||||
ses.webRequest.onBeforeSendHeaders(null)
|
||||
ses.webRequest.onHeadersReceived(null)
|
||||
ses.webRequest.onResponseStarted(null)
|
||||
ses.webRequest.onSendHeaders(null)
|
||||
ses.webRequest.onCompleted(null)
|
||||
})
|
||||
|
||||
contents.loadFile(path.join(fixturesPath, 'api', 'webrequest.html'), { query: { port } })
|
||||
await emittedOnce(ipcMain, 'websocket-success')
|
||||
|
||||
expect(receivedHeaders['/websocket']['Upgrade'][0]).to.equal('websocket')
|
||||
expect(receivedHeaders['/']['foo1'][0]).to.equal('bar1')
|
||||
expect(reqHeaders['/websocket']['foo']).to.equal('bar')
|
||||
expect(reqHeaders['/']['foo']).to.equal('bar')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -973,117 +973,25 @@ describe('chromium features', () => {
|
||||
|
||||
ifdescribe(features.isPDFViewerEnabled())('PDF Viewer', () => {
|
||||
const pdfSource = url.format({
|
||||
pathname: path.join(fixturesPath, 'assets', 'cat.pdf').replace(/\\/g, '/'),
|
||||
protocol: 'file',
|
||||
slashes: true
|
||||
})
|
||||
const pdfSourceWithParams = url.format({
|
||||
pathname: path.join(fixturesPath, 'assets', 'cat.pdf').replace(/\\/g, '/'),
|
||||
query: {
|
||||
a: 1,
|
||||
b: 2
|
||||
},
|
||||
pathname: path.join(__dirname, 'fixtures', 'cat.pdf').replace(/\\/g, '/'),
|
||||
protocol: 'file',
|
||||
slashes: true
|
||||
})
|
||||
|
||||
const createBrowserWindow = ({ plugins, preload }: { plugins: boolean, preload: string }) => {
|
||||
return new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload: path.join(fixturesPath, 'module', preload),
|
||||
plugins: plugins
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const testPDFIsLoadedInSubFrame = (page: string, preloadFile: string, done: Function) => {
|
||||
const pagePath = url.format({
|
||||
pathname: path.join(fixturesPath, 'pages', page).replace(/\\/g, '/'),
|
||||
protocol: 'file',
|
||||
slashes: true
|
||||
})
|
||||
|
||||
const w = createBrowserWindow({ plugins: true, preload: preloadFile })
|
||||
ipcMain.once('pdf-loaded', (event, state) => {
|
||||
expect(state).to.equal('success')
|
||||
done()
|
||||
})
|
||||
w.webContents.on('page-title-updated', () => {
|
||||
const parsedURL = url.parse(w.webContents.getURL(), true)
|
||||
expect(parsedURL.protocol).to.equal('chrome:')
|
||||
expect(parsedURL.hostname).to.equal('pdf-viewer')
|
||||
expect(parsedURL.query.src).to.equal(pagePath)
|
||||
expect(w.webContents.getTitle()).to.equal('cat.pdf')
|
||||
})
|
||||
w.loadFile(path.join(fixturesPath, 'pages', page))
|
||||
}
|
||||
|
||||
it('opens when loading a pdf resource as top level navigation', (done) => {
|
||||
const w = createBrowserWindow({ plugins: true, preload: 'preload-pdf-loaded.js' })
|
||||
ipcMain.once('pdf-loaded', (event, state) => {
|
||||
expect(state).to.equal('success')
|
||||
done()
|
||||
})
|
||||
w.webContents.on('page-title-updated', () => {
|
||||
const parsedURL = url.parse(w.webContents.getURL(), true)
|
||||
expect(parsedURL.protocol).to.equal('chrome:')
|
||||
expect(parsedURL.hostname).to.equal('pdf-viewer')
|
||||
expect(parsedURL.query.src).to.equal(pdfSource)
|
||||
expect(w.webContents.getTitle()).to.equal('cat.pdf')
|
||||
})
|
||||
w.webContents.loadURL(pdfSource)
|
||||
})
|
||||
|
||||
it('opens a pdf link given params, the query string should be escaped', (done) => {
|
||||
const w = createBrowserWindow({ plugins: true, preload: 'preload-pdf-loaded.js' })
|
||||
ipcMain.once('pdf-loaded', (event, state) => {
|
||||
expect(state).to.equal('success')
|
||||
done()
|
||||
})
|
||||
w.webContents.on('page-title-updated', () => {
|
||||
const parsedURL = url.parse(w.webContents.getURL(), true)
|
||||
expect(parsedURL.protocol).to.equal('chrome:')
|
||||
expect(parsedURL.hostname).to.equal('pdf-viewer')
|
||||
expect(parsedURL.query.src).to.equal(pdfSourceWithParams)
|
||||
expect(parsedURL.query.b).to.be.undefined()
|
||||
expect(parsedURL.search!.endsWith('%3Fa%3D1%26b%3D2')).to.be.true()
|
||||
expect(w.webContents.getTitle()).to.equal('cat.pdf')
|
||||
})
|
||||
w.webContents.loadURL(pdfSourceWithParams)
|
||||
})
|
||||
|
||||
it('should download a pdf when plugins are disabled', async () => {
|
||||
const w = createBrowserWindow({ plugins: false, preload: 'preload-pdf-loaded.js' })
|
||||
w.webContents.loadURL(pdfSource)
|
||||
const [state, filename, mimeType] = await new Promise(resolve => {
|
||||
session.defaultSession.once('will-download', (event, item) => {
|
||||
item.setSavePath(path.join(fixturesPath, 'mock.pdf'))
|
||||
item.on('done', (e, state) => {
|
||||
resolve([state, item.getFilename(), item.getMimeType()])
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(state).to.equal('completed')
|
||||
expect(filename).to.equal('cat.pdf')
|
||||
expect(mimeType).to.equal('application/pdf')
|
||||
fs.unlinkSync(path.join(fixturesPath, 'mock.pdf'))
|
||||
})
|
||||
|
||||
it('should not open when pdf is requested as sub resource', async () => {
|
||||
it('opens when loading a pdf resource as top level navigation', async () => {
|
||||
const w = new BrowserWindow({ show: false })
|
||||
w.loadURL('about:blank')
|
||||
const [status, title] = await w.webContents.executeJavaScript(`fetch(${JSON.stringify(pdfSource)}).then(res => [res.status, document.title])`)
|
||||
expect(status).to.equal(200)
|
||||
expect(title).to.not.equal('cat.pdf')
|
||||
w.loadURL(pdfSource)
|
||||
const [, contents] = await emittedOnce(app, 'web-contents-created')
|
||||
expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html')
|
||||
await emittedOnce(contents, 'did-finish-load')
|
||||
})
|
||||
|
||||
it('opens when loading a pdf resource in a iframe', (done) => {
|
||||
testPDFIsLoadedInSubFrame('pdf-in-iframe.html', 'preload-pdf-loaded-in-subframe.js', done)
|
||||
})
|
||||
|
||||
it('opens when loading a pdf resource in a nested iframe', (done) => {
|
||||
testPDFIsLoadedInSubFrame('pdf-in-nested-iframe.html', 'preload-pdf-loaded-in-nested-subframe.js', done)
|
||||
it('opens when loading a pdf resource in a iframe', async () => {
|
||||
const w = new BrowserWindow({ show: false })
|
||||
w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'pdf-in-iframe.html'))
|
||||
const [, contents] = await emittedOnce(app, 'web-contents-created')
|
||||
expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html')
|
||||
await emittedOnce(contents, 'did-finish-load')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
27
spec-main/fixtures/api/webrequest.html
Normal file
27
spec-main/fixtures/api/webrequest.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<script>
|
||||
var url = new URL(location.href)
|
||||
const port = new URLSearchParams(url.search).get("port")
|
||||
const ipcRenderer = require('electron').ipcRenderer
|
||||
let count = 0
|
||||
function checkFinish() {
|
||||
count++
|
||||
if (count === 2) {
|
||||
ipcRenderer.send('websocket-success')
|
||||
}
|
||||
}
|
||||
|
||||
var conn = new WebSocket(`ws://127.0.0.1:${port}/websocket`)
|
||||
conn.onopen = data => conn.send('foo')
|
||||
conn.onmessage = wsMsg
|
||||
function wsMsg(msg) {
|
||||
if (msg.data === 'bar') {
|
||||
checkFinish()
|
||||
} else {
|
||||
ipcRenderer.send('fail')
|
||||
}
|
||||
}
|
||||
|
||||
fetch(`http://127.0.0.1:${port}/`).then(() => {
|
||||
checkFinish()
|
||||
})
|
||||
</script>
|
||||
6
spec-main/fixtures/pages/pdf-in-iframe.html
Normal file
6
spec-main/fixtures/pages/pdf-in-iframe.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<iframe id="pdf-frame" src="../cat.pdf"/>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,8 +4,10 @@
|
||||
"main": "index.js",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@types/ws": "^7.2.0",
|
||||
"echo": "file:fixtures/native-addon/echo",
|
||||
"q": "^1.5.1"
|
||||
"q": "^1.5.1",
|
||||
"ws": "^7.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chai-as-promised": "^7.1.1",
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@*":
|
||||
version "13.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4"
|
||||
integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==
|
||||
|
||||
"@types/ws@^7.2.0":
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.1.tgz#b800f2b8aee694e2b581113643e20d79dd3b8556"
|
||||
integrity sha512-UEmRNbXFGvfs/sLncf01GuVv6U1mZP3Df0iXWx4kUlikJxbFyFADp95mDn1XDTE2mXpzzoHcKlfFcbytLq4vaA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
ajv-keywords@^3.1.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
|
||||
@@ -128,3 +140,8 @@ worker-loader@^2.0.0:
|
||||
dependencies:
|
||||
loader-utils "^1.0.0"
|
||||
schema-utils "^0.4.0"
|
||||
|
||||
ws@^7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e"
|
||||
integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==
|
||||
|
||||
6
spec/fixtures/pages/pdf-in-iframe.html
vendored
6
spec/fixtures/pages/pdf-in-iframe.html
vendored
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<iframe id="pdf-frame" src="../assets/cat.pdf"/>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<iframe id="outer-frame" src="./pdf-in-iframe.html"/>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user