mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
38 Commits
v24.0.0-ni
...
v24.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd761ec8f7 | ||
|
|
dbdbb6fb3b | ||
|
|
26ee197fe5 | ||
|
|
730a07ad62 | ||
|
|
01b4e3b521 | ||
|
|
899881457b | ||
|
|
9adbf49240 | ||
|
|
c6203d54d0 | ||
|
|
23739c644b | ||
|
|
ce35bda805 | ||
|
|
fcc7a869f2 | ||
|
|
85f41d59ac | ||
|
|
0026fdb78a | ||
|
|
1486cbdf64 | ||
|
|
c303135b02 | ||
|
|
7d46d3ec9d | ||
|
|
8d008c977d | ||
|
|
c6b9340b89 | ||
|
|
397aee7315 | ||
|
|
a30a9c7c4f | ||
|
|
a59f11fdb1 | ||
|
|
58beec1da2 | ||
|
|
4bc6b15f53 | ||
|
|
355f322dbd | ||
|
|
2f79444535 | ||
|
|
a9b6041d38 | ||
|
|
afca3ff965 | ||
|
|
86f99e9cf0 | ||
|
|
ca3145a547 | ||
|
|
37f5881882 | ||
|
|
f20d0b4ecb | ||
|
|
cdb65c15a8 | ||
|
|
c3f02d7df2 | ||
|
|
3b018143b4 | ||
|
|
885c1878d4 | ||
|
|
5ce8dfdcb5 | ||
|
|
4e4ae9ff53 | ||
|
|
55c818d0a8 |
@@ -13,7 +13,7 @@ parameters:
|
||||
run-docs-only:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
|
||||
upload-to-storage:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
@@ -4,7 +4,7 @@ parameters:
|
||||
run-docs-only:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
|
||||
upload-to-storage:
|
||||
type: string
|
||||
default: '1'
|
||||
@@ -141,7 +141,7 @@ env-mas-apple-silicon: &env-mas-apple-silicon
|
||||
|
||||
env-send-slack-notifications: &env-send-slack-notifications
|
||||
NOTIFY_SLACK: true
|
||||
|
||||
|
||||
env-global: &env-global
|
||||
ELECTRON_OUT_DIR: Default
|
||||
|
||||
@@ -486,7 +486,9 @@ step-fix-sync: &step-fix-sync
|
||||
run:
|
||||
name: Fix Sync
|
||||
command: |
|
||||
SEDOPTION="-i"
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
SEDOPTION="-i ''"
|
||||
# Fix Clang Install (wrong binary)
|
||||
rm -rf src/third_party/llvm-build
|
||||
python3 src/tools/clang/scripts/update.py
|
||||
@@ -496,13 +498,16 @@ step-fix-sync: &step-fix-sync
|
||||
# Remove extra output from calling gclient getdep which always calls update_depot_tools
|
||||
sed -i '' "s/Updating depot_tools... //g" esbuild_ensure_file
|
||||
cipd ensure --root src/third_party/devtools-frontend/src/third_party/esbuild -ensure-file esbuild_ensure_file
|
||||
|
||||
# Fix ninja (wrong binary)
|
||||
echo 'infra/3pp/tools/ninja/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/third_party/ninja:infra/3pp/tools/ninja/${platform}'` > ninja_ensure_file
|
||||
sed -i '' "s/Updating depot_tools... //g" ninja_ensure_file
|
||||
cipd ensure --root src/third_party/ninja -ensure-file ninja_ensure_file
|
||||
fi
|
||||
|
||||
# Make sure we are using the right ninja
|
||||
echo 'infra/3pp/tools/ninja/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/third_party/ninja:infra/3pp/tools/ninja/${platform}'` > ninja_ensure_file
|
||||
sed $SEDOPTION "s/Updating depot_tools... //g" ninja_ensure_file
|
||||
cipd ensure --root src/third_party/ninja -ensure-file ninja_ensure_file
|
||||
|
||||
# Explicitly add ninja to the path
|
||||
echo 'export PATH="$PATH:'"$PWD"'/src/third_party/ninja"' >> $BASH_ENV
|
||||
|
||||
cd src/third_party/angle
|
||||
rm .git/objects/info/alternates
|
||||
git remote set-url origin https://chromium.googlesource.com/angle/angle.git
|
||||
@@ -689,7 +694,7 @@ step-show-goma-stats: &step-show-goma-stats
|
||||
run:
|
||||
shell: /bin/bash
|
||||
name: Check goma stats after build
|
||||
command: |
|
||||
command: |
|
||||
set +e
|
||||
set +o pipefail
|
||||
$LOCAL_GOMA_DIR/goma_ctl.py stat
|
||||
@@ -793,7 +798,7 @@ step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
|
||||
elif [ "$TARGET_ARCH" == "arm64" ]; then
|
||||
export MKSNAPSHOT_PATH="clang_x64_v8_arm64"
|
||||
fi
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/mksnapshot" out/Default
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/mksnapshot" out/Default
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/v8_context_snapshot_generator" out/Default
|
||||
if [ "`uname`" == "Linux" ]; then
|
||||
cp "out/Default/$MKSNAPSHOT_PATH/libffmpeg.so" out/Default
|
||||
@@ -1007,7 +1012,7 @@ commands:
|
||||
else
|
||||
echo 'Using Python install from cache'
|
||||
fi
|
||||
sudo installer -pkg python-downloads/python-2.7.18-macosx10.9.pkg -target /
|
||||
sudo installer -pkg python-downloads/python-2.7.18-macosx10.9.pkg -target /
|
||||
fi
|
||||
- save_cache:
|
||||
paths:
|
||||
@@ -1415,7 +1420,7 @@ commands:
|
||||
artifact-key: << parameters.artifact-key >>
|
||||
build-nonproprietary-ffmpeg: << parameters.build-nonproprietary-ffmpeg >>
|
||||
- steps: << parameters.after-build-and-save >>
|
||||
|
||||
|
||||
# Save all data needed for a further tests run.
|
||||
- when:
|
||||
condition: << parameters.persist >>
|
||||
@@ -2119,7 +2124,7 @@ jobs:
|
||||
- electron-tests:
|
||||
artifact-key: darwin-x64
|
||||
|
||||
darwin-testing-arm64-tests:
|
||||
darwin-testing-arm64-tests:
|
||||
executor: apple-silicon
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2234,9 +2239,9 @@ workflows:
|
||||
filters:
|
||||
branches:
|
||||
# Do not run this on forked pull requests
|
||||
ignore: /pull\/[0-9]+/
|
||||
ignore: /pull\/[0-9]+/
|
||||
requires:
|
||||
- linux-arm-testing
|
||||
- linux-arm-testing
|
||||
- linux-arm64-testing:
|
||||
requires:
|
||||
- linux-make-src-cache
|
||||
@@ -2244,7 +2249,7 @@ workflows:
|
||||
filters:
|
||||
branches:
|
||||
# Do not run this on forked pull requests
|
||||
ignore: /pull\/[0-9]+/
|
||||
ignore: /pull\/[0-9]+/
|
||||
requires:
|
||||
- linux-arm64-testing
|
||||
- linux-arm64-testing-gn-check:
|
||||
|
||||
@@ -7,9 +7,9 @@ services:
|
||||
volumes:
|
||||
- ..:/workspaces/gclient/src/electron:cached
|
||||
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
command: /bin/sh -c "while sleep 1000; do :; done"
|
||||
command: /bin/sh -c "while sleep 1000; do :; done"
|
||||
|
||||
user: builduser
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -19,7 +19,7 @@ body:
|
||||
label: Electron Version
|
||||
description: |
|
||||
What version of Electron are you using?
|
||||
|
||||
|
||||
Note: Please only report issues for [currently supported versions of Electron](https://www.electronjs.org/docs/latest/tutorial/support#currently-supported-versions).
|
||||
placeholder: 17.0.0
|
||||
validations:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -29,7 +29,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
2
.github/workflows/issue-labeled.yml
vendored
2
.github/workflows/issue-labeled.yml
vendored
@@ -23,6 +23,6 @@ jobs:
|
||||
|
||||
Would it be possible for you to make a standalone testcase with only the code necessary to reproduce the issue? For example, [Electron Fiddle](https://www.electronjs.org/fiddle) is a great tool for making small test cases and makes it easy to publish your test case to a [gist](https://gist.github.com) that Electron maintainers can use.
|
||||
|
||||
Stand-alone test cases make fixing issues go more smoothly: it ensure everyone's looking at the same issue, it removes all unnecessary variables from the equation, and it can also provide the basis for automated regression tests.
|
||||
Stand-alone test cases make fixing issues go more smoothly: it ensure everyone's looking at the same issue, it removes all unnecessary variables from the equation, and it can also provide the basis for automated regression tests.
|
||||
|
||||
Now adding the `blocked/need-repro` label for this reason. After you make a test case, please link to it in a followup comment. This issue will be closed in 10 days if the above is not addressed.
|
||||
|
||||
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@3de2653986ebd134983c79fe2be5d45cc3d9f4e1
|
||||
- uses: actions/stale@5ebf00ea0e4c1561e9b43a292ed34424fb1d4578 # tag: v6.0.1
|
||||
with:
|
||||
days-before-stale: 90
|
||||
days-before-close: 30
|
||||
@@ -25,8 +25,10 @@ jobs:
|
||||
only-pr-labels: not-a-real-label
|
||||
pending-repro:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() }}
|
||||
needs: stale
|
||||
steps:
|
||||
- uses: actions/stale@3de2653986ebd134983c79fe2be5d45cc3d9f4e1
|
||||
- uses: actions/stale@5ebf00ea0e4c1561e9b43a292ed34424fb1d4578 # tag: v6.0.1
|
||||
with:
|
||||
days-before-stale: -1
|
||||
days-before-close: 10
|
||||
|
||||
2
BUILD.gn
2
BUILD.gn
@@ -632,8 +632,6 @@ source_set("electron_lib") {
|
||||
sources += [
|
||||
"shell/browser/certificate_manager_model.cc",
|
||||
"shell/browser/certificate_manager_model.h",
|
||||
"shell/browser/ui/gtk/menu_util.cc",
|
||||
"shell/browser/ui/gtk/menu_util.h",
|
||||
"shell/browser/ui/gtk_util.cc",
|
||||
"shell/browser/ui/gtk_util.h",
|
||||
]
|
||||
|
||||
@@ -39,7 +39,7 @@ For more installation options and troubleshooting tips, see
|
||||
Each Electron release provides binaries for macOS, Windows, and Linux.
|
||||
|
||||
* macOS (High Sierra and up): Electron provides 64-bit Intel and ARM binaries for macOS. Apple Silicon support was added in Electron 11.
|
||||
* Windows (Windows 10 and up): Electron provides `ia32` (`x86`), `x64` (`amd64`), and `arm64` binaries for Windows. Windows on ARM support was added in Electron 5.0.8. Support for Windows 7 and 8 was [removed in Electron 23, in line with Chromium's Windows deprecation policy](https://www.electronjs.org/blog/windows-7-to-8-1-deprecation-notice).
|
||||
* Windows (Windows 10 and up): Electron provides `ia32` (`x86`), `x64` (`amd64`), and `arm64` binaries for Windows. Windows on ARM support was added in Electron 5.0.8. Support for Windows 7, 8 and 8.1 was [removed in Electron 23, in line with Chromium's Windows deprecation policy](https://www.electronjs.org/blog/windows-7-to-8-1-deprecation-notice).
|
||||
* Linux: The prebuilt binaries of Electron are built on Ubuntu 20.04. They have also been verified to work on:
|
||||
* Ubuntu 14.04 and newer
|
||||
* Fedora 24 and newer
|
||||
|
||||
@@ -18,7 +18,7 @@ environment:
|
||||
PYTHONIOENCODING: UTF-8
|
||||
# Uncomment these lines and set APPVEYOR_RDP_PASSWORD in project settings to enable RDP before bake begins
|
||||
# install:
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
build_script:
|
||||
# Uncomment/change the following line if the hard drive/partition size needs to change
|
||||
# - ps: Resize-Partition -DriveLetter C -Size (256GB) # ensure initial partition size
|
||||
@@ -28,8 +28,8 @@ build_script:
|
||||
if (-not (Test-Path -Path .\src)) {
|
||||
New-Item -Path .\src -ItemType Directory
|
||||
}
|
||||
- ps: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
|
||||
- ps: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
|
||||
- update_depot_tools.bat
|
||||
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
|
||||
# Uncomment the following line if windows deps change
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# NOTE IF CHANGING THIS FILE, ALSO APPLY THE CHANGE TO appveyor.yml
|
||||
# IF APPLICABLE!!!!
|
||||
# NOTE IF CHANGING THIS FILE, ALSO APPLY THE CHANGE TO appveyor.yml
|
||||
# IF APPLICABLE!!!!
|
||||
#
|
||||
#
|
||||
# The config expects the following environment variables to be set:
|
||||
@@ -51,6 +51,8 @@ environment:
|
||||
|
||||
clone_folder: C:\projects\src\electron
|
||||
|
||||
skip_branch_with_pr: true
|
||||
|
||||
# the first failed job cancels other jobs and fails entire build
|
||||
matrix:
|
||||
fast_finish: true
|
||||
@@ -117,7 +119,7 @@ for:
|
||||
$env:NINJA_STATUS="[%r processes, %f/%t @ %o/s : %es] "
|
||||
}
|
||||
- gclient config --name "src\electron" --unmanaged %GCLIENT_EXTRA_ARGS% "https://github.com/electron/electron"
|
||||
# Patches are applied in the image bake. Check depshash to see if patches have changed.
|
||||
# Patches are applied in the image bake. Check depshash to see if patches have changed.
|
||||
- ps: $env:RUN_GCLIENT_SYNC="false"
|
||||
- ps: $depshash_baked = Get-Content .\src\.depshash -Raw
|
||||
- ps: cd src\electron
|
||||
@@ -130,6 +132,7 @@ for:
|
||||
}
|
||||
- if "%RUN_GCLIENT_SYNC%"=="true" ( gclient sync --with_branch_heads --with_tags ) else ( gclient runhooks )
|
||||
- cd src
|
||||
- ps: $env:PATH="$pwd\third_party\ninja;$env:PATH"
|
||||
- set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn
|
||||
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% "
|
||||
- gn check out/Default //electron:electron_lib
|
||||
@@ -258,7 +261,7 @@ for:
|
||||
cd src
|
||||
New-Item .\out\Default\gen\node_headers\Release -Type directory
|
||||
Copy-Item -path .\out\Default\electron.lib -destination .\out\Default\gen\node_headers\Release\node.lib
|
||||
- set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
- set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
- set npm_config_arch=arm64
|
||||
- cd electron
|
||||
# Explicitly set npm_config_arch because the .env doesn't persist
|
||||
@@ -269,7 +272,7 @@ for:
|
||||
- echo Running main test suite & node script/yarn test --runners=main --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
- cd ..
|
||||
- echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
|
||||
|
||||
on_finish:
|
||||
# Uncomment these lines to enable RDP
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
11
appveyor.yml
11
appveyor.yml
@@ -1,5 +1,5 @@
|
||||
# NOTE IF CHANGING THIS FILE, ALSO APPLY THE CHANGE TO appveyor-woa.yml
|
||||
# IF APPLICABLE!!!!
|
||||
# NOTE IF CHANGING THIS FILE, ALSO APPLY THE CHANGE TO appveyor-woa.yml
|
||||
# IF APPLICABLE!!!!
|
||||
#
|
||||
#
|
||||
# The config expects the following environment variables to be set:
|
||||
@@ -49,6 +49,8 @@ environment:
|
||||
|
||||
clone_folder: C:\projects\src\electron
|
||||
|
||||
skip_branch_with_pr: true
|
||||
|
||||
# the first failed job cancels other jobs and fails entire build
|
||||
matrix:
|
||||
fast_finish: true
|
||||
@@ -115,7 +117,7 @@ for:
|
||||
$env:NINJA_STATUS="[%r processes, %f/%t @ %o/s : %es] "
|
||||
}
|
||||
- gclient config --name "src\electron" --unmanaged %GCLIENT_EXTRA_ARGS% "https://github.com/electron/electron"
|
||||
# Patches are applied in the image bake. Check depshash to see if patches have changed.
|
||||
# Patches are applied in the image bake. Check depshash to see if patches have changed.
|
||||
- ps: $env:RUN_GCLIENT_SYNC="false"
|
||||
- ps: $depshash_baked = Get-Content .\src\.depshash -Raw
|
||||
- ps: cd src\electron
|
||||
@@ -128,6 +130,7 @@ for:
|
||||
}
|
||||
- if "%RUN_GCLIENT_SYNC%"=="true" ( gclient sync --with_branch_heads --with_tags ) else ( gclient runhooks )
|
||||
- cd src
|
||||
- ps: $env:PATH="$pwd\third_party\ninja;$env:PATH"
|
||||
- set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn
|
||||
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% "
|
||||
- gn check out/Default //electron:electron_lib
|
||||
@@ -269,7 +272,7 @@ for:
|
||||
- echo "Done verifying mksnapshot"
|
||||
- echo Verifying chromedriver & python electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd%
|
||||
- echo "Done verifying chromedriver"
|
||||
|
||||
|
||||
on_finish:
|
||||
# Uncomment these lines to enable RDP
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
@@ -110,6 +110,7 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* [BrowserView](api/browser-view.md)
|
||||
* [BrowserWindow](api/browser-window.md)
|
||||
* [contentTracing](api/content-tracing.md)
|
||||
* [desktopCapturer](api/desktop-capturer.md)
|
||||
* [dialog](api/dialog.md)
|
||||
* [globalShortcut](api/global-shortcut.md)
|
||||
* [inAppPurchase](api/in-app-purchase.md)
|
||||
@@ -118,19 +119,22 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* [MenuItem](api/menu-item.md)
|
||||
* [MessageChannelMain](api/message-channel-main.md)
|
||||
* [MessagePortMain](api/message-port-main.md)
|
||||
* [nativeTheme](api/native-theme.md)
|
||||
* [net](api/net.md)
|
||||
* [netLog](api/net-log.md)
|
||||
* [nativeTheme](api/native-theme.md)
|
||||
* [Notification](api/notification.md)
|
||||
* [powerMonitor](api/power-monitor.md)
|
||||
* [powerSaveBlocker](api/power-save-blocker.md)
|
||||
* [protocol](api/protocol.md)
|
||||
* [pushNotifications](api/push-notifications.md)
|
||||
* [safeStorage](api/safe-storage.md)
|
||||
* [screen](api/screen.md)
|
||||
* [session](api/session.md)
|
||||
* [ShareMenu](api/share-menu.md)
|
||||
* [systemPreferences](api/system-preferences.md)
|
||||
* [TouchBar](api/touch-bar.md)
|
||||
* [Tray](api/tray.md)
|
||||
* [utilityProcess](api/utility-process.md)
|
||||
* [webContents](api/web-contents.md)
|
||||
* [webFrameMain](api/web-frame-main.md)
|
||||
|
||||
@@ -142,11 +146,10 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
|
||||
### Modules for Both Processes:
|
||||
|
||||
* [clipboard](api/clipboard.md)
|
||||
* [clipboard](api/clipboard.md) (non-sandboxed renderers only)
|
||||
* [crashReporter](api/crash-reporter.md)
|
||||
* [desktopCapturer](api/desktop-capturer.md)
|
||||
* [nativeImage](api/native-image.md)
|
||||
* [shell](api/shell.md)
|
||||
* [shell](api/shell.md) (non-sandboxed renderers only)
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@@ -1566,5 +1566,4 @@ an ARM64 translator (like the macOS
|
||||
or Windows [WOW](https://en.wikipedia.org/wiki/Windows_on_Windows)).
|
||||
|
||||
You can use this property to prompt users to download the arm64 version of
|
||||
your application when they are running the x64 version under Rosetta
|
||||
incorrectly.
|
||||
your application when they are mistakenly running the x64 version under Rosetta or WOW.
|
||||
|
||||
@@ -659,9 +659,9 @@ Emitted when scroll wheel event phase has begun.
|
||||
|
||||
> **Note**
|
||||
> This event is deprecated beginning in Electron 22.0.0. See [Breaking
|
||||
> Changes](breaking-changes.md#deprecated-browserwindow-scroll-touch--events)
|
||||
> Changes](../breaking-changes.md#deprecated-browserwindow-scroll-touch--events)
|
||||
> for details of how to migrate to using the [WebContents
|
||||
> `input-event`](api/web-contents.md#event-input-event) event.
|
||||
> `input-event`](./web-contents.md#event-input-event) event.
|
||||
|
||||
#### Event: 'scroll-touch-end' _macOS_ _Deprecated_
|
||||
|
||||
@@ -669,9 +669,9 @@ Emitted when scroll wheel event phase has ended.
|
||||
|
||||
> **Note**
|
||||
> This event is deprecated beginning in Electron 22.0.0. See [Breaking
|
||||
> Changes](breaking-changes.md#deprecated-browserwindow-scroll-touch--events)
|
||||
> Changes](../breaking-changes.md#deprecated-browserwindow-scroll-touch--events)
|
||||
> for details of how to migrate to using the [WebContents
|
||||
> `input-event`](api/web-contents.md#event-input-event) event.
|
||||
> `input-event`](./web-contents.md#event-input-event) event.
|
||||
|
||||
#### Event: 'scroll-touch-edge' _macOS_ _Deprecated_
|
||||
|
||||
@@ -679,9 +679,9 @@ Emitted when scroll wheel event phase filed upon reaching the edge of element.
|
||||
|
||||
> **Note**
|
||||
> This event is deprecated beginning in Electron 22.0.0. See [Breaking
|
||||
> Changes](breaking-changes.md#deprecated-browserwindow-scroll-touch--events)
|
||||
> Changes](../breaking-changes.md#deprecated-browserwindow-scroll-touch--events)
|
||||
> for details of how to migrate to using the [WebContents
|
||||
> `input-event`](api/web-contents.md#event-input-event) event.
|
||||
> `input-event`](./web-contents.md#event-input-event) event.
|
||||
|
||||
#### Event: 'swipe' _macOS_
|
||||
|
||||
@@ -1036,6 +1036,8 @@ height areas you have within the overall content view.
|
||||
The aspect ratio is not respected when window is resized programmatically with
|
||||
APIs like `win.setSize`.
|
||||
|
||||
To reset an aspect ratio, pass 0 as the `aspectRatio` value: `win.setAspectRatio(0)`.
|
||||
|
||||
#### `win.setBackgroundColor(backgroundColor)`
|
||||
|
||||
* `backgroundColor` string - Color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. The alpha channel is optional for the hex type.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Perform copy and paste operations on the system clipboard.
|
||||
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process) (non-sandboxed only)
|
||||
|
||||
On Linux, there is also a `selection` clipboard. To manipulate it
|
||||
you need to pass `selection` to each method:
|
||||
|
||||
@@ -29,8 +29,9 @@ __Platform Considerations__
|
||||
|
||||
__Linux__
|
||||
|
||||
* Tray icon requires support of [StatusNotifierItem](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/)
|
||||
in user's desktop environment.
|
||||
* Tray icon uses [StatusNotifierItem](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/)
|
||||
by default, when it is not available in user's desktop environment the
|
||||
`GtkStatusIcon` will be used instead.
|
||||
* The `click` event is emitted when the tray icon receives activation from
|
||||
user, however the StatusNotifierItem spec does not specify which action would
|
||||
cause an activation, for some environments it is left mouse click, but for
|
||||
|
||||
@@ -492,6 +492,14 @@ The `focus` and `blur` events of `WebContents` should only be used to detect
|
||||
focus change between different `WebContents` and `BrowserView` in the same
|
||||
window.
|
||||
|
||||
#### Event: 'devtools-open-url'
|
||||
|
||||
Returns:
|
||||
|
||||
* `url` string - URL of the link that was clicked or selected.
|
||||
|
||||
Emitted when a link is clicked in DevTools or 'Open in new tab' is selected for a link in its context menu.
|
||||
|
||||
#### Event: 'devtools-opened'
|
||||
|
||||
Emitted when DevTools is opened.
|
||||
|
||||
@@ -981,6 +981,14 @@ Returns:
|
||||
|
||||
Emitted when mouse moves over a link or the keyboard moves the focus to a link.
|
||||
|
||||
### Event: 'devtools-open-url'
|
||||
|
||||
Returns:
|
||||
|
||||
* `url` string - URL of the link that was clicked or selected.
|
||||
|
||||
Emitted when a link is clicked in DevTools or 'Open in new tab' is selected for a link in its context menu.
|
||||
|
||||
### Event: 'devtools-opened'
|
||||
|
||||
Emitted when DevTools is opened.
|
||||
|
||||
@@ -1433,7 +1433,7 @@ When building native modules for windows, the `win_delay_load_hook` variable in
|
||||
the module's `binding.gyp` must be true (which is the default). If this hook is
|
||||
not present, then the native module will fail to load on Windows, with an error
|
||||
message like `Cannot find module`. See the [native module
|
||||
guide](/docs/tutorial/using-native-node-modules.md) for more.
|
||||
guide](./tutorial/using-native-node-modules.md) for more.
|
||||
|
||||
### Removed: IA32 Linux support
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
|
||||
|
||||
<p>Hit any key with this window focused to see it captured here.</p>
|
||||
<div><span>Last Key Pressed: </span><span id="last-keypress"></span></div>
|
||||
<script src="./renderer.js"></script>
|
||||
|
||||
@@ -19,7 +19,7 @@ function createWindow () {
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connection status: <strong id='status'></strong></h1>
|
||||
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,14 +7,14 @@ function createWindow () {
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
||||
event.preventDefault()
|
||||
if (deviceList && deviceList.length > 0) {
|
||||
callback(deviceList[0].deviceId)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Listen for a message from the renderer to get the response for the Bluetooth pairing.
|
||||
@@ -27,14 +27,14 @@ function createWindow () {
|
||||
bluetoothPinCallback = callback
|
||||
// Send a message to the renderer to prompt the user to confirm the pairing.
|
||||
mainWindow.webContents.send('bluetooth-pairing-request', details)
|
||||
})
|
||||
})
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ document.getElementById('clickme').addEventListener('click',testIt)
|
||||
|
||||
window.electronAPI.bluetoothPairingRequest((event, details) => {
|
||||
const response = {}
|
||||
|
||||
|
||||
switch (details.pairingKind) {
|
||||
case 'confirm': {
|
||||
response.confirmed = confirm(`Do you want to connect to device ${details.deviceId}?`)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<h3>HID devices automatically granted access via <i>setDevicePermissionHandler</i></h3>
|
||||
<div id="granted-devices"></div>
|
||||
|
||||
|
||||
<h3>HID devices automatically granted access via <i>select-hid-device</i></h3>
|
||||
<div id="granted-devices2"></div>
|
||||
|
||||
|
||||
@@ -6,16 +6,16 @@ function createWindow () {
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
|
||||
mainWindow.webContents.session.on('select-hid-device', (event, details, callback) => {
|
||||
//Add events to handle devices being added or removed before the callback on
|
||||
//`select-hid-device` is called.
|
||||
mainWindow.webContents.session.on('hid-device-added', (event, device) => {
|
||||
mainWindow.webContents.session.on('hid-device-added', (event, device) => {
|
||||
console.log('hid-device-added FIRED WITH', device)
|
||||
//Optionally update details.deviceList
|
||||
})
|
||||
|
||||
mainWindow.webContents.session.on('hid-device-removed', (event, device) => {
|
||||
|
||||
mainWindow.webContents.session.on('hid-device-removed', (event, device) => {
|
||||
console.log('hid-device-removed FIRED WITH', device)
|
||||
//Optionally update details.deviceList
|
||||
})
|
||||
@@ -37,13 +37,13 @@ function createWindow () {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ function createWindow () {
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
|
||||
mainWindow.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
|
||||
|
||||
//Add listeners to handle ports being added or removed before the callback for `select-serial-port`
|
||||
@@ -15,7 +15,7 @@ function createWindow () {
|
||||
console.log('serial-port-added FIRED WITH', port)
|
||||
//Optionally update portList to add the new port
|
||||
})
|
||||
|
||||
|
||||
mainWindow.webContents.session.on('serial-port-removed', (event, port) => {
|
||||
console.log('serial-port-removed FIRED WITH', port)
|
||||
//Optionally update portList to remove the port
|
||||
@@ -33,7 +33,7 @@ function createWindow () {
|
||||
if (permission === 'serial' && details.securityOrigin === 'file:///') {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -41,10 +41,10 @@ function createWindow () {
|
||||
if (details.deviceType === 'serial' && details.origin === 'file://') {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
mainWindow.webContents.openDevTools()
|
||||
@@ -52,7 +52,7 @@ function createWindow () {
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<h3>USB devices automatically granted access via <i>setDevicePermissionHandler</i></h3>
|
||||
<div id="granted-devices"></div>
|
||||
|
||||
|
||||
<h3>USB devices automatically granted access via <i>select-usb-device</i></h3>
|
||||
<div id="granted-devices2"></div>
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@ function createWindow () {
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
|
||||
let grantedDeviceThroughPermHandler
|
||||
|
||||
mainWindow.webContents.session.on('select-usb-device', (event, details, callback) => {
|
||||
//Add events to handle devices being added or removed before the callback on
|
||||
//`select-usb-device` is called.
|
||||
mainWindow.webContents.session.on('usb-device-added', (event, device) => {
|
||||
mainWindow.webContents.session.on('usb-device-added', (event, device) => {
|
||||
console.log('usb-device-added FIRED WITH', device)
|
||||
//Optionally update details.deviceList
|
||||
})
|
||||
|
||||
|
||||
mainWindow.webContents.session.on('usb-device-removed', (event, device) => {
|
||||
console.log('usb-device-removed FIRED WITH', device)
|
||||
//Optionally update details.deviceList
|
||||
@@ -31,7 +31,7 @@ function createWindow () {
|
||||
}
|
||||
})
|
||||
if (deviceToReturn) {
|
||||
callback(deviceToReturn.deviceId)
|
||||
callback(deviceToReturn.deviceId)
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
@@ -44,10 +44,10 @@ function createWindow () {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
mainWindow.webContents.session.setDevicePermissionHandler((details) => {
|
||||
if (details.deviceType === 'usb' && details.origin === 'file://') {
|
||||
if (!grantedDeviceThroughPermHandler) {
|
||||
if (!grantedDeviceThroughPermHandler) {
|
||||
grantedDeviceThroughPermHandler = details.device
|
||||
return true
|
||||
} else {
|
||||
@@ -55,13 +55,13 @@ function createWindow () {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
@@ -6,10 +6,10 @@ async function testIt() {
|
||||
const noDevicesFoundMsg = 'No devices found'
|
||||
const grantedDevices = await navigator.usb.getDevices()
|
||||
let grantedDeviceList = ''
|
||||
if (grantedDevices.length > 0) {
|
||||
if (grantedDevices.length > 0) {
|
||||
grantedDevices.forEach(device => {
|
||||
grantedDeviceList += `<hr>${getDeviceDetails(device)}</hr>`
|
||||
})
|
||||
})
|
||||
} else {
|
||||
grantedDeviceList = noDevicesFoundMsg
|
||||
}
|
||||
@@ -21,7 +21,7 @@ async function testIt() {
|
||||
filters: []
|
||||
})
|
||||
grantedDeviceList += `<hr>${getDeviceDetails(device)}</hr>`
|
||||
|
||||
|
||||
} catch (ex) {
|
||||
if (ex.name === 'NotFoundError') {
|
||||
grantedDeviceList = noDevicesFoundMsg
|
||||
|
||||
@@ -19,7 +19,7 @@ function createWindow () {
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
@@ -37,7 +37,7 @@ app.whenReady().then(() => {
|
||||
console.log(value) // will print value to Node console
|
||||
})
|
||||
createWindow()
|
||||
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
})
|
||||
})
|
||||
</code></pre>
|
||||
|
||||
<div class="demo-protip">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>
|
||||
<h1>
|
||||
Open external links and the file manager
|
||||
</h1>
|
||||
<h3>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<p>
|
||||
Electron conveniently allows developers to send notifications with the
|
||||
<a href="https://notifications.spec.whatwg.org/">HTML5 Notification API</a>,
|
||||
<a href="https://notifications.spec.whatwg.org/">HTML5 Notification API</a>,
|
||||
using the currently running operating system’s native notification
|
||||
APIs to display it.
|
||||
</p>
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Clipboard copy</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Main, Renderer (non-sandboxed only)</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="copy-to">Copy</button>
|
||||
@@ -17,8 +18,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const { app, BrowserWindow, ipcMain, clipboard } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
@@ -8,7 +9,7 @@ function createWindow () {
|
||||
height: 400,
|
||||
title: 'Clipboard copy',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +21,18 @@ function createWindow () {
|
||||
})
|
||||
}
|
||||
|
||||
ipcMain.handle('clipboard:writeText', (event, text) => {
|
||||
clipboard.writeText(text)
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', function () {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
5
docs/fiddles/system/clipboard/copy/preload.js
Normal file
5
docs/fiddles/system/clipboard/copy/preload.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('clipboard', {
|
||||
writeText: (text) => ipcRenderer.invoke('clipboard:writeText', text)
|
||||
})
|
||||
@@ -1,5 +1,3 @@
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const copyBtn = document.getElementById('copy-to')
|
||||
const copyInput = document.getElementById('copy-to-input')
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<h1>Clipboard paste</h1>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Both</i>
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Main, Renderer (non-sandboxed only)</i>
|
||||
<div>
|
||||
<div>
|
||||
<button id="paste-to">Paste</button>
|
||||
@@ -17,8 +18,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
<script>
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const { app, BrowserWindow, ipcMain, clipboard } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
@@ -8,7 +9,7 @@ function createWindow () {
|
||||
height: 400,
|
||||
title: 'Clipboard paste',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +21,22 @@ function createWindow () {
|
||||
})
|
||||
}
|
||||
|
||||
ipcMain.handle('clipboard:readText', () => {
|
||||
return clipboard.readText()
|
||||
})
|
||||
|
||||
ipcMain.handle('clipboard:writeText', (event, text) => {
|
||||
clipboard.writeText(text)
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', function () {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
6
docs/fiddles/system/clipboard/paste/preload.js
Normal file
6
docs/fiddles/system/clipboard/paste/preload.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('clipboard', {
|
||||
readText: () => ipcRenderer.invoke('clipboard:readText'),
|
||||
writeText: (text) => ipcRenderer.invoke('clipboard:writeText', text)
|
||||
})
|
||||
@@ -1,9 +1,7 @@
|
||||
const { clipboard } = require('electron')
|
||||
|
||||
const pasteBtn = document.getElementById('paste-to')
|
||||
|
||||
pasteBtn.addEventListener('click', () => {
|
||||
clipboard.writeText('What a demo!')
|
||||
const message = `Clipboard contents: ${clipboard.readText()}`
|
||||
pasteBtn.addEventListener('click', async () => {
|
||||
await clipboard.writeText('What a demo!')
|
||||
const message = `Clipboard contents: ${await clipboard.readText()}`
|
||||
document.getElementById('paste-from').innerHTML = message
|
||||
})
|
||||
|
||||
@@ -23,13 +23,15 @@ if (!gotTheLock) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore()
|
||||
mainWindow.focus()
|
||||
}
|
||||
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${commandLine.pop().slice(0,-1)}`)
|
||||
})
|
||||
|
||||
// Create mainWindow, load the rest of the app, etc...
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
})
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
require('./renderer.js')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -10,7 +10,7 @@
|
||||
<i>Supports: Win, macOS, Linux <span>|</span> Process: Main</i>
|
||||
<div>
|
||||
<p>A frameless window is a window that has no <i>"chrome"</i>,
|
||||
such as toolbars, title bars, status bars, borders, etc. You can make
|
||||
such as toolbars, title bars, status bars, borders, etc. You can make
|
||||
a browser window frameless by setting
|
||||
<code>frame</code> to <code>false</code> when creating the window.</p>
|
||||
<div>
|
||||
|
||||
@@ -61,7 +61,7 @@ const createWindow = () => {
|
||||
|
||||
In this next step, we will create our `BrowserWindow` and tell our application how to handle an event in which an external protocol is clicked.
|
||||
|
||||
This code will be different in Windows compared to MacOS and Linux. This is due to Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. Read more about this [here](https://www.electronjs.org/docs/api/app#apprequestsingleinstancelock).
|
||||
This code will be different in Windows compared to MacOS and Linux. This is due to Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. Read more about this [here](../api/app.md#apprequestsingleinstancelockadditionaldata).
|
||||
|
||||
#### Windows code:
|
||||
|
||||
@@ -77,17 +77,15 @@ if (!gotTheLock) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore()
|
||||
mainWindow.focus()
|
||||
}
|
||||
// the commandLine is array of strings in which last element is deep link url
|
||||
// the url str ends with /
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${commandLine.pop().slice(0, -1)}`)
|
||||
})
|
||||
|
||||
// Create mainWindow, load the rest of the app, etc...
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
// Handle the protocol. In this case, we choose to show an Error Box.
|
||||
app.on('open-url', (event, url) => {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -35,9 +35,15 @@ filenames = {
|
||||
"shell/browser/relauncher_linux.cc",
|
||||
"shell/browser/ui/electron_desktop_window_tree_host_linux.cc",
|
||||
"shell/browser/ui/file_dialog_gtk.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.h",
|
||||
"shell/browser/ui/gtk/menu_util.cc",
|
||||
"shell/browser/ui/gtk/menu_util.h",
|
||||
"shell/browser/ui/message_box_gtk.cc",
|
||||
"shell/browser/ui/tray_icon_gtk.cc",
|
||||
"shell/browser/ui/tray_icon_gtk.h",
|
||||
"shell/browser/ui/status_icon_gtk.cc",
|
||||
"shell/browser/ui/status_icon_gtk.h",
|
||||
"shell/browser/ui/tray_icon_linux.cc",
|
||||
"shell/browser/ui/tray_icon_linux.h",
|
||||
"shell/browser/ui/views/client_frame_view_linux.cc",
|
||||
"shell/browser/ui/views/client_frame_view_linux.h",
|
||||
"shell/common/application_info_linux.cc",
|
||||
|
||||
@@ -9,6 +9,7 @@ export const webViewEvents: Record<string, readonly string[]> = {
|
||||
'dom-ready': [],
|
||||
'console-message': ['level', 'message', 'line', 'sourceId'],
|
||||
'context-menu': ['params'],
|
||||
'devtools-open-url': ['url'],
|
||||
'devtools-opened': [],
|
||||
'devtools-closed': [],
|
||||
'devtools-focused': [],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl';
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { WEB_VIEW_ATTRIBUTES, WEB_VIEW_ERROR_MESSAGES } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
|
||||
const resolveURL = function (url?: string | null) {
|
||||
return url ? new URL(url, location.href).href : '';
|
||||
@@ -76,7 +76,7 @@ export class PartitionAttribute extends WebViewAttribute {
|
||||
public validPartitionId = true
|
||||
|
||||
constructor (public webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.PARTITION, webViewImpl);
|
||||
}
|
||||
|
||||
public handleMutation = (oldValue: any, newValue: any) => {
|
||||
@@ -84,13 +84,13 @@ export class PartitionAttribute extends WebViewAttribute {
|
||||
|
||||
// The partition cannot change if the webview has already navigated.
|
||||
if (!this.webViewImpl.beforeFirstNavigation) {
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED);
|
||||
console.error(WEB_VIEW_ERROR_MESSAGES.ALREADY_NAVIGATED);
|
||||
this.setValueIgnoreMutation(oldValue);
|
||||
return;
|
||||
}
|
||||
if (newValue === 'persist:') {
|
||||
this.validPartitionId = false;
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE);
|
||||
console.error(WEB_VIEW_ERROR_MESSAGES.INVALID_PARTITION_ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ export class SrcAttribute extends WebViewAttribute {
|
||||
public observer!: MutationObserver;
|
||||
|
||||
constructor (public webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.SRC, webViewImpl);
|
||||
this.setupMutationObserver();
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ export class SrcAttribute extends WebViewAttribute {
|
||||
}
|
||||
|
||||
public parse () {
|
||||
if (!this.webViewImpl.elementAttached || !(this.webViewImpl.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION) as PartitionAttribute).validPartitionId || !this.getValue()) {
|
||||
if (!this.webViewImpl.elementAttached || !(this.webViewImpl.attributes.get(WEB_VIEW_ATTRIBUTES.PARTITION) as PartitionAttribute).validPartitionId || !this.getValue()) {
|
||||
return;
|
||||
}
|
||||
if (this.webViewImpl.guestInstanceId == null) {
|
||||
@@ -176,12 +176,12 @@ export class SrcAttribute extends WebViewAttribute {
|
||||
// Navigate to |this.src|.
|
||||
const opts: Record<string, string> = {};
|
||||
|
||||
const httpreferrer = this.webViewImpl.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER)!.getValue();
|
||||
const httpreferrer = this.webViewImpl.attributes.get(WEB_VIEW_ATTRIBUTES.HTTPREFERRER)!.getValue();
|
||||
if (httpreferrer) {
|
||||
opts.httpReferrer = httpreferrer;
|
||||
}
|
||||
|
||||
const useragent = this.webViewImpl.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT)!.getValue();
|
||||
const useragent = this.webViewImpl.attributes.get(WEB_VIEW_ATTRIBUTES.USERAGENT)!.getValue();
|
||||
if (useragent) {
|
||||
opts.userAgent = useragent;
|
||||
}
|
||||
@@ -196,21 +196,21 @@ export class SrcAttribute extends WebViewAttribute {
|
||||
// Attribute specifies HTTP referrer.
|
||||
class HttpReferrerAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.HTTPREFERRER, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute specifies user agent
|
||||
class UserAgentAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.USERAGENT, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that set preload script.
|
||||
class PreloadAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.PRELOAD, webViewImpl);
|
||||
}
|
||||
|
||||
public getValue () {
|
||||
@@ -222,7 +222,7 @@ class PreloadAttribute extends WebViewAttribute {
|
||||
const protocol = preload.substr(0, 5);
|
||||
|
||||
if (protocol !== 'file:') {
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE);
|
||||
console.error(WEB_VIEW_ERROR_MESSAGES.INVALID_PRELOAD_ATTRIBUTE);
|
||||
preload = '';
|
||||
}
|
||||
|
||||
@@ -233,39 +233,39 @@ class PreloadAttribute extends WebViewAttribute {
|
||||
// Attribute that specifies the blink features to be enabled.
|
||||
class BlinkFeaturesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.BLINKFEATURES, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that specifies the blink features to be disabled.
|
||||
class DisableBlinkFeaturesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.DISABLEBLINKFEATURES, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that specifies the web preferences to be enabled.
|
||||
class WebPreferencesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl);
|
||||
super(WEB_VIEW_ATTRIBUTES.WEBPREFERENCES, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up all of the webview attributes.
|
||||
export function setupWebViewAttributes (self: WebViewImpl) {
|
||||
return new Map<string, WebViewAttribute>([
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, new PartitionAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, new SrcAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, new HttpReferrerAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, new UserAgentAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, new PreloadAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, new BlinkFeaturesAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, new DisableBlinkFeaturesAttribute(self)],
|
||||
[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, new WebPreferencesAttribute(self)]
|
||||
[WEB_VIEW_ATTRIBUTES.PARTITION, new PartitionAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.SRC, new SrcAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.HTTPREFERRER, new HttpReferrerAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.USERAGENT, new UserAgentAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.NODEINTEGRATION, new BooleanAttribute(WEB_VIEW_ATTRIBUTES.NODEINTEGRATION, self)],
|
||||
[WEB_VIEW_ATTRIBUTES.NODEINTEGRATIONINSUBFRAMES, new BooleanAttribute(WEB_VIEW_ATTRIBUTES.NODEINTEGRATIONINSUBFRAMES, self)],
|
||||
[WEB_VIEW_ATTRIBUTES.PLUGINS, new BooleanAttribute(WEB_VIEW_ATTRIBUTES.PLUGINS, self)],
|
||||
[WEB_VIEW_ATTRIBUTES.DISABLEWEBSECURITY, new BooleanAttribute(WEB_VIEW_ATTRIBUTES.DISABLEWEBSECURITY, self)],
|
||||
[WEB_VIEW_ATTRIBUTES.ALLOWPOPUPS, new BooleanAttribute(WEB_VIEW_ATTRIBUTES.ALLOWPOPUPS, self)],
|
||||
[WEB_VIEW_ATTRIBUTES.PRELOAD, new PreloadAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.BLINKFEATURES, new BlinkFeaturesAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.DISABLEBLINKFEATURES, new DisableBlinkFeaturesAttribute(self)],
|
||||
[WEB_VIEW_ATTRIBUTES.WEBPREFERENCES, new WebPreferencesAttribute(self)]
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
export const enum WEB_VIEW_CONSTANTS {
|
||||
// Attributes.
|
||||
ATTRIBUTE_NAME = 'name',
|
||||
ATTRIBUTE_PARTITION = 'partition',
|
||||
ATTRIBUTE_SRC = 'src',
|
||||
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
|
||||
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
|
||||
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
|
||||
ATTRIBUTE_PLUGINS = 'plugins',
|
||||
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
|
||||
ATTRIBUTE_ALLOWPOPUPS = 'allowpopups',
|
||||
ATTRIBUTE_PRELOAD = 'preload',
|
||||
ATTRIBUTE_USERAGENT = 'useragent',
|
||||
ATTRIBUTE_BLINKFEATURES = 'blinkfeatures',
|
||||
ATTRIBUTE_DISABLEBLINKFEATURES = 'disableblinkfeatures',
|
||||
ATTRIBUTE_WEBPREFERENCES = 'webpreferences',
|
||||
|
||||
// Error messages.
|
||||
ERROR_MSG_ALREADY_NAVIGATED = 'The object has already navigated, so its partition cannot be changed.',
|
||||
ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.',
|
||||
ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE = 'Only "file:" protocol is supported in "preload" attribute.',
|
||||
ERROR_MSG_NOT_ATTACHED = 'The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.'
|
||||
export const enum WEB_VIEW_ATTRIBUTES {
|
||||
NAME = 'name',
|
||||
PARTITION = 'partition',
|
||||
SRC = 'src',
|
||||
HTTPREFERRER = 'httpreferrer',
|
||||
NODEINTEGRATION = 'nodeintegration',
|
||||
NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
|
||||
PLUGINS = 'plugins',
|
||||
DISABLEWEBSECURITY = 'disablewebsecurity',
|
||||
ALLOWPOPUPS = 'allowpopups',
|
||||
PRELOAD = 'preload',
|
||||
USERAGENT = 'useragent',
|
||||
BLINKFEATURES = 'blinkfeatures',
|
||||
DISABLEBLINKFEATURES = 'disableblinkfeatures',
|
||||
WEBPREFERENCES = 'webpreferences',
|
||||
}
|
||||
|
||||
export const enum WEB_VIEW_ERROR_MESSAGES {
|
||||
// Error messages.
|
||||
ALREADY_NAVIGATED = 'The object has already navigated, so its partition cannot be changed.',
|
||||
INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.',
|
||||
INVALID_PRELOAD_ATTRIBUTE = 'Only "file:" protocol is supported in "preload" attribute.',
|
||||
NOT_ATTACHED = 'The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.'
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// which runs in browserify environment instead of Node environment, all native
|
||||
// modules must be passed from outside, all included files must be plain JS.
|
||||
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { WEB_VIEW_ATTRIBUTES, WEB_VIEW_ERROR_MESSAGES } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { WebViewImpl, WebViewImplHooks, setupMethods } from '@electron/internal/renderer/web-view/web-view-impl';
|
||||
import type { SrcAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||
|
||||
@@ -19,19 +19,19 @@ const defineWebViewElement = (hooks: WebViewImplHooks) => {
|
||||
return class WebViewElement extends HTMLElement {
|
||||
static get observedAttributes () {
|
||||
return [
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES
|
||||
WEB_VIEW_ATTRIBUTES.PARTITION,
|
||||
WEB_VIEW_ATTRIBUTES.SRC,
|
||||
WEB_VIEW_ATTRIBUTES.HTTPREFERRER,
|
||||
WEB_VIEW_ATTRIBUTES.USERAGENT,
|
||||
WEB_VIEW_ATTRIBUTES.NODEINTEGRATION,
|
||||
WEB_VIEW_ATTRIBUTES.NODEINTEGRATIONINSUBFRAMES,
|
||||
WEB_VIEW_ATTRIBUTES.PLUGINS,
|
||||
WEB_VIEW_ATTRIBUTES.DISABLEWEBSECURITY,
|
||||
WEB_VIEW_ATTRIBUTES.ALLOWPOPUPS,
|
||||
WEB_VIEW_ATTRIBUTES.PRELOAD,
|
||||
WEB_VIEW_ATTRIBUTES.BLINKFEATURES,
|
||||
WEB_VIEW_ATTRIBUTES.DISABLEBLINKFEATURES,
|
||||
WEB_VIEW_ATTRIBUTES.WEBPREFERENCES
|
||||
];
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ const defineWebViewElement = (hooks: WebViewImplHooks) => {
|
||||
getWebContentsId () {
|
||||
const internal = internals.get(this);
|
||||
if (!internal || !internal.guestInstanceId) {
|
||||
throw new Error(WEB_VIEW_CONSTANTS.ERROR_MSG_NOT_ATTACHED);
|
||||
throw new Error(WEB_VIEW_ERROR_MESSAGES.NOT_ATTACHED);
|
||||
}
|
||||
return internal.guestInstanceId;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ const defineWebViewElement = (hooks: WebViewImplHooks) => {
|
||||
dispatchEvent: internal.dispatchEvent.bind(internal)
|
||||
});
|
||||
internal.elementAttached = true;
|
||||
(internal.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC) as SrcAttribute).parse();
|
||||
(internal.attributes.get(WEB_VIEW_ATTRIBUTES.SRC) as SrcAttribute).parse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type * as guestViewInternalModule from '@electron/internal/renderer/web-view/guest-view-internal';
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { WEB_VIEW_ATTRIBUTES } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods';
|
||||
import type { WebViewAttribute, PartitionAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||
import { setupWebViewAttributes } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||
@@ -75,7 +75,7 @@ export class WebViewImpl {
|
||||
}
|
||||
|
||||
this.beforeFirstNavigation = true;
|
||||
(this.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION) as PartitionAttribute).validPartitionId = true;
|
||||
(this.attributes.get(WEB_VIEW_ATTRIBUTES.PARTITION) as PartitionAttribute).validPartitionId = true;
|
||||
|
||||
// Since attachment swaps a local frame for a remote frame, we need our
|
||||
// internal iframe element to be local again before we can reattach.
|
||||
@@ -145,13 +145,13 @@ export class WebViewImpl {
|
||||
|
||||
// Updates state upon loadcommit.
|
||||
onLoadCommit (props: Record<string, any>) {
|
||||
const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC);
|
||||
const oldValue = this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTES.SRC);
|
||||
const newValue = props.url;
|
||||
if (props.isMainFrame && (oldValue !== newValue)) {
|
||||
// Touching the src attribute triggers a navigation. To avoid
|
||||
// triggering a page reload on every guest-initiated navigation,
|
||||
// we do not handle this mutation.
|
||||
this.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC)!.setValueIgnoreMutation(newValue);
|
||||
this.attributes.get(WEB_VIEW_ATTRIBUTES.SRC)!.setValueIgnoreMutation(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ export class WebViewImpl {
|
||||
}
|
||||
|
||||
onAttach (storagePartitionId: number) {
|
||||
return this.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION)!.setValue(storagePartitionId);
|
||||
return this.attributes.get(WEB_VIEW_ATTRIBUTES.PARTITION)!.setValue(storagePartitionId);
|
||||
}
|
||||
|
||||
buildParams () {
|
||||
|
||||
10
package.json
10
package.json
@@ -5,6 +5,7 @@
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
"@azure/storage-blob": "^12.9.0",
|
||||
"@dsanders11/vscode-markdown-languageservice": "^0.3.0-alpha.4",
|
||||
"@electron/asar": "^3.2.1",
|
||||
"@electron/docs-parser": "^1.0.0",
|
||||
"@electron/fiddle-core": "^1.0.4",
|
||||
@@ -38,7 +39,7 @@
|
||||
"check-for-leaks": "^1.2.1",
|
||||
"colors": "1.4.0",
|
||||
"dotenv-safe": "^4.0.4",
|
||||
"dugite": "^1.103.0",
|
||||
"dugite": "^2.3.0",
|
||||
"eslint": "^7.4.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
@@ -56,6 +57,7 @@
|
||||
"lint": "^1.1.2",
|
||||
"lint-staged": "^10.2.11",
|
||||
"markdownlint-cli": "^0.33.0",
|
||||
"mdast-util-from-markdown": "^1.2.0",
|
||||
"minimist": "^1.2.6",
|
||||
"null-loader": "^4.0.0",
|
||||
"pre-flight": "^1.1.0",
|
||||
@@ -72,6 +74,10 @@
|
||||
"ts-loader": "^8.0.2",
|
||||
"ts-node": "6.2.0",
|
||||
"typescript": "^4.5.5",
|
||||
"unist-util-visit": "^4.1.1",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-uri": "^3.0.6",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"wrapper-webpack-plugin": "^2.2.0"
|
||||
@@ -89,7 +95,7 @@
|
||||
"lint:py": "node ./script/lint.js --py",
|
||||
"lint:gn": "node ./script/lint.js --gn",
|
||||
"lint:docs": "remark docs -qf && npm run lint:js-in-markdown && npm run create-typescript-definitions && npm run lint:docs-relative-links && npm run lint:markdownlint",
|
||||
"lint:docs-relative-links": "python3 ./script/check-relative-doc-links.py",
|
||||
"lint:docs-relative-links": "ts-node script/lint-docs-links.ts",
|
||||
"lint:markdownlint": "markdownlint -r ./script/markdownlint-emd001.js \"*.md\" \"docs/**/*.md\"",
|
||||
"lint:js-in-markdown": "standard-markdown docs",
|
||||
"create-api-json": "node script/create-api-json.js",
|
||||
|
||||
@@ -78,7 +78,7 @@ index 796c7f06fa6063ac409f3fef53871e18d4c07838..6575e833388bcc3ac487a409027d984b
|
||||
: PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3;
|
||||
}
|
||||
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
|
||||
index 017e421d0ca3e6154b849373abbcb8d75b369e60..463f743af273b0f2f5afe6904ed77e7e99a91f46 100644
|
||||
index 017e421d0ca3e6154b849373abbcb8d75b369e60..aaadfba1735b21e17b66e8efe69f8a5ab4a312b0 100644
|
||||
--- a/chrome/browser/printing/print_view_manager_base.cc
|
||||
+++ b/chrome/browser/printing/print_view_manager_base.cc
|
||||
@@ -29,8 +29,6 @@
|
||||
@@ -264,6 +264,15 @@ index 017e421d0ca3e6154b849373abbcb8d75b369e60..463f743af273b0f2f5afe6904ed77e7e
|
||||
|
||||
auto callback_wrapper =
|
||||
base::BindOnce(&PrintViewManagerBase::UpdatePrintSettingsReply,
|
||||
@@ -630,7 +664,7 @@ void PrintViewManagerBase::UpdatePrintSettings(
|
||||
void PrintViewManagerBase::IsPrintingEnabled(
|
||||
IsPrintingEnabledCallback callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
- std::move(callback).Run(printing_enabled_.GetValue());
|
||||
+ std::move(callback).Run(true);
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
|
||||
@@ -646,14 +680,14 @@ void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
|
||||
// didn't happen for some reason.
|
||||
bad_message::ReceivedBadMessage(
|
||||
|
||||
@@ -51,7 +51,7 @@ index 7cec4e9a3e3675ba75d66a44ed4e142d13ca1821..0c7aad193442a7e5cab62638441969a7
|
||||
}
|
||||
}
|
||||
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
|
||||
index 6f25b2e67ab77613c6ed63c227bb875d5461f45f..d1527b859bbea15fdf30622fc8f2700bde5b4591 100644
|
||||
index 6f25b2e67ab77613c6ed63c227bb875d5461f45f..010fa8f78a21a8146879849e2e887332e2694e51 100644
|
||||
--- a/lib/internal/modules/esm/translators.js
|
||||
+++ b/lib/internal/modules/esm/translators.js
|
||||
@@ -154,7 +154,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
|
||||
@@ -63,15 +63,19 @@ index 6f25b2e67ab77613c6ed63c227bb875d5461f45f..d1527b859bbea15fdf30622fc8f2700b
|
||||
[...exportNames] : ['default', ...exportNames];
|
||||
|
||||
return new ModuleWrap(url, undefined, namesWithDefault, function() {
|
||||
@@ -173,7 +173,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
|
||||
@@ -173,9 +173,9 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
|
||||
}
|
||||
}
|
||||
|
||||
- for (const exportName of exportNames) {
|
||||
- if (!ObjectPrototypeHasOwnProperty(exports, exportName) ||
|
||||
- exportName === 'default')
|
||||
+ for (const exportName of namesWithDefault) {
|
||||
if (!ObjectPrototypeHasOwnProperty(exports, exportName) ||
|
||||
exportName === 'default')
|
||||
+ if (exportName === 'default' ||
|
||||
+ !ObjectPrototypeHasOwnProperty(exports, exportName))
|
||||
continue;
|
||||
// We might trigger a getter -> dont fail.
|
||||
let value;
|
||||
diff --git a/lib/internal/url.js b/lib/internal/url.js
|
||||
index 40b25f6890b5db721923ba2e9cc351e514ca22bc..288f3253b4c686d1b061dfcdf18dc95794943d87 100644
|
||||
--- a/lib/internal/url.js
|
||||
|
||||
@@ -7,7 +7,7 @@ Wc++98-compat-extra-semi is turned on for Electron so this
|
||||
patch fixes that error in node.
|
||||
|
||||
diff --git a/src/node_serdes.cc b/src/node_serdes.cc
|
||||
index 0cd76078218433b46c17f350e3ba6073987438cf..ba13061b6aa7fd8f877aa456db9d352a847e682a 100644
|
||||
index 97917c91c06dc47dfa6be2c194944cdc93e6bd7f..177390a24eb6490b128e22c104014e80f338c9d9 100644
|
||||
--- a/src/node_serdes.cc
|
||||
+++ b/src/node_serdes.cc
|
||||
@@ -32,7 +32,7 @@ namespace serdes {
|
||||
|
||||
@@ -155,7 +155,7 @@ index efbdbeabf5a6afb658cbdc7888f94952e55f4f71..8b37639361e8902d7e1481071d3ec24b
|
||||
|
||||
// Delegate to V8's allocator for compatibility with the V8 memory cage.
|
||||
diff --git a/src/node_serdes.cc b/src/node_serdes.cc
|
||||
index 45a16d9de43703c2115dde85c9faae3a04be2a88..0cd76078218433b46c17f350e3ba6073987438cf 100644
|
||||
index 45a16d9de43703c2115dde85c9faae3a04be2a88..97917c91c06dc47dfa6be2c194944cdc93e6bd7f 100644
|
||||
--- a/src/node_serdes.cc
|
||||
+++ b/src/node_serdes.cc
|
||||
@@ -29,6 +29,11 @@ using v8::ValueSerializer;
|
||||
@@ -219,17 +219,32 @@ index 45a16d9de43703c2115dde85c9faae3a04be2a88..0cd76078218433b46c17f350e3ba6073
|
||||
Maybe<bool> SerializerContext::WriteHostObject(Isolate* isolate,
|
||||
Local<Object> input) {
|
||||
MaybeLocal<Value> ret;
|
||||
@@ -211,7 +240,12 @@ void SerializerContext::ReleaseBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -209,9 +238,14 @@ void SerializerContext::ReleaseBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
// Note: Both ValueSerializer and this Buffer::New() variant use malloc()
|
||||
// as the underlying allocator.
|
||||
std::pair<uint8_t*, size_t> ret = ctx->serializer_.Release();
|
||||
auto buf = Buffer::New(ctx->env(),
|
||||
reinterpret_cast<char*>(ret.first),
|
||||
- auto buf = Buffer::New(ctx->env(),
|
||||
- reinterpret_cast<char*>(ret.first),
|
||||
- ret.second);
|
||||
+ ret.second,
|
||||
+ [](char* data, void* hint){
|
||||
+ if (data)
|
||||
+ GetAllocator()->Free(data, reinterpret_cast<size_t>(hint));
|
||||
+ },
|
||||
+ reinterpret_cast<void*>(ctx->last_length_));
|
||||
+ std::unique_ptr<v8::BackingStore> bs =
|
||||
+ v8::ArrayBuffer::NewBackingStore(reinterpret_cast<char*>(ret.first), ret.second,
|
||||
+ [](void* data, size_t length, void* deleter_data) {
|
||||
+ if (data) GetAllocator()->Free(reinterpret_cast<char*>(data), length);
|
||||
+ }, nullptr);
|
||||
+ Local<ArrayBuffer> ab = v8::ArrayBuffer::New(ctx->env()->isolate(), std::move(bs));
|
||||
+
|
||||
+ auto buf = Buffer::New(ctx->env(), ab, 0, ret.second);
|
||||
|
||||
if (!buf.IsEmpty()) {
|
||||
args.GetReturnValue().Set(buf.ToLocalChecked());
|
||||
diff --git a/test/parallel/test-v8-serialize-leak.js b/test/parallel/test-v8-serialize-leak.js
|
||||
index a90c398adcdaf30491a0fecdcf00895038d62e69..f5b8a1430ad2033eae06ca0157af2fb51d3f36a5 100644
|
||||
--- a/test/parallel/test-v8-serialize-leak.js
|
||||
+++ b/test/parallel/test-v8-serialize-leak.js
|
||||
@@ -23,5 +23,5 @@ const after = process.memoryUsage.rss();
|
||||
if (process.config.variables.asan) {
|
||||
assert(after < before * 10, `asan: before=${before} after=${after}`);
|
||||
} else {
|
||||
- assert(after < before * 2, `before=${before} after=${after}`);
|
||||
+ assert(after < before * 3, `before=${before} after=${after}`);
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
DOCS_DIR = os.path.join(SOURCE_ROOT, 'docs')
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(SOURCE_ROOT)
|
||||
|
||||
filepaths = []
|
||||
totalDirs = 0
|
||||
try:
|
||||
for root, dirs, files in os.walk(DOCS_DIR):
|
||||
totalDirs += len(dirs)
|
||||
for f in files:
|
||||
if f.endswith('.md'):
|
||||
filepaths.append(os.path.join(root, f))
|
||||
except KeyboardInterrupt:
|
||||
print('Keyboard interruption. Please try again.')
|
||||
return 0
|
||||
|
||||
totalBrokenLinks = 0
|
||||
for path in filepaths:
|
||||
totalBrokenLinks += getBrokenLinks(path)
|
||||
|
||||
print('Parsed through ' + str(len(filepaths)) +
|
||||
' files within docs directory and its ' +
|
||||
str(totalDirs) + ' subdirectories.')
|
||||
print('Found ' + str(totalBrokenLinks) + ' broken relative links.')
|
||||
return totalBrokenLinks
|
||||
|
||||
|
||||
def getBrokenLinks(filepath):
|
||||
currentDir = os.path.dirname(filepath)
|
||||
brokenLinks = []
|
||||
|
||||
try:
|
||||
f = open(filepath, 'r', encoding="utf-8")
|
||||
lines = f.readlines()
|
||||
except KeyboardInterrupt:
|
||||
print('Keyboard interruption while parsing. Please try again.')
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
linkRegexLink = re.compile('\[(.*?)\]\((?P<link>(.*?))\)')
|
||||
referenceLinkRegex = re.compile(
|
||||
'^\s{0,3}\[.*?\]:\s*(?P<link>[^<\s]+|<[^<>\r\n]+>)'
|
||||
)
|
||||
links = []
|
||||
for line in lines:
|
||||
matchLinks = linkRegexLink.search(line)
|
||||
matchReferenceLinks = referenceLinkRegex.search(line)
|
||||
if matchLinks:
|
||||
relativeLink = matchLinks.group('link')
|
||||
if not str(relativeLink).startswith('http'):
|
||||
links.append(relativeLink)
|
||||
if matchReferenceLinks:
|
||||
referenceLink = matchReferenceLinks.group('link').strip('<>')
|
||||
if not str(referenceLink).startswith('http'):
|
||||
links.append(referenceLink)
|
||||
|
||||
for link in links:
|
||||
sections = link.split('#')
|
||||
if len(sections) < 2:
|
||||
if not os.path.isfile(os.path.join(currentDir, link)):
|
||||
brokenLinks.append(link)
|
||||
elif str(link).startswith('#'):
|
||||
if not checkSections(sections, lines):
|
||||
brokenLinks.append(link)
|
||||
else:
|
||||
tempFile = os.path.join(currentDir, sections[0])
|
||||
if os.path.isfile(tempFile):
|
||||
try:
|
||||
newFile = open(tempFile, 'r', encoding="utf-8")
|
||||
newLines = newFile.readlines()
|
||||
except KeyboardInterrupt:
|
||||
print('Keyboard interruption while parsing. Please try again.')
|
||||
finally:
|
||||
newFile.close()
|
||||
|
||||
if not checkSections(sections, newLines):
|
||||
brokenLinks.append(link)
|
||||
else:
|
||||
brokenLinks.append(link)
|
||||
|
||||
|
||||
print_errors(filepath, brokenLinks)
|
||||
return len(brokenLinks)
|
||||
|
||||
|
||||
def checkSections(sections, lines):
|
||||
invalidCharsRegex = '[^A-Za-z0-9_ \-]'
|
||||
sectionHeader = sections[1]
|
||||
regexSectionTitle = re.compile('# (?P<header>.*)')
|
||||
for line in lines:
|
||||
matchHeader = regexSectionTitle.search(line)
|
||||
if matchHeader:
|
||||
# This does the following to slugify a header name:
|
||||
# * Replace whitespace with dashes
|
||||
# * Strip anything that's not alphanumeric or a dash
|
||||
# * Anything quoted with backticks (`) is an exception and will
|
||||
# not have underscores stripped
|
||||
matchHeader = str(matchHeader.group('header')).replace(' ', '-')
|
||||
matchHeader = ''.join(
|
||||
map(
|
||||
lambda match: re.sub(invalidCharsRegex, '', match[0])
|
||||
+ re.sub(invalidCharsRegex + '|_', '', match[1]),
|
||||
re.findall('(`[^`]+`)|([^`]+)', matchHeader),
|
||||
)
|
||||
)
|
||||
if matchHeader.lower() == sectionHeader:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def print_errors(filepath, brokenLink):
|
||||
if brokenLink:
|
||||
print("File Location: " + filepath)
|
||||
for link in brokenLink:
|
||||
print("\tBroken links: " + link)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -17,7 +17,7 @@ def copy_debug_from_binaries(directory, out_dir, target_cpu, compress):
|
||||
def copy_debug_from_binary(binary_path, out_dir, target_cpu, compress):
|
||||
if PLATFORM == 'linux' and target_cpu in ('x86', 'arm', 'arm64'):
|
||||
# Skip because no objcopy binary on the given target.
|
||||
return
|
||||
return
|
||||
debug_name = get_debug_name(binary_path)
|
||||
cmd = ['objcopy', '--only-keep-debug']
|
||||
if compress:
|
||||
|
||||
@@ -21,6 +21,10 @@ def run_node_configure(target_cpu):
|
||||
# Work around "No acceptable ASM compiler found" error on some System,
|
||||
# it breaks nothing since Electron does not use OpenSSL.
|
||||
args += ['--openssl-no-asm']
|
||||
|
||||
# Enable whole-program optimization for electron native modules.
|
||||
if sys.platform == "win32":
|
||||
args += ['--with-ltcg']
|
||||
subprocess.check_call([sys.executable, configure] + args)
|
||||
|
||||
def read_node_config_gypi():
|
||||
|
||||
334
script/lib/markdown.ts
Normal file
334
script/lib/markdown.ts
Normal file
@@ -0,0 +1,334 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as MarkdownIt from 'markdown-it';
|
||||
import {
|
||||
githubSlugifier,
|
||||
resolveInternalDocumentLink,
|
||||
ExternalHref,
|
||||
FileStat,
|
||||
HrefKind,
|
||||
InternalHref,
|
||||
IMdLinkComputer,
|
||||
IMdParser,
|
||||
ITextDocument,
|
||||
IWorkspace,
|
||||
MdLink,
|
||||
MdLinkKind
|
||||
} from '@dsanders11/vscode-markdown-languageservice';
|
||||
import { Emitter, Range } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import { URI } from 'vscode-uri';
|
||||
|
||||
import { findMatchingFiles } from './utils';
|
||||
|
||||
import type { Definition, ImageReference, Link, LinkReference } from 'mdast';
|
||||
import type { fromMarkdown as FromMarkdownFunction } from 'mdast-util-from-markdown';
|
||||
import type { Node, Position } from 'unist';
|
||||
import type { visit as VisitFunction } from 'unist-util-visit';
|
||||
|
||||
// Helper function to work around import issues with ESM modules and ts-node
|
||||
// eslint-disable-next-line no-new-func
|
||||
const dynamicImport = new Function('specifier', 'return import(specifier)');
|
||||
|
||||
// Helper function from `vscode-markdown-languageservice` codebase
|
||||
function tryDecodeUri (str: string): string {
|
||||
try {
|
||||
return decodeURI(str);
|
||||
} catch {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function from `vscode-markdown-languageservice` codebase
|
||||
function createHref (
|
||||
sourceDocUri: URI,
|
||||
link: string,
|
||||
workspace: IWorkspace
|
||||
): ExternalHref | InternalHref | undefined {
|
||||
if (/^[a-z-][a-z-]+:/i.test(link)) {
|
||||
// Looks like a uri
|
||||
return { kind: HrefKind.External, uri: URI.parse(tryDecodeUri(link)) };
|
||||
}
|
||||
|
||||
const resolved = resolveInternalDocumentLink(sourceDocUri, link, workspace);
|
||||
if (!resolved) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
kind: HrefKind.Internal,
|
||||
path: resolved.resource,
|
||||
fragment: resolved.linkFragment
|
||||
};
|
||||
}
|
||||
|
||||
function positionToRange (position: Position): Range {
|
||||
return {
|
||||
start: {
|
||||
character: position.start.column - 1,
|
||||
line: position.start.line - 1
|
||||
},
|
||||
end: { character: position.end.column - 1, line: position.end.line - 1 }
|
||||
};
|
||||
}
|
||||
|
||||
const mdIt = MarkdownIt({ html: true });
|
||||
|
||||
export class MarkdownParser implements IMdParser {
|
||||
slugifier = githubSlugifier;
|
||||
|
||||
async tokenize (document: TextDocument) {
|
||||
return mdIt.parse(document.getText(), {});
|
||||
}
|
||||
}
|
||||
|
||||
export class DocsWorkspace implements IWorkspace {
|
||||
private readonly documentCache: Map<string, TextDocument>;
|
||||
readonly root: string;
|
||||
|
||||
constructor (root: string) {
|
||||
this.documentCache = new Map();
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
get workspaceFolders () {
|
||||
return [URI.file(this.root)];
|
||||
}
|
||||
|
||||
async getAllMarkdownDocuments (): Promise<Iterable<ITextDocument>> {
|
||||
const files = await findMatchingFiles(this.root, (file) =>
|
||||
file.endsWith('.md')
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const document = TextDocument.create(
|
||||
URI.file(file).toString(),
|
||||
'markdown',
|
||||
1,
|
||||
fs.readFileSync(file, 'utf8')
|
||||
);
|
||||
|
||||
this.documentCache.set(file, document);
|
||||
}
|
||||
|
||||
return this.documentCache.values();
|
||||
}
|
||||
|
||||
hasMarkdownDocument (resource: URI) {
|
||||
const relativePath = path.relative(this.root, resource.path);
|
||||
return (
|
||||
!relativePath.startsWith('..') &&
|
||||
!path.isAbsolute(relativePath) &&
|
||||
fs.existsSync(resource.path)
|
||||
);
|
||||
}
|
||||
|
||||
async openMarkdownDocument (resource: URI) {
|
||||
if (!this.documentCache.has(resource.path)) {
|
||||
const document = TextDocument.create(
|
||||
resource.toString(),
|
||||
'markdown',
|
||||
1,
|
||||
fs.readFileSync(resource.path, 'utf8')
|
||||
);
|
||||
|
||||
this.documentCache.set(resource.path, document);
|
||||
}
|
||||
|
||||
return this.documentCache.get(resource.path);
|
||||
}
|
||||
|
||||
async stat (resource: URI): Promise<FileStat | undefined> {
|
||||
if (this.hasMarkdownDocument(resource)) {
|
||||
const stats = fs.statSync(resource.path);
|
||||
return { isDirectory: stats.isDirectory() };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async readDirectory (): Promise<Iterable<readonly [string, FileStat]>> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
//
|
||||
// These events are defined to fulfill the interface, but are never emitted
|
||||
// by this implementation since it's not meant for watching a workspace
|
||||
//
|
||||
|
||||
#onDidChangeMarkdownDocument = new Emitter<ITextDocument>();
|
||||
onDidChangeMarkdownDocument = this.#onDidChangeMarkdownDocument.event;
|
||||
|
||||
#onDidCreateMarkdownDocument = new Emitter<ITextDocument>();
|
||||
onDidCreateMarkdownDocument = this.#onDidCreateMarkdownDocument.event;
|
||||
|
||||
#onDidDeleteMarkdownDocument = new Emitter<URI>();
|
||||
onDidDeleteMarkdownDocument = this.#onDidDeleteMarkdownDocument.event;
|
||||
}
|
||||
|
||||
export class MarkdownLinkComputer implements IMdLinkComputer {
|
||||
private readonly workspace: IWorkspace;
|
||||
|
||||
constructor (workspace: IWorkspace) {
|
||||
this.workspace = workspace;
|
||||
}
|
||||
|
||||
async getAllLinks (document: ITextDocument): Promise<MdLink[]> {
|
||||
const { fromMarkdown } = (await dynamicImport(
|
||||
'mdast-util-from-markdown'
|
||||
)) as { fromMarkdown: typeof FromMarkdownFunction };
|
||||
|
||||
const tree = fromMarkdown(document.getText());
|
||||
|
||||
const links = [
|
||||
...(await this.#getInlineLinks(document, tree)),
|
||||
...(await this.#getReferenceLinks(document, tree)),
|
||||
...(await this.#getLinkDefinitions(document, tree))
|
||||
];
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
async #getInlineLinks (
|
||||
document: ITextDocument,
|
||||
tree: Node
|
||||
): Promise<MdLink[]> {
|
||||
const { visit } = (await dynamicImport('unist-util-visit')) as {
|
||||
visit: typeof VisitFunction;
|
||||
};
|
||||
|
||||
const documentUri = URI.parse(document.uri);
|
||||
const links: MdLink[] = [];
|
||||
|
||||
visit(
|
||||
tree,
|
||||
(node) => node.type === 'link',
|
||||
(node: Node) => {
|
||||
const link = node as Link;
|
||||
const href = createHref(documentUri, link.url, this.workspace);
|
||||
|
||||
if (href) {
|
||||
const range = positionToRange(link.position!);
|
||||
|
||||
// NOTE - These haven't been implemented properly, but their
|
||||
// values aren't used for the link linting use-case
|
||||
const targetRange = range;
|
||||
const hrefRange = range;
|
||||
const fragmentRange = undefined;
|
||||
|
||||
links.push({
|
||||
kind: MdLinkKind.Link,
|
||||
href,
|
||||
source: {
|
||||
hrefText: link.url,
|
||||
resource: documentUri,
|
||||
range,
|
||||
targetRange,
|
||||
hrefRange,
|
||||
fragmentRange,
|
||||
pathText: link.url.split('#')[0]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
async #getReferenceLinks (
|
||||
document: ITextDocument,
|
||||
tree: Node
|
||||
): Promise<MdLink[]> {
|
||||
const { visit } = (await dynamicImport('unist-util-visit')) as {
|
||||
visit: typeof VisitFunction;
|
||||
};
|
||||
|
||||
const links: MdLink[] = [];
|
||||
|
||||
visit(
|
||||
tree,
|
||||
(node) => ['imageReference', 'linkReference'].includes(node.type),
|
||||
(node: Node) => {
|
||||
const link = node as ImageReference | LinkReference;
|
||||
const range = positionToRange(link.position!);
|
||||
|
||||
// NOTE - These haven't been implemented properly, but their
|
||||
// values aren't used for the link linting use-case
|
||||
const targetRange = range;
|
||||
const hrefRange = range;
|
||||
|
||||
links.push({
|
||||
kind: MdLinkKind.Link,
|
||||
href: {
|
||||
kind: HrefKind.Reference,
|
||||
ref: link.label!
|
||||
},
|
||||
source: {
|
||||
hrefText: link.label!,
|
||||
resource: URI.parse(document.uri),
|
||||
range,
|
||||
targetRange,
|
||||
hrefRange,
|
||||
fragmentRange: undefined,
|
||||
pathText: link.label!
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
async #getLinkDefinitions (
|
||||
document: ITextDocument,
|
||||
tree: Node
|
||||
): Promise<MdLink[]> {
|
||||
const { visit } = (await dynamicImport('unist-util-visit')) as {
|
||||
visit: typeof VisitFunction;
|
||||
};
|
||||
|
||||
const documentUri = URI.parse(document.uri);
|
||||
const links: MdLink[] = [];
|
||||
|
||||
visit(
|
||||
tree,
|
||||
(node) => node.type === 'definition',
|
||||
(node: Node) => {
|
||||
const definition = node as Definition;
|
||||
const href = createHref(documentUri, definition.url, this.workspace);
|
||||
|
||||
if (href) {
|
||||
const range = positionToRange(definition.position!);
|
||||
|
||||
// NOTE - These haven't been implemented properly, but their
|
||||
// values aren't used for the link linting use-case
|
||||
const targetRange = range;
|
||||
const hrefRange = range;
|
||||
const fragmentRange = undefined;
|
||||
|
||||
links.push({
|
||||
kind: MdLinkKind.Definition,
|
||||
href,
|
||||
ref: {
|
||||
range,
|
||||
text: definition.label!
|
||||
},
|
||||
source: {
|
||||
hrefText: definition.url,
|
||||
resource: documentUri,
|
||||
range,
|
||||
targetRange,
|
||||
hrefRange,
|
||||
fragmentRange,
|
||||
pathText: definition.url.split('#')[0]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return links;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
const { GitProcess } = require('dugite');
|
||||
const fs = require('fs');
|
||||
const klaw = require('klaw');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
@@ -122,8 +123,29 @@ function chunkFilenames (filenames, offset = 0) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} top
|
||||
* @param {(filename: string) => boolean} test
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
async function findMatchingFiles (top, test) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const matches = [];
|
||||
klaw(top, {
|
||||
filter: f => path.basename(f) !== '.bin'
|
||||
})
|
||||
.on('end', () => resolve(matches))
|
||||
.on('data', item => {
|
||||
if (test(item.path)) {
|
||||
matches.push(item.path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
chunkFilenames,
|
||||
findMatchingFiles,
|
||||
getCurrentBranch,
|
||||
getElectronExec,
|
||||
getOutDir,
|
||||
|
||||
177
script/lint-docs-links.ts
Executable file
177
script/lint-docs-links.ts
Executable file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env ts-node
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import {
|
||||
createLanguageService,
|
||||
DiagnosticLevel,
|
||||
DiagnosticOptions,
|
||||
ILogger
|
||||
} from '@dsanders11/vscode-markdown-languageservice';
|
||||
import * as minimist from 'minimist';
|
||||
import fetch from 'node-fetch';
|
||||
import { CancellationTokenSource } from 'vscode-languageserver';
|
||||
import { URI } from 'vscode-uri';
|
||||
|
||||
import {
|
||||
DocsWorkspace,
|
||||
MarkdownLinkComputer,
|
||||
MarkdownParser
|
||||
} from './lib/markdown';
|
||||
|
||||
class NoOpLogger implements ILogger {
|
||||
log (): void {}
|
||||
}
|
||||
|
||||
const diagnosticOptions: DiagnosticOptions = {
|
||||
ignoreLinks: [],
|
||||
validateDuplicateLinkDefinitions: DiagnosticLevel.error,
|
||||
validateFileLinks: DiagnosticLevel.error,
|
||||
validateFragmentLinks: DiagnosticLevel.error,
|
||||
validateMarkdownFileLinkFragments: DiagnosticLevel.error,
|
||||
validateReferences: DiagnosticLevel.error,
|
||||
validateUnusedLinkDefinitions: DiagnosticLevel.error
|
||||
};
|
||||
|
||||
async function fetchExternalLink (link: string, checkRedirects = false) {
|
||||
try {
|
||||
const response = await fetch(link);
|
||||
if (response.status !== 200) {
|
||||
console.log('Broken link', link, response.status, response.statusText);
|
||||
} else {
|
||||
if (checkRedirects && response.redirected) {
|
||||
const wwwUrl = new URL(link);
|
||||
wwwUrl.hostname = `www.${wwwUrl.hostname}`;
|
||||
|
||||
// For now cut down on noise to find meaningful redirects
|
||||
const wwwRedirect = wwwUrl.toString() === response.url;
|
||||
const trailingSlashRedirect = `${link}/` === response.url;
|
||||
|
||||
if (!wwwRedirect && !trailingSlashRedirect) {
|
||||
console.log('Link redirection', link, '->', response.url);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
console.log('Broken link', link);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function main ({ fetchExternalLinks = false, checkRedirects = false }) {
|
||||
const workspace = new DocsWorkspace(path.resolve(__dirname, '..', 'docs'));
|
||||
const parser = new MarkdownParser();
|
||||
const linkComputer = new MarkdownLinkComputer(workspace);
|
||||
const languageService = createLanguageService({
|
||||
workspace,
|
||||
parser,
|
||||
logger: new NoOpLogger(),
|
||||
linkComputer
|
||||
});
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
let errors = false;
|
||||
|
||||
const externalLinks = new Set<string>();
|
||||
|
||||
try {
|
||||
// Collect diagnostics for all documents in the workspace
|
||||
for (const document of await workspace.getAllMarkdownDocuments()) {
|
||||
for (let link of await languageService.getDocumentLinks(
|
||||
document,
|
||||
cts.token
|
||||
)) {
|
||||
if (link.target === undefined) {
|
||||
link =
|
||||
(await languageService.resolveDocumentLink(link, cts.token)) ??
|
||||
link;
|
||||
}
|
||||
|
||||
if (
|
||||
link.target &&
|
||||
link.target.startsWith('http') &&
|
||||
new URL(link.target).hostname !== 'localhost'
|
||||
) {
|
||||
externalLinks.add(link.target);
|
||||
}
|
||||
}
|
||||
const diagnostics = await languageService.computeDiagnostics(
|
||||
document,
|
||||
diagnosticOptions,
|
||||
cts.token
|
||||
);
|
||||
|
||||
if (diagnostics.length) {
|
||||
console.log(
|
||||
'File Location:',
|
||||
path.relative(workspace.root, URI.parse(document.uri).path)
|
||||
);
|
||||
}
|
||||
|
||||
for (const diagnostic of diagnostics) {
|
||||
console.log(
|
||||
`\tBroken link on line ${diagnostic.range.start.line + 1}:`,
|
||||
diagnostic.message
|
||||
);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cts.dispose();
|
||||
}
|
||||
|
||||
if (fetchExternalLinks) {
|
||||
const externalLinkStates = await Promise.all(
|
||||
Array.from(externalLinks).map((link) =>
|
||||
fetchExternalLink(link, checkRedirects)
|
||||
)
|
||||
);
|
||||
|
||||
errors = errors || !externalLinkStates.every((x) => x);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
function parseCommandLine () {
|
||||
const showUsage = (arg?: string): boolean => {
|
||||
if (!arg || arg.startsWith('-')) {
|
||||
console.log(
|
||||
'Usage: script/lint-docs-links.ts [-h|--help] [--fetch-external-links] ' +
|
||||
'[--check-redirects]'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const opts = minimist(process.argv.slice(2), {
|
||||
boolean: ['help', 'fetch-external-links', 'check-redirects'],
|
||||
stopEarly: true,
|
||||
unknown: showUsage
|
||||
});
|
||||
|
||||
if (opts.help) showUsage();
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
if (process.mainModule === module) {
|
||||
const opts = parseCommandLine();
|
||||
|
||||
main({
|
||||
fetchExternalLinks: opts['fetch-external-links'],
|
||||
checkRedirects: opts['check-redirects']
|
||||
})
|
||||
.then((errors) => {
|
||||
if (errors) process.exit(1);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -5,11 +5,10 @@ const { GitProcess } = require('dugite');
|
||||
const childProcess = require('child_process');
|
||||
const { ESLint } = require('eslint');
|
||||
const fs = require('fs');
|
||||
const klaw = require('klaw');
|
||||
const minimist = require('minimist');
|
||||
const path = require('path');
|
||||
|
||||
const { chunkFilenames } = require('./lib/utils');
|
||||
const { chunkFilenames, findMatchingFiles } = require('./lib/utils');
|
||||
|
||||
const ELECTRON_ROOT = path.normalize(path.dirname(__dirname));
|
||||
const SOURCE_ROOT = path.resolve(ELECTRON_ROOT, '..');
|
||||
@@ -87,11 +86,8 @@ const LINTERS = [{
|
||||
roots: ['shell'],
|
||||
test: filename => filename.endsWith('.mm') || (filename.endsWith('.h') && isObjCHeader(filename)),
|
||||
run: (opts, filenames) => {
|
||||
if (opts.fix) {
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', '--fix', ...filenames]);
|
||||
} else {
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', ...filenames]);
|
||||
}
|
||||
const clangFormatFlags = opts.fix ? ['--fix'] : [];
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', ...clangFormatFlags, ...filenames]);
|
||||
const filter = [
|
||||
'-readability/braces',
|
||||
'-readability/casting',
|
||||
@@ -279,21 +275,6 @@ async function findChangedFiles (top) {
|
||||
return new Set(absolutePaths);
|
||||
}
|
||||
|
||||
async function findMatchingFiles (top, test) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const matches = [];
|
||||
klaw(top, {
|
||||
filter: f => path.basename(f) !== '.bin'
|
||||
})
|
||||
.on('end', () => resolve(matches))
|
||||
.on('data', item => {
|
||||
if (test(item.path)) {
|
||||
matches.push(item.path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function findFiles (args, linter) {
|
||||
let filenames = [];
|
||||
let includelist = null;
|
||||
|
||||
@@ -137,7 +137,6 @@
|
||||
"parallel/test-worker-debug",
|
||||
"parallel/test-worker-init-failure",
|
||||
"parallel/test-worker-stdio",
|
||||
"parallel/test-v8-serialize-leak",
|
||||
"parallel/test-zlib-unused-weak",
|
||||
"report/test-report-fatalerror-oomerror-set",
|
||||
"report/test-report-fatalerror-oomerror-directory",
|
||||
|
||||
@@ -57,8 +57,9 @@ async function main () {
|
||||
if (args.validateDisabled) {
|
||||
const missing = [];
|
||||
for (const test of DISABLED_TESTS) {
|
||||
const testName = test.endsWith('.js') ? test : `${test}.js`;
|
||||
if (!fs.existsSync(path.join(NODE_DIR, 'test', testName))) {
|
||||
const js = path.join(NODE_DIR, 'test', `${test}.js`);
|
||||
const mjs = path.join(NODE_DIR, 'test', `${test}.mjs`);
|
||||
if (!fs.existsSync(js) && !fs.existsSync(mjs)) {
|
||||
missing.push(test);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ def upload_node(version):
|
||||
elif get_target_arch() == 'arm64':
|
||||
node_lib = os.path.join(DIST_DIR, 'arm64', 'node.lib')
|
||||
iojs_lib = os.path.join(DIST_DIR, 'win-arm64', 'iojs.lib')
|
||||
v4_node_lib = os.path.join(DIST_DIR, 'win-arm64', 'node.lib')
|
||||
v4_node_lib = os.path.join(DIST_DIR, 'win-arm64', 'node.lib')
|
||||
else:
|
||||
node_lib = os.path.join(DIST_DIR, 'x64', 'node.lib')
|
||||
iojs_lib = os.path.join(DIST_DIR, 'win-x64', 'iojs.lib')
|
||||
|
||||
@@ -93,7 +93,7 @@ def main():
|
||||
|
||||
dsym_snapshot_zip = os.path.join(OUT_DIR, DSYM_SNAPSHOT_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'dsym-snapshot.zip'), dsym_snapshot_zip)
|
||||
upload_electron(release, dsym_snapshot_zip, args)
|
||||
upload_electron(release, dsym_snapshot_zip, args)
|
||||
elif PLATFORM == 'win32':
|
||||
pdb_zip = os.path.join(OUT_DIR, PDB_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'pdb.zip'), pdb_zip)
|
||||
|
||||
@@ -20,7 +20,7 @@ function parseCommandLine () {
|
||||
--bump=patch to increment patch version\n
|
||||
--version={version} to set version number directly\n
|
||||
--dryRun to print the next version without updating files
|
||||
Note that you can use both --bump and --stable simultaneously.
|
||||
Note that you can use both --bump and --stable simultaneously.
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as childProcess from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as klaw from 'klaw';
|
||||
import * as minimist from 'minimist';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
@@ -9,7 +8,7 @@ import * as streamJson from 'stream-json';
|
||||
import { ignore as streamJsonIgnore } from 'stream-json/filters/Ignore';
|
||||
import { streamArray as streamJsonStreamArray } from 'stream-json/streamers/StreamArray';
|
||||
|
||||
import { chunkFilenames } from './lib/utils';
|
||||
import { chunkFilenames, findMatchingFiles } from './lib/utils';
|
||||
|
||||
const SOURCE_ROOT = path.normalize(path.dirname(__dirname));
|
||||
const LLVM_BIN = path.resolve(
|
||||
@@ -204,24 +203,6 @@ async function runClangTidy (
|
||||
}
|
||||
}
|
||||
|
||||
async function findMatchingFiles (
|
||||
top: string,
|
||||
test: (filename: string) => boolean
|
||||
): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
const matches = [] as string[];
|
||||
klaw(top, {
|
||||
filter: (f) => path.basename(f) !== '.bin'
|
||||
})
|
||||
.on('end', () => resolve(matches))
|
||||
.on('data', (item) => {
|
||||
if (test(item.path)) {
|
||||
matches.push(item.path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function parseCommandLine () {
|
||||
const showUsage = (arg?: string) : boolean => {
|
||||
if (!arg || arg.startsWith('-')) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
param([string]$gomaDir=$PWD)
|
||||
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
|
||||
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
|
||||
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
|
||||
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
|
||||
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
|
||||
if ($timedOut) {
|
||||
write-error 'Timed out waiting for goma to start'; exit 1;
|
||||
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
|
||||
if ($timedOut) {
|
||||
write-error 'Timed out waiting for goma to start'; exit 1;
|
||||
} else {
|
||||
Write-Output "Successfully started goma!"
|
||||
}
|
||||
|
||||
@@ -104,26 +104,6 @@ bool IsWidevineAvailable(
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_WIDEVINE)
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
void ComputeBuiltInPlugins(std::vector<content::ContentPluginInfo>* plugins) {
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
// TODO(upstream/thestig): Figure out how to make the PDF Viewer work without
|
||||
// this PPAPI plugin registration.
|
||||
content::ContentPluginInfo pdf_info;
|
||||
pdf_info.is_internal = true;
|
||||
pdf_info.is_out_of_process = true;
|
||||
pdf_info.name = kPDFInternalPluginName;
|
||||
pdf_info.description = "Portable Document Format";
|
||||
// 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(pdf::kInternalPluginMimeType, "pdf",
|
||||
"Portable Document Format");
|
||||
pdf_info.mime_types.push_back(pdf_mime_type);
|
||||
plugins->push_back(pdf_info);
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PLUGINS)
|
||||
|
||||
void AppendDelimitedSwitchToVector(const base::StringPiece cmd_switch,
|
||||
std::vector<std::string>* append_me) {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
@@ -204,9 +184,22 @@ void ElectronContentClient::AddAdditionalSchemes(Schemes* schemes) {
|
||||
|
||||
void ElectronContentClient::AddPlugins(
|
||||
std::vector<content::ContentPluginInfo>* plugins) {
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
ComputeBuiltInPlugins(plugins);
|
||||
#endif // BUILDFLAG(ENABLE_PLUGINS)
|
||||
#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
static constexpr char kPDFPluginExtension[] = "pdf";
|
||||
static constexpr char kPDFPluginDescription[] = "Portable Document Format";
|
||||
|
||||
content::ContentPluginInfo pdf_info;
|
||||
pdf_info.is_internal = true;
|
||||
pdf_info.is_out_of_process = true;
|
||||
pdf_info.name = kPDFInternalPluginName;
|
||||
pdf_info.description = kPDFPluginDescription;
|
||||
// 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(
|
||||
pdf::kInternalPluginMimeType, kPDFPluginExtension, kPDFPluginDescription);
|
||||
pdf_info.mime_types.push_back(pdf_mime_type);
|
||||
plugins->push_back(pdf_info);
|
||||
#endif // BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
}
|
||||
|
||||
void ElectronContentClient::AddContentDecryptionModules(
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "content/common/mac_helpers.h"
|
||||
#include "content/public/common/content_paths.h"
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "shell/browser/mac/electron_application.h"
|
||||
#include "shell/common/application_info.h"
|
||||
#include "shell/common/mac/main_application_bundle.h"
|
||||
@@ -41,11 +40,9 @@ base::FilePath GetHelperAppPath(const base::FilePath& frameworks_path,
|
||||
} else if (base::EndsWith(path.value(), content::kMacHelperSuffix_gpu,
|
||||
base::CompareCase::SENSITIVE)) {
|
||||
helper_name += content::kMacHelperSuffix_gpu;
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
} else if (base::EndsWith(path.value(), content::kMacHelperSuffix_plugin,
|
||||
base::CompareCase::SENSITIVE)) {
|
||||
helper_name += content::kMacHelperSuffix_plugin;
|
||||
#endif
|
||||
}
|
||||
|
||||
return frameworks_path.Append(name + " " + helper_name + ".app")
|
||||
|
||||
@@ -425,9 +425,10 @@ std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
|
||||
|
||||
bool SystemPreferences::CanPromptTouchID() {
|
||||
base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
|
||||
if (![context
|
||||
canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
|
||||
error:nil])
|
||||
LAPolicy auth_policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
|
||||
if (@available(macOS 10.15, *))
|
||||
auth_policy = LAPolicyDeviceOwnerAuthenticationWithBiometricsOrWatch;
|
||||
if (![context canEvaluatePolicy:auth_policy error:nil])
|
||||
return false;
|
||||
if (@available(macOS 10.13.2, *))
|
||||
return [context biometryType] == LABiometryTypeTouchID;
|
||||
|
||||
@@ -3825,6 +3825,10 @@ void WebContents::DevToolsStopIndexing(int request_id) {
|
||||
devtools_indexing_jobs_.erase(it);
|
||||
}
|
||||
|
||||
void WebContents::DevToolsOpenInNewTab(const std::string& url) {
|
||||
Emit("devtools-open-url", url);
|
||||
}
|
||||
|
||||
void WebContents::DevToolsSearchInPath(int request_id,
|
||||
const std::string& file_system_path,
|
||||
const std::string& query) {
|
||||
|
||||
@@ -704,6 +704,7 @@ class WebContents : public ExclusiveAccessContext,
|
||||
void DevToolsIndexPath(int request_id,
|
||||
const std::string& file_system_path,
|
||||
const std::string& excluded_folders_message) override;
|
||||
void DevToolsOpenInNewTab(const std::string& url) override;
|
||||
void DevToolsStopIndexing(int request_id) override;
|
||||
void DevToolsSearchInPath(int request_id,
|
||||
const std::string& file_system_path,
|
||||
|
||||
@@ -120,10 +120,6 @@
|
||||
#include "ui/native_theme/native_theme.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "sandbox/win/src/sandbox_policy.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(USE_NSS_CERTS)
|
||||
#include "net/ssl/client_cert_store_nss.h"
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
@@ -474,15 +470,10 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
content::ChildProcessHost::CHILD_RENDERER);
|
||||
auto gpu_child_path = content::ChildProcessHost::GetChildPath(
|
||||
content::ChildProcessHost::CHILD_GPU);
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
auto plugin_child_path = content::ChildProcessHost::GetChildPath(
|
||||
content::ChildProcessHost::CHILD_PLUGIN);
|
||||
#endif
|
||||
if (program != renderer_child_path && program != gpu_child_path
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
&& program != plugin_child_path
|
||||
#endif
|
||||
) {
|
||||
if (program != renderer_child_path && program != gpu_child_path &&
|
||||
program != plugin_child_path) {
|
||||
child_path = content::ChildProcessHost::GetChildPath(
|
||||
content::ChildProcessHost::CHILD_NORMAL);
|
||||
CHECK_EQ(program, child_path)
|
||||
@@ -1440,19 +1431,6 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
|
||||
browser_context, origin, is_for_isolated_world, factory_params);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool ElectronBrowserClient::PreSpawnChild(sandbox::TargetPolicy* policy,
|
||||
sandbox::mojom::Sandbox sandbox_type,
|
||||
ChildSpawnFlags flags) {
|
||||
sandbox::ResultCode result = policy->GetConfig()->AddRule(
|
||||
sandbox::SubSystem::kFiles, sandbox::Semantics::kFilesAllowAny,
|
||||
L"\\??\\pipe\\crashpad_*");
|
||||
if (result != sandbox::SBOX_ALL_OK)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
void ElectronBrowserClient::
|
||||
RegisterAssociatedInterfaceBindersForRenderFrameHost(
|
||||
content::RenderFrameHost&
|
||||
|
||||
@@ -242,11 +242,6 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
const url::Origin& origin,
|
||||
bool is_for_isolated_world,
|
||||
network::mojom::URLLoaderFactoryParams* factory_params) override;
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool PreSpawnChild(sandbox::TargetPolicy* policy,
|
||||
sandbox::mojom::Sandbox sandbox_type,
|
||||
ChildSpawnFlags flags) override;
|
||||
#endif
|
||||
void RegisterAssociatedInterfaceBindersForRenderFrameHost(
|
||||
content::RenderFrameHost& render_frame_host,
|
||||
blink::AssociatedInterfaceRegistry& associated_registry) override;
|
||||
|
||||
@@ -180,7 +180,7 @@ void FileSelectHelper::OnListDone(int error) {
|
||||
std::unique_ptr<ActiveDirectoryEnumeration> entry =
|
||||
std::move(directory_enumeration_);
|
||||
if (error) {
|
||||
FileSelectionCanceled(NULL);
|
||||
FileSelectionCanceled(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -487,6 +487,11 @@ void FileSelectHelper::RunFileChooserEnd() {
|
||||
listener_->FileSelectionCanceled();
|
||||
render_frame_host_ = nullptr;
|
||||
web_contents_ = nullptr;
|
||||
// If the dialog was actually opened, dispose of our reference.
|
||||
if (select_file_dialog_) {
|
||||
select_file_dialog_->ListenerDestroyed();
|
||||
select_file_dialog_.reset();
|
||||
}
|
||||
Release();
|
||||
}
|
||||
|
||||
@@ -525,8 +530,6 @@ void FileSelectHelper::RenderWidgetHostDestroyed(
|
||||
void FileSelectHelper::RenderFrameHostChanged(
|
||||
content::RenderFrameHost* old_host,
|
||||
content::RenderFrameHost* new_host) {
|
||||
if (!render_frame_host_)
|
||||
return;
|
||||
// The |old_host| and its children are now pending deletion. Do not give them
|
||||
// file access past this point.
|
||||
for (content::RenderFrameHost* host = render_frame_host_; host;
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "ui/base/win/shell.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
@@ -693,6 +697,17 @@ void NativeWindow::NotifyWindowMessage(UINT message,
|
||||
#endif
|
||||
|
||||
int NativeWindow::NonClientHitTest(const gfx::Point& point) {
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
// We need to ensure we account for resizing borders on Windows and Linux.
|
||||
if ((!has_frame() || has_client_frame()) && IsResizable()) {
|
||||
auto* frame =
|
||||
static_cast<FramelessView*>(widget()->non_client_view()->frame_view());
|
||||
int border_hit = frame->ResizingBorderHitTest(point);
|
||||
if (border_hit != HTNOWHERE)
|
||||
return border_hit;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto* provider : draggable_region_providers_) {
|
||||
int hit = provider->NonClientHitTest(point);
|
||||
if (hit != HTNOWHERE)
|
||||
|
||||
@@ -1592,19 +1592,7 @@ views::View* NativeWindowViews::GetContentsView() {
|
||||
bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
|
||||
gfx::NativeView child,
|
||||
const gfx::Point& location) {
|
||||
// App window should claim mouse events that fall within any BrowserViews'
|
||||
// draggable region.
|
||||
if (NonClientHitTest(location) != HTNOWHERE)
|
||||
return false;
|
||||
|
||||
// And the events on border for dragging resizable frameless window.
|
||||
if ((!has_frame() || has_client_frame()) && resizable_) {
|
||||
auto* frame =
|
||||
static_cast<FramelessView*>(widget()->non_client_view()->frame_view());
|
||||
return frame->ResizingBorderHitTest(location) == HTNOWHERE;
|
||||
}
|
||||
|
||||
return true;
|
||||
return NonClientHitTest(location) == HTNOWHERE;
|
||||
}
|
||||
|
||||
views::ClientView* NativeWindowViews::CreateClientView(views::Widget* widget) {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>AtomApplication</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
|
||||
@@ -33,12 +33,14 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
void CalculatePopupXAndWidthHorizontallyCentered(
|
||||
int popup_preferred_width,
|
||||
const gfx::Rect& content_area_bounds,
|
||||
const gfx::Rect& element_bounds,
|
||||
bool is_rtl,
|
||||
gfx::Rect* bubble_bounds) {
|
||||
gfx::Rect* popup_bounds) {
|
||||
// The preferred horizontal starting point for the pop-up is at the horizontal
|
||||
// center of the field.
|
||||
int preferred_starting_point =
|
||||
@@ -66,15 +68,15 @@ void CalculatePopupXAndWidthHorizontallyCentered(
|
||||
int amount_to_grow_in_unpreferred_direction =
|
||||
std::max(0, popup_width - space_to_grow_in_preferred_direction);
|
||||
|
||||
bubble_bounds->set_width(popup_width);
|
||||
popup_bounds->set_width(popup_width);
|
||||
if (is_rtl) {
|
||||
// Note, in RTL the |pop_up_width| must be subtracted to achieve
|
||||
// right-alignment of the pop-up with the element.
|
||||
bubble_bounds->set_x(preferred_starting_point - popup_width +
|
||||
amount_to_grow_in_unpreferred_direction);
|
||||
popup_bounds->set_x(preferred_starting_point - popup_width +
|
||||
amount_to_grow_in_unpreferred_direction);
|
||||
} else {
|
||||
bubble_bounds->set_x(preferred_starting_point -
|
||||
amount_to_grow_in_unpreferred_direction);
|
||||
popup_bounds->set_x(preferred_starting_point -
|
||||
amount_to_grow_in_unpreferred_direction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ void CalculatePopupXAndWidth(int popup_preferred_width,
|
||||
const gfx::Rect& content_area_bounds,
|
||||
const gfx::Rect& element_bounds,
|
||||
bool is_rtl,
|
||||
gfx::Rect* bubble_bounds) {
|
||||
gfx::Rect* popup_bounds) {
|
||||
int right_growth_start = base::clamp(
|
||||
element_bounds.x(), content_area_bounds.x(), content_area_bounds.right());
|
||||
int left_growth_end =
|
||||
@@ -107,15 +109,15 @@ void CalculatePopupXAndWidth(int popup_preferred_width,
|
||||
right_available < popup_width && right_available < left_available;
|
||||
}
|
||||
|
||||
bubble_bounds->set_width(popup_width);
|
||||
bubble_bounds->set_x(grow_left ? left_growth_end - popup_width
|
||||
: right_growth_start);
|
||||
popup_bounds->set_width(popup_width);
|
||||
popup_bounds->set_x(grow_left ? left_growth_end - popup_width
|
||||
: right_growth_start);
|
||||
}
|
||||
|
||||
void CalculatePopupYAndHeight(int popup_preferred_height,
|
||||
const gfx::Rect& content_area_bounds,
|
||||
const gfx::Rect& element_bounds,
|
||||
gfx::Rect* bubble_bounds) {
|
||||
gfx::Rect* popup_bounds) {
|
||||
int top_growth_end = base::clamp(element_bounds.y(), content_area_bounds.y(),
|
||||
content_area_bounds.bottom());
|
||||
int bottom_growth_start =
|
||||
@@ -125,18 +127,18 @@ void CalculatePopupYAndHeight(int popup_preferred_height,
|
||||
int top_available = top_growth_end - content_area_bounds.y();
|
||||
int bottom_available = content_area_bounds.bottom() - bottom_growth_start;
|
||||
|
||||
bubble_bounds->set_height(popup_preferred_height);
|
||||
bubble_bounds->set_y(top_growth_end);
|
||||
popup_bounds->set_height(popup_preferred_height);
|
||||
popup_bounds->set_y(top_growth_end);
|
||||
|
||||
if (bottom_available >= popup_preferred_height ||
|
||||
bottom_available >= top_available) {
|
||||
bubble_bounds->AdjustToFit(
|
||||
gfx::Rect(bubble_bounds->x(), element_bounds.bottom(),
|
||||
bubble_bounds->width(), bottom_available));
|
||||
popup_bounds->AdjustToFit(
|
||||
gfx::Rect(popup_bounds->x(), element_bounds.bottom(),
|
||||
popup_bounds->width(), bottom_available));
|
||||
} else {
|
||||
bubble_bounds->AdjustToFit(
|
||||
gfx::Rect(bubble_bounds->x(), content_area_bounds.y(),
|
||||
bubble_bounds->width(), top_available));
|
||||
popup_bounds->AdjustToFit(gfx::Rect(popup_bounds->x(),
|
||||
content_area_bounds.y(),
|
||||
popup_bounds->width(), top_available));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,22 +147,24 @@ gfx::Rect CalculatePopupBounds(const gfx::Size& desired_size,
|
||||
const gfx::Rect& element_bounds,
|
||||
bool is_rtl,
|
||||
bool horizontally_centered) {
|
||||
gfx::Rect bubble_bounds;
|
||||
gfx::Rect popup_bounds;
|
||||
|
||||
if (horizontally_centered) {
|
||||
CalculatePopupXAndWidthHorizontallyCentered(
|
||||
desired_size.width(), content_area_bounds, element_bounds, is_rtl,
|
||||
&bubble_bounds);
|
||||
&popup_bounds);
|
||||
} else {
|
||||
CalculatePopupXAndWidth(desired_size.width(), content_area_bounds,
|
||||
element_bounds, is_rtl, &bubble_bounds);
|
||||
element_bounds, is_rtl, &popup_bounds);
|
||||
}
|
||||
CalculatePopupYAndHeight(desired_size.height(), content_area_bounds,
|
||||
element_bounds, &bubble_bounds);
|
||||
element_bounds, &popup_bounds);
|
||||
|
||||
return bubble_bounds;
|
||||
return popup_bounds;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AutofillPopup::AutofillPopup() {
|
||||
bold_font_list_ = gfx::FontList().DeriveWithWeight(gfx::Font::Weight::BOLD);
|
||||
smaller_font_list_ =
|
||||
@@ -242,16 +246,12 @@ void AutofillPopup::UpdatePopupBounds() {
|
||||
views::View::ConvertPointToScreen(parent_, &origin);
|
||||
|
||||
gfx::Rect bounds(origin, element_bounds_.size());
|
||||
gfx::Rect window_bounds = parent_->GetBoundsInScreen();
|
||||
|
||||
gfx::Size preferred_size =
|
||||
gfx::Size(GetDesiredPopupWidth(), GetDesiredPopupHeight());
|
||||
|
||||
popup_bounds_ = CalculatePopupBounds(preferred_size, window_bounds, bounds,
|
||||
base::i18n::IsRTL(), true);
|
||||
CalculatePopupXAndWidthHorizontallyCentered(
|
||||
preferred_size.width(), window_bounds, element_bounds_,
|
||||
base::i18n::IsRTL(), &popup_bounds_);
|
||||
popup_bounds_ =
|
||||
CalculatePopupBounds(preferred_size, parent_->GetBoundsInScreen(), bounds,
|
||||
base::i18n::IsRTL(), false);
|
||||
}
|
||||
|
||||
gfx::Rect AutofillPopup::popup_bounds_in_view() {
|
||||
|
||||
70
shell/browser/ui/gtk/menu_gtk.cc
Normal file
70
shell/browser/ui/gtk/menu_gtk.cc
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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/ui/gtk/menu_gtk.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "shell/browser/ui/gtk/menu_util.h"
|
||||
#include "ui/base/models/menu_model.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace gtkui {
|
||||
|
||||
MenuGtk::MenuGtk(ui::MenuModel* model)
|
||||
: menu_model_(model), gtk_menu_(TakeGObject(gtk_menu_new())) {
|
||||
if (menu_model_) {
|
||||
BuildSubmenuFromModel(menu_model_, gtk_menu_,
|
||||
G_CALLBACK(OnMenuItemActivatedThunk),
|
||||
&block_activation_, this);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
MenuGtk::~MenuGtk() {
|
||||
gtk_widget_destroy(gtk_menu_);
|
||||
}
|
||||
|
||||
void MenuGtk::Refresh() {
|
||||
gtk_container_foreach(GTK_CONTAINER(gtk_menu_.get()), SetMenuItemInfo,
|
||||
&block_activation_);
|
||||
}
|
||||
|
||||
GtkMenu* MenuGtk::GetGtkMenu() {
|
||||
return GTK_MENU(gtk_menu_.get());
|
||||
}
|
||||
|
||||
void MenuGtk::OnMenuItemActivated(GtkWidget* menu_item) {
|
||||
if (block_activation_)
|
||||
return;
|
||||
|
||||
ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item));
|
||||
if (!model) {
|
||||
// There won't be a model for "native" submenus like the "Input Methods"
|
||||
// context menu. We don't need to handle activation messages for submenus
|
||||
// anyway, so we can just return here.
|
||||
DCHECK(gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)));
|
||||
return;
|
||||
}
|
||||
|
||||
// The activate signal is sent to radio items as they get deselected;
|
||||
// ignore it in this case.
|
||||
if (GTK_IS_RADIO_MENU_ITEM(menu_item) &&
|
||||
!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) {
|
||||
return;
|
||||
}
|
||||
|
||||
int id;
|
||||
if (!GetMenuItemID(menu_item, &id))
|
||||
return;
|
||||
|
||||
// The menu item can still be activated by hotkeys even if it is disabled.
|
||||
if (model->IsEnabledAt(id))
|
||||
ExecuteCommand(model, id);
|
||||
}
|
||||
|
||||
} // namespace gtkui
|
||||
|
||||
} // namespace electron
|
||||
48
shell/browser/ui/gtk/menu_gtk.h
Normal file
48
shell/browser/ui/gtk/menu_gtk.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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 ELECTRON_SHELL_BROWSER_UI_GTK_MENU_GTK_H_
|
||||
#define ELECTRON_SHELL_BROWSER_UI_GTK_MENU_GTK_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "ui/base/glib/glib_signal.h"
|
||||
#include "ui/base/glib/scoped_gobject.h"
|
||||
|
||||
typedef struct _GtkMenu GtkMenu;
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
|
||||
namespace ui {
|
||||
class MenuModel;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace gtkui {
|
||||
|
||||
class MenuGtk {
|
||||
public:
|
||||
explicit MenuGtk(ui::MenuModel* model);
|
||||
virtual ~MenuGtk();
|
||||
|
||||
// Refreshes all the menu item labels and menu item checked/enabled states.
|
||||
void Refresh();
|
||||
|
||||
GtkMenu* GetGtkMenu();
|
||||
|
||||
private:
|
||||
// Callback for when a menu item is activated.
|
||||
CHROMEG_CALLBACK_0(MenuGtk, void, OnMenuItemActivated, GtkWidget*);
|
||||
|
||||
raw_ptr<ui::MenuModel> menu_model_; // not owned
|
||||
ScopedGObject<GtkWidget> gtk_menu_;
|
||||
|
||||
bool block_activation_ = false;
|
||||
};
|
||||
|
||||
} // namespace gtkui
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_UI_GTK_MENU_GTK_H_
|
||||
@@ -723,7 +723,10 @@ void InspectableWebContents::SetIsDocked(DispatchCallback callback,
|
||||
std::move(callback).Run(nullptr);
|
||||
}
|
||||
|
||||
void InspectableWebContents::OpenInNewTab(const std::string& url) {}
|
||||
void InspectableWebContents::OpenInNewTab(const std::string& url) {
|
||||
if (delegate_)
|
||||
delegate_->DevToolsOpenInNewTab(url);
|
||||
}
|
||||
|
||||
void InspectableWebContents::ShowItemInFolder(
|
||||
const std::string& file_system_path) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user